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.getString() ); 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 @Override 241 public boolean previous() throws LdapException, CursorException 242 { 243 if ( userIdxCursor != null ) 244 { 245 return userIdxCursor.previous(); 246 } 247 248 while ( uuidIdxCursor.previous() ) 249 { 250 checkNotClosed(); 251 IndexEntry<?, String> candidate = uuidIdxCursor.get(); 252 253 if ( equalityEvaluator.evaluate( partitionTxn, candidate ) ) 254 { 255 return setAvailable( true ); 256 } 257 } 258 259 return setAvailable( false ); 260 } 261 262 263 /** 264 * {@inheritDoc} 265 */ 266 @Override 267 public boolean next() throws LdapException, CursorException 268 { 269 if ( userIdxCursor != null ) 270 { 271 return userIdxCursor.next(); 272 } 273 274 while ( uuidIdxCursor.next() ) 275 { 276 checkNotClosed(); 277 IndexEntry<?, String> candidate = uuidIdxCursor.get(); 278 279 if ( equalityEvaluator.evaluate( partitionTxn, candidate ) ) 280 { 281 return setAvailable( true ); 282 } 283 } 284 285 return setAvailable( false ); 286 } 287 288 289 /** 290 * {@inheritDoc} 291 */ 292 @SuppressWarnings("unchecked") 293 public IndexEntry<V, String> get() throws CursorException 294 { 295 checkNotClosed(); 296 297 if ( userIdxCursor != null ) 298 { 299 return userIdxCursor.get(); 300 } 301 302 if ( available() ) 303 { 304 return ( IndexEntry<V, String> ) uuidIdxCursor.get(); 305 } 306 307 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 308 } 309 310 311 /** 312 * {@inheritDoc} 313 */ 314 @Override 315 public void close() throws IOException 316 { 317 if ( IS_DEBUG ) 318 { 319 LOG_CURSOR.debug( "Closing EqualityCursor {}", this ); 320 } 321 322 super.close(); 323 324 if ( userIdxCursor != null ) 325 { 326 userIdxCursor.close(); 327 } 328 else 329 { 330 uuidIdxCursor.close(); 331 } 332 } 333 334 335 /** 336 * {@inheritDoc} 337 */ 338 @Override 339 public void close( Exception cause ) throws IOException 340 { 341 if ( IS_DEBUG ) 342 { 343 LOG_CURSOR.debug( "Closing EqualityCursor {}", this ); 344 } 345 346 super.close( cause ); 347 348 if ( userIdxCursor != null ) 349 { 350 userIdxCursor.close( cause ); 351 } 352 else 353 { 354 uuidIdxCursor.close( cause ); 355 } 356 } 357 358 359 /** 360 * @see Object#toString() 361 */ 362 @Override 363 public String toString( String tabs ) 364 { 365 StringBuilder sb = new StringBuilder(); 366 367 sb.append( tabs ).append( "EqualityCursor (" ); 368 369 if ( available() ) 370 { 371 sb.append( "available)" ); 372 } 373 else 374 { 375 sb.append( "absent)" ); 376 } 377 378 sb.append( " :\n" ); 379 380 sb.append( tabs + " >>" ).append( equalityEvaluator ); 381 382 if ( userIdxCursor != null ) 383 { 384 sb.append( tabs + " <user>\n" ); 385 sb.append( userIdxCursor.toString( tabs + " " ) ); 386 } 387 388 if ( uuidIdxCursor != null ) 389 { 390 sb.append( tabs + " <uuid>\n" ); 391 sb.append( uuidIdxCursor.toString( tabs + " " ) ); 392 } 393 394 return sb.toString(); 395 } 396 397 398 /** 399 * @see Object#toString() 400 */ 401 public String toString() 402 { 403 return toString( "" ); 404 } 405}