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().getValue() ); 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().getValue() ); 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().getValue() ); 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 public boolean previous() throws LdapException, CursorException 287 { 288 checkNotClosed(); 289 290 if ( userIdxCursor != null ) 291 { 292 /* 293 * No need to do the same check that is done in next() since 294 * values are decreasing with calls to previous(). We will 295 * always have lesser values. 296 */ 297 return setAvailable( userIdxCursor.previous() ); 298 } 299 300 while ( uuidIdxCursor.previous() ) 301 { 302 checkNotClosed(); 303 uuidCandidate = uuidIdxCursor.get(); 304 305 if ( lessEqEvaluator.evaluate( partitionTxn, uuidCandidate ) ) 306 { 307 return setAvailable( true ); 308 } 309 else 310 { 311 uuidCandidate = null; 312 } 313 } 314 315 return setAvailable( false ); 316 } 317 318 319 /** 320 * {@inheritDoc} 321 */ 322 public boolean next() throws LdapException, CursorException 323 { 324 checkNotClosed(); 325 326 if ( userIdxCursor != null ) 327 { 328 /* 329 * We have to check and make sure the next value complies by 330 * being less than or eq to the expression node's value. We need 331 * to do this since values are increasing and we must limit to our 332 * upper bound. 333 */ 334 while ( userIdxCursor.next() ) 335 { 336 checkNotClosed(); 337 IndexEntry<?, String> candidate = userIdxCursor.get(); 338 339 if ( lessEqEvaluator.getComparator().compare( candidate.getKey(), 340 lessEqEvaluator.getExpression().getValue().getValue() ) <= 0 ) 341 { 342 return setAvailable( true ); 343 } 344 } 345 346 return setAvailable( false ); 347 } 348 349 while ( uuidIdxCursor.next() ) 350 { 351 checkNotClosed(); 352 uuidCandidate = uuidIdxCursor.get(); 353 354 if ( lessEqEvaluator.evaluate( partitionTxn, uuidCandidate ) ) 355 { 356 return setAvailable( true ); 357 } 358 else 359 { 360 uuidCandidate = null; 361 } 362 } 363 364 return setAvailable( false ); 365 } 366 367 368 /** 369 * {@inheritDoc} 370 */ 371 public IndexEntry<V, String> get() throws CursorException 372 { 373 checkNotClosed(); 374 375 if ( userIdxCursor != null ) 376 { 377 if ( available() ) 378 { 379 return userIdxCursor.get(); 380 } 381 382 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 383 } 384 385 if ( available() ) 386 { 387 return ( IndexEntry<V, String> ) uuidCandidate; 388 } 389 390 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 391 } 392 393 394 /** 395 * {@inheritDoc} 396 */ 397 @Override 398 public void close() throws IOException 399 { 400 if ( IS_DEBUG ) 401 { 402 LOG_CURSOR.debug( "Closing LessEqCursor {}", this ); 403 } 404 405 super.close(); 406 407 if ( userIdxCursor != null ) 408 { 409 userIdxCursor.close(); 410 } 411 else 412 { 413 uuidIdxCursor.close(); 414 uuidCandidate = null; 415 } 416 } 417 418 419 /** 420 * {@inheritDoc} 421 */ 422 @Override 423 public void close( Exception cause ) throws IOException 424 { 425 LOG_CURSOR.debug( "Closing LessEqCursor {}", this ); 426 super.close( cause ); 427 428 if ( userIdxCursor != null ) 429 { 430 userIdxCursor.close( cause ); 431 } 432 else 433 { 434 uuidIdxCursor.close( cause ); 435 uuidCandidate = null; 436 } 437 } 438 439 440 /** 441 * @see Object#toString() 442 */ 443 @Override 444 public String toString( String tabs ) 445 { 446 StringBuilder sb = new StringBuilder(); 447 448 sb.append( tabs ).append( "LessEqCursor (" ); 449 450 if ( available() ) 451 { 452 sb.append( "available)" ); 453 } 454 else 455 { 456 sb.append( "absent)" ); 457 } 458 459 sb.append( "#candidate<" ).append( uuidCandidate ).append( ">:\n" ); 460 461 sb.append( tabs + " >>" ).append( lessEqEvaluator ).append( '\n' ); 462 463 if ( userIdxCursor != null ) 464 { 465 sb.append( tabs + " <user>\n" ); 466 sb.append( userIdxCursor.toString( tabs + " " ) ); 467 } 468 469 if ( uuidIdxCursor != null ) 470 { 471 sb.append( tabs + " <uuid>\n" ); 472 sb.append( uuidIdxCursor.toString( tabs + " " ) ); 473 } 474 475 return sb.toString(); 476 } 477 478 479 /** 480 * @see Object#toString() 481 */ 482 public String toString() 483 { 484 return toString( "" ); 485 } 486}