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 public boolean previous() throws LdapException, CursorException 236 { 237 checkNotClosed(); 238 239 if ( presenceCursor != null ) 240 { 241 return presenceCursor.previous(); 242 } 243 244 while ( uuidCursor.previous() ) 245 { 246 checkNotClosed(); 247 IndexEntry<?, String> candidate = uuidCursor.get(); 248 249 if ( presenceEvaluator.evaluate( partitionTxn, candidate ) ) 250 { 251 return setAvailable( true ); 252 } 253 } 254 255 return setAvailable( false ); 256 } 257 258 259 /** 260 * {@inheritDoc} 261 */ 262 public boolean next() throws LdapException, CursorException 263 { 264 checkNotClosed(); 265 266 if ( presenceCursor != null ) 267 { 268 return presenceCursor.next(); 269 } 270 271 while ( uuidCursor.next() ) 272 { 273 checkNotClosed(); 274 IndexEntry<String, String> candidate = uuidCursor.get(); 275 276 if ( presenceEvaluator.evaluate( partitionTxn, candidate ) ) 277 { 278 prefetched = candidate; 279 280 return setAvailable( true ); 281 } 282 } 283 284 return setAvailable( false ); 285 } 286 287 288 /** 289 * {@inheritDoc} 290 */ 291 public IndexEntry<String, String> get() throws CursorException 292 { 293 checkNotClosed(); 294 295 if ( presenceCursor != null ) 296 { 297 if ( presenceCursor.available() ) 298 { 299 return presenceCursor.get(); 300 } 301 302 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 303 } 304 305 if ( available() ) 306 { 307 if ( prefetched == null ) 308 { 309 prefetched = uuidCursor.get(); 310 } 311 312 /* 313 * The value of NDN indices is the normalized dn and we want the 314 * value to be the value of the attribute in question. So we will 315 * set that accordingly here. 316 */ 317 prefetched.setKey( presenceEvaluator.getAttributeType().getOid() ); 318 319 return prefetched; 320 } 321 322 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 323 } 324 325 326 /** 327 * {@inheritDoc} 328 */ 329 @Override 330 public void close() throws IOException 331 { 332 if ( IS_DEBUG ) 333 { 334 LOG_CURSOR.debug( "Closing PresenceCursor {}", this ); 335 } 336 337 super.close(); 338 339 if ( presenceCursor != null ) 340 { 341 presenceCursor.close(); 342 } 343 else 344 { 345 uuidCursor.close(); 346 } 347 } 348 349 350 /** 351 * {@inheritDoc} 352 */ 353 @Override 354 public void close( Exception cause ) throws IOException 355 { 356 if ( IS_DEBUG ) 357 { 358 LOG_CURSOR.debug( "Closing PresenceCursor {}", this ); 359 } 360 361 super.close( cause ); 362 363 if ( presenceCursor != null ) 364 { 365 presenceCursor.close( cause ); 366 } 367 else 368 { 369 uuidCursor.close( cause ); 370 } 371 } 372 373 374 /** 375 * @see Object#toString() 376 */ 377 @Override 378 public String toString( String tabs ) 379 { 380 StringBuilder sb = new StringBuilder(); 381 382 sb.append( tabs ).append( "PresenceCursor (" ); 383 384 if ( available() ) 385 { 386 sb.append( "available)" ); 387 } 388 else 389 { 390 sb.append( "absent)" ); 391 } 392 393 sb.append( " :\n" ); 394 395 sb.append( tabs + " >>" ).append( presenceEvaluator ).append( '\n' ); 396 397 if ( presenceCursor != null ) 398 { 399 sb.append( tabs + " <presence>\n" ); 400 sb.append( presenceCursor.toString( tabs + " " ) ); 401 } 402 403 if ( uuidCursor != null ) 404 { 405 sb.append( tabs + " <uuid>\n" ); 406 sb.append( uuidCursor.toString( tabs + " " ) ); 407 } 408 409 return sb.toString(); 410 } 411 412 413 /** 414 * @see Object#toString() 415 */ 416 public String toString() 417 { 418 return toString( "" ); 419 } 420}