001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.server.xdbm.search.cursor; 021 022 023import java.io.IOException; 024 025import org.apache.directory.api.ldap.model.constants.Loggers; 026import org.apache.directory.api.ldap.model.cursor.Cursor; 027import org.apache.directory.api.ldap.model.cursor.CursorException; 028import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException; 029import org.apache.directory.api.ldap.model.exception.LdapException; 030import org.apache.directory.api.ldap.model.schema.AttributeType; 031import org.apache.directory.server.core.api.partition.PartitionTxn; 032import org.apache.directory.server.i18n.I18n; 033import org.apache.directory.server.xdbm.AbstractIndexCursor; 034import org.apache.directory.server.xdbm.Index; 035import org.apache.directory.server.xdbm.IndexEntry; 036import org.apache.directory.server.xdbm.IndexNotFoundException; 037import org.apache.directory.server.xdbm.Store; 038import org.apache.directory.server.xdbm.search.evaluator.LessEqEvaluator; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041 042 043/** 044 * A Cursor over entry candidates matching a LessEq assertion filter. This 045 * Cursor operates in two modes. The first is when an index exists for the 046 * attribute the assertion is built on. The second is when the user index for 047 * the assertion attribute does not exist. Different Cursors are used in each 048 * of these cases where the other remains null. 049 * 050 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 051 */ 052public class LessEqCursor<V> extends AbstractIndexCursor<V> 053{ 054 /** A dedicated log for cursors */ 055 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 056 057 /** Speedup for logs */ 058 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 059 060 private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_716 ); 061 062 /** An less eq evaluator for candidates */ 063 private final LessEqEvaluator<V> lessEqEvaluator; 064 065 /** Cursor over attribute entry matching filter: set when index present */ 066 private final Cursor<IndexEntry<V, String>> userIdxCursor; 067 068 /** NDN Cursor on all entries in (set when no index on user attribute) */ 069 private final Cursor<IndexEntry<String, String>> uuidIdxCursor; 070 071 /** 072 * Used to store indexEntry from uudCandidate so it can be saved after 073 * call to evaluate() which changes the value so it's not referring to 074 * the String but to the value of the attribute instead. 075 */ 076 private IndexEntry<String, String> uuidCandidate; 077 078 079 /** 080 * Creates a new instance of an LessEqCursor 081 * 082 * @param partitionTxn The transaction to use 083 * @param store The store 084 * @param lessEqEvaluator The LessEqEvaluator 085 * @throws LdapException If the creation failed 086 * @throws IndexNotFoundException If the index was not found 087 */ 088 @SuppressWarnings("unchecked") 089 public LessEqCursor( PartitionTxn partitionTxn, Store store, LessEqEvaluator<V> lessEqEvaluator ) 090 throws LdapException, IndexNotFoundException 091 { 092 if ( IS_DEBUG ) 093 { 094 LOG_CURSOR.debug( "Creating LessEqCursor {}", this ); 095 } 096 097 this.lessEqEvaluator = lessEqEvaluator; 098 this.partitionTxn = partitionTxn; 099 100 AttributeType attributeType = lessEqEvaluator.getExpression().getAttributeType(); 101 102 if ( store.hasIndexOn( attributeType ) ) 103 { 104 userIdxCursor = ( ( Index<V, String> ) store.getIndex( attributeType ) ).forwardCursor( partitionTxn ); 105 uuidIdxCursor = null; 106 } 107 else 108 { 109 uuidIdxCursor = new AllEntriesCursor( partitionTxn, store ); 110 userIdxCursor = null; 111 } 112 } 113 114 115 /** 116 * {@inheritDoc} 117 */ 118 protected String getUnsupportedMessage() 119 { 120 return UNSUPPORTED_MSG; 121 } 122 123 124 /** 125 * {@inheritDoc} 126 */ 127 @Override 128 public void before( IndexEntry<V, String> element ) throws LdapException, CursorException 129 { 130 checkNotClosed(); 131 132 if ( userIdxCursor != null ) 133 { 134 /* 135 * First we need to check and make sure this element is within 136 * bounds as mandated by the assertion node. To do so we compare 137 * it's value with the value of the expression node. If the 138 * element's value is greater than this upper bound then we 139 * position the userIdxCursor after the last node. 140 * 141 * If the element's value is equal to this upper bound then we 142 * position the userIdxCursor right before the last node. 143 * 144 * If the element's value is smaller, then we delegate to the 145 * before() method of the userIdxCursor. 146 */ 147 int compareValue = lessEqEvaluator.getComparator().compare( element.getKey(), 148 lessEqEvaluator.getExpression().getValue().getString() ); 149 150 if ( compareValue > 0 ) 151 { 152 afterLast(); 153 return; 154 } 155 else if ( compareValue == 0 ) 156 { 157 last(); 158 previous(); 159 setAvailable( false ); 160 return; 161 } 162 163 userIdxCursor.before( element ); 164 setAvailable( false ); 165 } 166 else 167 { 168 super.before( element ); 169 } 170 } 171 172 173 /** 174 * {@inheritDoc} 175 */ 176 @Override 177 public void after( IndexEntry<V, String> element ) throws LdapException, CursorException 178 { 179 checkNotClosed(); 180 181 if ( userIdxCursor != null ) 182 { 183 int comparedValue = lessEqEvaluator.getComparator().compare( element.getKey(), 184 lessEqEvaluator.getExpression().getValue().getString() ); 185 186 /* 187 * First we need to check and make sure this element is within 188 * bounds as mandated by the assertion node. To do so we compare 189 * it's value with the value of the expression node. 190 * 191 * If the element's value is equal to or greater than this upper 192 * bound then we position the userIdxCursor after the last node. 193 * 194 * If the element's value is smaller, then we delegate to the 195 * after() method of the userIdxCursor. 196 */ 197 if ( comparedValue >= 0 ) 198 { 199 afterLast(); 200 return; 201 } 202 203 // Element is in the valid range as specified by assertion 204 userIdxCursor.after( element ); 205 setAvailable( false ); 206 } 207 else 208 { 209 super.after( element ); 210 } 211 } 212 213 214 /** 215 * {@inheritDoc} 216 */ 217 public void beforeFirst() throws LdapException, CursorException 218 { 219 checkNotClosed(); 220 221 if ( userIdxCursor != null ) 222 { 223 userIdxCursor.beforeFirst(); 224 } 225 else 226 { 227 uuidIdxCursor.beforeFirst(); 228 uuidCandidate = null; 229 } 230 231 setAvailable( false ); 232 } 233 234 235 /** 236 * {@inheritDoc} 237 */ 238 public void afterLast() throws LdapException, CursorException 239 { 240 checkNotClosed(); 241 242 if ( userIdxCursor != null ) 243 { 244 IndexEntry<V, String> advanceTo = new IndexEntry<>(); 245 //noinspection unchecked 246 String normalizedKey = lessEqEvaluator.getAttributeType().getEquality().getNormalizer().normalize( 247 lessEqEvaluator.getExpression().getValue().getString() ); 248 249 advanceTo.setKey( ( V ) normalizedKey ); 250 userIdxCursor.after( advanceTo ); 251 } 252 else 253 { 254 uuidIdxCursor.afterLast(); 255 uuidCandidate = null; 256 } 257 258 setAvailable( false ); 259 } 260 261 262 /** 263 * {@inheritDoc} 264 */ 265 public boolean first() throws LdapException, CursorException 266 { 267 beforeFirst(); 268 return next(); 269 } 270 271 272 /** 273 * {@inheritDoc} 274 */ 275 public boolean last() throws LdapException, CursorException 276 { 277 afterLast(); 278 279 return previous(); 280 } 281 282 283 /** 284 * {@inheritDoc} 285 */ 286 @Override 287 public boolean previous() throws LdapException, CursorException 288 { 289 checkNotClosed(); 290 291 if ( userIdxCursor != null ) 292 { 293 /* 294 * No need to do the same check that is done in next() since 295 * values are decreasing with calls to previous(). We will 296 * always have lesser values. 297 */ 298 return setAvailable( userIdxCursor.previous() ); 299 } 300 301 while ( uuidIdxCursor.previous() ) 302 { 303 checkNotClosed(); 304 uuidCandidate = uuidIdxCursor.get(); 305 306 if ( lessEqEvaluator.evaluate( partitionTxn, uuidCandidate ) ) 307 { 308 return setAvailable( true ); 309 } 310 else 311 { 312 uuidCandidate = null; 313 } 314 } 315 316 return setAvailable( false ); 317 } 318 319 320 /** 321 * {@inheritDoc} 322 */ 323 @Override 324 public boolean next() throws LdapException, CursorException 325 { 326 checkNotClosed(); 327 328 if ( userIdxCursor != null ) 329 { 330 /* 331 * We have to check and make sure the next value complies by 332 * being less than or eq to the expression node's value. We need 333 * to do this since values are increasing and we must limit to our 334 * upper bound. 335 */ 336 while ( userIdxCursor.next() ) 337 { 338 checkNotClosed(); 339 IndexEntry<?, String> candidate = userIdxCursor.get(); 340 341 if ( lessEqEvaluator.getComparator().compare( candidate.getKey(), 342 lessEqEvaluator.getExpression().getValue().getString() ) <= 0 ) 343 { 344 return setAvailable( true ); 345 } 346 } 347 348 return setAvailable( false ); 349 } 350 351 while ( uuidIdxCursor.next() ) 352 { 353 checkNotClosed(); 354 uuidCandidate = uuidIdxCursor.get(); 355 356 if ( lessEqEvaluator.evaluate( partitionTxn, uuidCandidate ) ) 357 { 358 return setAvailable( true ); 359 } 360 else 361 { 362 uuidCandidate = null; 363 } 364 } 365 366 return setAvailable( false ); 367 } 368 369 370 /** 371 * {@inheritDoc} 372 */ 373 public IndexEntry<V, String> get() throws CursorException 374 { 375 checkNotClosed(); 376 377 if ( userIdxCursor != null ) 378 { 379 if ( available() ) 380 { 381 return userIdxCursor.get(); 382 } 383 384 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 385 } 386 387 if ( available() ) 388 { 389 return ( IndexEntry<V, String> ) uuidCandidate; 390 } 391 392 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 393 } 394 395 396 /** 397 * {@inheritDoc} 398 */ 399 @Override 400 public void close() throws IOException 401 { 402 if ( IS_DEBUG ) 403 { 404 LOG_CURSOR.debug( "Closing LessEqCursor {}", this ); 405 } 406 407 super.close(); 408 409 if ( userIdxCursor != null ) 410 { 411 userIdxCursor.close(); 412 } 413 else 414 { 415 uuidIdxCursor.close(); 416 uuidCandidate = null; 417 } 418 } 419 420 421 /** 422 * {@inheritDoc} 423 */ 424 @Override 425 public void close( Exception cause ) throws IOException 426 { 427 LOG_CURSOR.debug( "Closing LessEqCursor {}", this ); 428 super.close( cause ); 429 430 if ( userIdxCursor != null ) 431 { 432 userIdxCursor.close( cause ); 433 } 434 else 435 { 436 uuidIdxCursor.close( cause ); 437 uuidCandidate = null; 438 } 439 } 440 441 442 /** 443 * @see Object#toString() 444 */ 445 @Override 446 public String toString( String tabs ) 447 { 448 StringBuilder sb = new StringBuilder(); 449 450 sb.append( tabs ).append( "LessEqCursor (" ); 451 452 if ( available() ) 453 { 454 sb.append( "available)" ); 455 } 456 else 457 { 458 sb.append( "absent)" ); 459 } 460 461 sb.append( "#candidate<" ).append( uuidCandidate ).append( ">:\n" ); 462 463 sb.append( tabs + " >>" ).append( lessEqEvaluator ).append( '\n' ); 464 465 if ( userIdxCursor != null ) 466 { 467 sb.append( tabs + " <user>\n" ); 468 sb.append( userIdxCursor.toString( tabs + " " ) ); 469 } 470 471 if ( uuidIdxCursor != null ) 472 { 473 sb.append( tabs + " <uuid>\n" ); 474 sb.append( uuidIdxCursor.toString( tabs + " " ) ); 475 } 476 477 return sb.toString(); 478 } 479 480 481 /** 482 * @see Object#toString() 483 */ 484 public String toString() 485 { 486 return toString( "" ); 487 } 488}