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.entry.Value; 030import org.apache.directory.api.ldap.model.exception.LdapException; 031import org.apache.directory.api.ldap.model.schema.AttributeType; 032import org.apache.directory.server.core.api.partition.PartitionTxn; 033import org.apache.directory.server.i18n.I18n; 034import org.apache.directory.server.xdbm.AbstractIndexCursor; 035import org.apache.directory.server.xdbm.Index; 036import org.apache.directory.server.xdbm.IndexEntry; 037import org.apache.directory.server.xdbm.IndexNotFoundException; 038import org.apache.directory.server.xdbm.Store; 039import org.apache.directory.server.xdbm.search.evaluator.EqualityEvaluator; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043 044/** 045 * A Cursor over entry candidates matching an equality assertion filter. This 046 * Cursor operates in two modes. The first is when an index exists for the 047 * attribute the equality assertion is built on. The second is when the user 048 * index for the assertion attribute does not exist. Different Cursors are 049 * used in each of these cases where the other remains null. 050 * 051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 052 */ 053public class EqualityCursor<V> extends AbstractIndexCursor<V> 054{ 055 /** A dedicated log for cursors */ 056 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 057 058 /** Speedup for logs */ 059 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 060 061 /** The message for unsupported operations */ 062 private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_714 ); 063 064 /** An equality evaluator for candidates */ 065 private final EqualityEvaluator<V> equalityEvaluator; 066 067 /** Cursor over attribute entry matching filter: set when index present */ 068 private final Cursor<IndexEntry<V, String>> userIdxCursor; 069 070 /** NDN Cursor on all entries in (set when no index on user attribute) */ 071 private final Cursor<IndexEntry<String, String>> uuidIdxCursor; 072 073 074 /** 075 * Creates a new instance of an EqualityCursor 076 * 077 * @param partitionTxn The transaction to use 078 * @param store The store 079 * @param equalityEvaluator The EqualityEvaluator 080 * @throws LdapException If the creation failed 081 * @throws IndexNotFoundException If the index was not found 082 */ 083 @SuppressWarnings("unchecked") 084 public EqualityCursor( PartitionTxn partitionTxn, Store store, EqualityEvaluator<V> equalityEvaluator ) 085 throws LdapException, IndexNotFoundException 086 { 087 if ( IS_DEBUG ) 088 { 089 LOG_CURSOR.debug( "Creating EqualityCursor {}", this ); 090 } 091 092 this.equalityEvaluator = equalityEvaluator; 093 this.partitionTxn = partitionTxn; 094 095 AttributeType attributeType = equalityEvaluator.getExpression().getAttributeType(); 096 Value value = equalityEvaluator.getExpression().getValue(); 097 098 if ( store.hasIndexOn( attributeType ) ) 099 { 100 Index<V, String> userIndex = ( Index<V, String> ) store.getIndex( attributeType ); 101 String normalizedValue = attributeType.getEquality().getNormalizer().normalize( value.getValue() ); 102 userIdxCursor = userIndex.forwardCursor( partitionTxn, ( V ) normalizedValue ); 103 uuidIdxCursor = null; 104 } 105 else 106 { 107 uuidIdxCursor = new AllEntriesCursor( partitionTxn, store ); 108 userIdxCursor = null; 109 } 110 } 111 112 113 /** 114 * {@inheritDoc} 115 */ 116 protected String getUnsupportedMessage() 117 { 118 return UNSUPPORTED_MSG; 119 } 120 121 122 /** 123 * {@inheritDoc} 124 */ 125 @Override 126 public boolean available() 127 { 128 if ( userIdxCursor != null ) 129 { 130 return userIdxCursor.available(); 131 } 132 133 return super.available(); 134 } 135 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override 141 public void before( IndexEntry<V, String> element ) throws LdapException, CursorException 142 { 143 checkNotClosed(); 144 145 if ( userIdxCursor != null ) 146 { 147 userIdxCursor.before( element ); 148 } 149 else 150 { 151 super.before( element ); 152 } 153 } 154 155 156 /** 157 * {@inheritDoc} 158 */ 159 @Override 160 public void after( IndexEntry<V, String> element ) throws LdapException, CursorException 161 { 162 checkNotClosed(); 163 164 if ( userIdxCursor != null ) 165 { 166 userIdxCursor.after( element ); 167 } 168 else 169 { 170 super.after( element ); 171 } 172 } 173 174 175 /** 176 * {@inheritDoc} 177 */ 178 public void beforeFirst() throws LdapException, CursorException 179 { 180 checkNotClosed(); 181 182 if ( userIdxCursor != null ) 183 { 184 userIdxCursor.beforeFirst(); 185 } 186 else 187 { 188 uuidIdxCursor.beforeFirst(); 189 } 190 191 setAvailable( false ); 192 } 193 194 195 /** 196 * {@inheritDoc} 197 */ 198 public void afterLast() throws LdapException, CursorException 199 { 200 checkNotClosed(); 201 202 if ( userIdxCursor != null ) 203 { 204 userIdxCursor.afterLast(); 205 } 206 else 207 { 208 uuidIdxCursor.afterLast(); 209 } 210 211 setAvailable( false ); 212 } 213 214 215 /** 216 * {@inheritDoc} 217 */ 218 public boolean first() throws LdapException, CursorException 219 { 220 beforeFirst(); 221 222 return next(); 223 } 224 225 226 /** 227 * {@inheritDoc} 228 */ 229 public boolean last() throws LdapException, CursorException 230 { 231 afterLast(); 232 233 return previous(); 234 } 235 236 237 /** 238 * {@inheritDoc} 239 */ 240 public boolean previous() throws LdapException, CursorException 241 { 242 if ( userIdxCursor != null ) 243 { 244 return userIdxCursor.previous(); 245 } 246 247 while ( uuidIdxCursor.previous() ) 248 { 249 checkNotClosed(); 250 IndexEntry<?, String> candidate = uuidIdxCursor.get(); 251 252 if ( equalityEvaluator.evaluate( partitionTxn, candidate ) ) 253 { 254 return setAvailable( true ); 255 } 256 } 257 258 return setAvailable( false ); 259 } 260 261 262 /** 263 * {@inheritDoc} 264 */ 265 public boolean next() throws LdapException, CursorException 266 { 267 if ( userIdxCursor != null ) 268 { 269 return userIdxCursor.next(); 270 } 271 272 while ( uuidIdxCursor.next() ) 273 { 274 checkNotClosed(); 275 IndexEntry<?, String> candidate = uuidIdxCursor.get(); 276 277 if ( equalityEvaluator.evaluate( partitionTxn, candidate ) ) 278 { 279 return setAvailable( true ); 280 } 281 } 282 283 return setAvailable( false ); 284 } 285 286 287 /** 288 * {@inheritDoc} 289 */ 290 @SuppressWarnings("unchecked") 291 public IndexEntry<V, String> get() throws CursorException 292 { 293 checkNotClosed(); 294 295 if ( userIdxCursor != null ) 296 { 297 return userIdxCursor.get(); 298 } 299 300 if ( available() ) 301 { 302 return ( IndexEntry<V, String> ) uuidIdxCursor.get(); 303 } 304 305 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 306 } 307 308 309 /** 310 * {@inheritDoc} 311 */ 312 @Override 313 public void close() throws IOException 314 { 315 if ( IS_DEBUG ) 316 { 317 LOG_CURSOR.debug( "Closing EqualityCursor {}", this ); 318 } 319 320 super.close(); 321 322 if ( userIdxCursor != null ) 323 { 324 userIdxCursor.close(); 325 } 326 else 327 { 328 uuidIdxCursor.close(); 329 } 330 } 331 332 333 /** 334 * {@inheritDoc} 335 */ 336 @Override 337 public void close( Exception cause ) throws IOException 338 { 339 if ( IS_DEBUG ) 340 { 341 LOG_CURSOR.debug( "Closing EqualityCursor {}", this ); 342 } 343 344 super.close( cause ); 345 346 if ( userIdxCursor != null ) 347 { 348 userIdxCursor.close( cause ); 349 } 350 else 351 { 352 uuidIdxCursor.close( cause ); 353 } 354 } 355 356 357 /** 358 * @see Object#toString() 359 */ 360 @Override 361 public String toString( String tabs ) 362 { 363 StringBuilder sb = new StringBuilder(); 364 365 sb.append( tabs ).append( "EqualityCursor (" ); 366 367 if ( available() ) 368 { 369 sb.append( "available)" ); 370 } 371 else 372 { 373 sb.append( "absent)" ); 374 } 375 376 sb.append( " :\n" ); 377 378 sb.append( tabs + " >>" ).append( equalityEvaluator ); 379 380 if ( userIdxCursor != null ) 381 { 382 sb.append( tabs + " <user>\n" ); 383 sb.append( userIdxCursor.toString( tabs + " " ) ); 384 } 385 386 if ( uuidIdxCursor != null ) 387 { 388 sb.append( tabs + " <uuid>\n" ); 389 sb.append( uuidIdxCursor.toString( tabs + " " ) ); 390 } 391 392 return sb.toString(); 393 } 394 395 396 /** 397 * @see Object#toString() 398 */ 399 public String toString() 400 { 401 return toString( "" ); 402 } 403}