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.IndexEntry; 035import org.apache.directory.server.xdbm.Store; 036import org.apache.directory.server.xdbm.search.evaluator.PresenceEvaluator; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040 041/** 042 * A returning candidates satisfying an attribute presence expression. 043 * 044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 045 */ 046public class PresenceCursor extends AbstractIndexCursor<String> 047{ 048 /** A dedicated log for cursors */ 049 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 050 051 /** Speedup for logs */ 052 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 053 054 private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_724 ); 055 private final Cursor<IndexEntry<String, String>> uuidCursor; 056 private final Cursor<IndexEntry<String, String>> presenceCursor; 057 private final PresenceEvaluator presenceEvaluator; 058 059 /** The prefetched entry, if it's a valid one */ 060 private IndexEntry<String, String> prefetched; 061 062 063 /** 064 * Creates a new instance of an PresenceCursor 065 * 066 * @param partitionTxn The transaction to use 067 * @param store The store 068 * @param presenceEvaluator The Presence evaluator 069 * @throws LdapException If the cursor can't be created 070 */ 071 public PresenceCursor( PartitionTxn partitionTxn, Store store, PresenceEvaluator presenceEvaluator ) 072 throws LdapException 073 { 074 if ( IS_DEBUG ) 075 { 076 LOG_CURSOR.debug( "Creating PresenceCursor {}", this ); 077 } 078 079 this.presenceEvaluator = presenceEvaluator; 080 this.partitionTxn = partitionTxn; 081 AttributeType type = presenceEvaluator.getAttributeType(); 082 083 // we don't maintain a presence index for objectClass, and entryCSN 084 // as it doesn't make sense because every entry has such an attribute 085 // instead for those attributes and all un-indexed attributes we use the ndn index 086 if ( store.hasUserIndexOn( type ) ) 087 { 088 presenceCursor = store.getPresenceIndex().forwardCursor( partitionTxn, type.getOid() ); 089 uuidCursor = null; 090 } 091 else 092 { 093 presenceCursor = null; 094 uuidCursor = new AllEntriesCursor( partitionTxn, store ); 095 } 096 } 097 098 099 /** 100 * {@inheritDoc} 101 */ 102 protected String getUnsupportedMessage() 103 { 104 return UNSUPPORTED_MSG; 105 } 106 107 108 /** 109 * {@inheritDoc} 110 */ 111 @Override 112 public boolean available() 113 { 114 if ( presenceCursor != null ) 115 { 116 return presenceCursor.available(); 117 } 118 119 return super.available(); 120 } 121 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override 127 public void before( IndexEntry<String, String> element ) throws LdapException, CursorException 128 { 129 checkNotClosed(); 130 131 if ( presenceCursor != null ) 132 { 133 presenceCursor.before( element ); 134 135 return; 136 } 137 138 super.before( element ); 139 } 140 141 142 /** 143 * {@inheritDoc} 144 */ 145 @Override 146 public void after( IndexEntry<String, String> element ) throws LdapException, CursorException 147 { 148 checkNotClosed(); 149 150 if ( presenceCursor != null ) 151 { 152 presenceCursor.after( element ); 153 154 return; 155 } 156 157 super.after( element ); 158 } 159 160 161 /** 162 * {@inheritDoc} 163 */ 164 public void beforeFirst() throws LdapException, CursorException 165 { 166 checkNotClosed(); 167 168 if ( presenceCursor != null ) 169 { 170 presenceCursor.beforeFirst(); 171 172 return; 173 } 174 175 uuidCursor.beforeFirst(); 176 setAvailable( false ); 177 } 178 179 180 /** 181 * {@inheritDoc} 182 */ 183 public void afterLast() throws LdapException, CursorException 184 { 185 checkNotClosed(); 186 187 if ( presenceCursor != null ) 188 { 189 presenceCursor.afterLast(); 190 return; 191 } 192 193 uuidCursor.afterLast(); 194 setAvailable( false ); 195 } 196 197 198 /** 199 * {@inheritDoc} 200 */ 201 public boolean first() throws LdapException, CursorException 202 { 203 checkNotClosed(); 204 if ( presenceCursor != null ) 205 { 206 return presenceCursor.first(); 207 } 208 209 beforeFirst(); 210 return next(); 211 } 212 213 214 /** 215 * {@inheritDoc} 216 */ 217 public boolean last() throws LdapException, CursorException 218 { 219 checkNotClosed(); 220 221 if ( presenceCursor != null ) 222 { 223 return presenceCursor.last(); 224 } 225 226 afterLast(); 227 228 return previous(); 229 } 230 231 232 /** 233 * {@inheritDoc} 234 */ 235 @Override 236 public boolean previous() throws LdapException, CursorException 237 { 238 checkNotClosed(); 239 240 if ( presenceCursor != null ) 241 { 242 return presenceCursor.previous(); 243 } 244 245 while ( uuidCursor.previous() ) 246 { 247 checkNotClosed(); 248 IndexEntry<?, String> candidate = uuidCursor.get(); 249 250 if ( presenceEvaluator.evaluate( partitionTxn, candidate ) ) 251 { 252 return setAvailable( true ); 253 } 254 } 255 256 return setAvailable( false ); 257 } 258 259 260 /** 261 * {@inheritDoc} 262 */ 263 @Override 264 public boolean next() throws LdapException, CursorException 265 { 266 checkNotClosed(); 267 268 if ( presenceCursor != null ) 269 { 270 return presenceCursor.next(); 271 } 272 273 while ( uuidCursor.next() ) 274 { 275 checkNotClosed(); 276 IndexEntry<String, String> candidate = uuidCursor.get(); 277 278 if ( presenceEvaluator.evaluate( partitionTxn, candidate ) ) 279 { 280 prefetched = candidate; 281 282 return setAvailable( true ); 283 } 284 } 285 286 return setAvailable( false ); 287 } 288 289 290 /** 291 * {@inheritDoc} 292 */ 293 public IndexEntry<String, String> get() throws CursorException 294 { 295 checkNotClosed(); 296 297 if ( presenceCursor != null ) 298 { 299 if ( presenceCursor.available() ) 300 { 301 return presenceCursor.get(); 302 } 303 304 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 305 } 306 307 if ( available() ) 308 { 309 if ( prefetched == null ) 310 { 311 prefetched = uuidCursor.get(); 312 } 313 314 /* 315 * The value of NDN indices is the normalized dn and we want the 316 * value to be the value of the attribute in question. So we will 317 * set that accordingly here. 318 */ 319 prefetched.setKey( presenceEvaluator.getAttributeType().getOid() ); 320 321 return prefetched; 322 } 323 324 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 325 } 326 327 328 /** 329 * {@inheritDoc} 330 */ 331 @Override 332 public void close() throws IOException 333 { 334 if ( IS_DEBUG ) 335 { 336 LOG_CURSOR.debug( "Closing PresenceCursor {}", this ); 337 } 338 339 super.close(); 340 341 if ( presenceCursor != null ) 342 { 343 presenceCursor.close(); 344 } 345 else 346 { 347 uuidCursor.close(); 348 } 349 } 350 351 352 /** 353 * {@inheritDoc} 354 */ 355 @Override 356 public void close( Exception cause ) throws IOException 357 { 358 if ( IS_DEBUG ) 359 { 360 LOG_CURSOR.debug( "Closing PresenceCursor {}", this ); 361 } 362 363 super.close( cause ); 364 365 if ( presenceCursor != null ) 366 { 367 presenceCursor.close( cause ); 368 } 369 else 370 { 371 uuidCursor.close( cause ); 372 } 373 } 374 375 376 /** 377 * @see Object#toString() 378 */ 379 @Override 380 public String toString( String tabs ) 381 { 382 StringBuilder sb = new StringBuilder(); 383 384 sb.append( tabs ).append( "PresenceCursor (" ); 385 386 if ( available() ) 387 { 388 sb.append( "available)" ); 389 } 390 else 391 { 392 sb.append( "absent)" ); 393 } 394 395 sb.append( " :\n" ); 396 397 sb.append( tabs + " >>" ).append( presenceEvaluator ).append( '\n' ); 398 399 if ( presenceCursor != null ) 400 { 401 sb.append( tabs + " <presence>\n" ); 402 sb.append( presenceCursor.toString( tabs + " " ) ); 403 } 404 405 if ( uuidCursor != null ) 406 { 407 sb.append( tabs + " <uuid>\n" ); 408 sb.append( uuidCursor.toString( tabs + " " ) ); 409 } 410 411 return sb.toString(); 412 } 413 414 415 /** 416 * @see Object#toString() 417 */ 418 public String toString() 419 { 420 return toString( "" ); 421 } 422}