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.ApproximateEvaluator; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043 044/** 045 * A Cursor over entry candidates matching an approximate assertion filter. 046 * This Cursor really is a copy of EqualityCursor for now but later on 047 * approximate matching can be implemented and this can change. It operates 048 * in two modes. The first is when an index exists for the attribute the 049 * approximate assertion is built on. The second is when the user index for 050 * the assertion attribute does not exist. Different Cursors are used in each 051 * of these cases where the other remains null. 052 * 053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 054 */ 055public class ApproximateCursor<V> extends AbstractIndexCursor<V> 056{ 057 /** A dedicated log for cursors */ 058 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 059 060 /** Speedup for logs */ 061 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 062 063 /** The message for unsupported operations */ 064 private static final String UNSUPPORTED_MSG = "ApproximateCursors only support positioning by element when a user index exists on the asserted attribute."; 065 066 /** An approximate evaluator for candidates */ 067 private final ApproximateEvaluator<V> approximateEvaluator; 068 069 /** Cursor over attribute entry matching filter: set when index present */ 070 private final Cursor<IndexEntry<V, String>> userIdxCursor; 071 072 /** NDN Cursor on all entries in (set when no index on user attribute) */ 073 private final Cursor<IndexEntry<String, String>> uuidIdxCursor; 074 075 076 /** 077 * Creates a new instance of ApproximateCursor 078 * 079 * @param partitionTxn The transaction to use 080 * @param store The Store we want to build a cursor on 081 * @param approximateEvaluator The evaluator 082 * @throws LdapException If the creation failed 083 * @throws IndexNotFoundException If the index was not found 084 */ 085 @SuppressWarnings("unchecked") 086 public ApproximateCursor( PartitionTxn partitionTxn, Store store, ApproximateEvaluator<V> approximateEvaluator ) 087 throws LdapException, IndexNotFoundException 088 { 089 if ( IS_DEBUG ) 090 { 091 LOG_CURSOR.debug( "Creating ApproximateCursor {}", this ); 092 } 093 094 this.approximateEvaluator = approximateEvaluator; 095 this.partitionTxn = partitionTxn; 096 097 AttributeType attributeType = approximateEvaluator.getExpression().getAttributeType(); 098 Value value = approximateEvaluator.getExpression().getValue(); 099 100 if ( store.hasIndexOn( attributeType ) ) 101 { 102 Index<V, String> index = ( Index<V, String> ) store.getIndex( attributeType ); 103 userIdxCursor = index.forwardCursor( partitionTxn, ( V ) value.getString() ); 104 uuidIdxCursor = null; 105 } 106 else 107 { 108 uuidIdxCursor = new AllEntriesCursor( partitionTxn, store ); 109 userIdxCursor = null; 110 } 111 } 112 113 114 /** 115 * {@inheritDoc} 116 */ 117 protected String getUnsupportedMessage() 118 { 119 return UNSUPPORTED_MSG; 120 } 121 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override 127 public boolean available() 128 { 129 if ( userIdxCursor != null ) 130 { 131 return userIdxCursor.available(); 132 } 133 134 return super.available(); 135 } 136 137 138 /** 139 * {@inheritDoc} 140 */ 141 @Override 142 public void before( IndexEntry<V, String> element ) throws LdapException, CursorException 143 { 144 checkNotClosed(); 145 146 if ( userIdxCursor != null ) 147 { 148 userIdxCursor.before( element ); 149 } 150 else 151 { 152 super.before( element ); 153 } 154 } 155 156 157 /** 158 * {@inheritDoc} 159 */ 160 @Override 161 public void after( IndexEntry<V, String> element ) throws LdapException, CursorException 162 { 163 checkNotClosed(); 164 165 if ( userIdxCursor != null ) 166 { 167 userIdxCursor.after( element ); 168 } 169 else 170 { 171 super.after( element ); 172 } 173 } 174 175 176 /** 177 * {@inheritDoc} 178 */ 179 public void beforeFirst() throws LdapException, CursorException 180 { 181 checkNotClosed(); 182 if ( userIdxCursor != null ) 183 { 184 userIdxCursor.beforeFirst(); 185 } 186 else 187 { 188 uuidIdxCursor.beforeFirst(); 189 setAvailable( false ); 190 } 191 } 192 193 194 /** 195 * {@inheritDoc} 196 */ 197 public void afterLast() throws LdapException, CursorException 198 { 199 checkNotClosed(); 200 201 if ( userIdxCursor != null ) 202 { 203 userIdxCursor.afterLast(); 204 } 205 else 206 { 207 uuidIdxCursor.afterLast(); 208 setAvailable( false ); 209 } 210 } 211 212 213 /** 214 * {@inheritDoc} 215 */ 216 public boolean first() throws LdapException, CursorException 217 { 218 beforeFirst(); 219 220 return next(); 221 } 222 223 224 /** 225 * {@inheritDoc} 226 */ 227 public boolean last() throws LdapException, CursorException 228 { 229 afterLast(); 230 231 return previous(); 232 } 233 234 235 /** 236 * {@inheritDoc} 237 */ 238 @Override 239 public boolean previous() throws LdapException, CursorException 240 { 241 if ( userIdxCursor != null ) 242 { 243 return userIdxCursor.previous(); 244 } 245 246 while ( uuidIdxCursor.previous() ) 247 { 248 checkNotClosed(); 249 IndexEntry<?, String> candidate = uuidIdxCursor.get(); 250 251 if ( approximateEvaluator.evaluate( partitionTxn, candidate ) ) 252 { 253 return setAvailable( true ); 254 } 255 } 256 257 return setAvailable( false ); 258 } 259 260 261 /** 262 * {@inheritDoc} 263 */ 264 @Override 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 ( approximateEvaluator.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 ApproximateCursor {}", 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 ApproximateCursor {}", 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( "ApproximateCursor (" ); 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( approximateEvaluator ).append( '\n' ); 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}