View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.xdbm.search.cursor;
21  
22  
23  import java.io.IOException;
24  
25  import org.apache.directory.api.ldap.model.constants.Loggers;
26  import org.apache.directory.api.ldap.model.cursor.Cursor;
27  import org.apache.directory.api.ldap.model.cursor.CursorException;
28  import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
29  import org.apache.directory.api.ldap.model.exception.LdapException;
30  import org.apache.directory.api.ldap.model.schema.AttributeType;
31  import org.apache.directory.server.core.api.partition.PartitionTxn;
32  import org.apache.directory.server.i18n.I18n;
33  import org.apache.directory.server.xdbm.AbstractIndexCursor;
34  import org.apache.directory.server.xdbm.IndexEntry;
35  import org.apache.directory.server.xdbm.Store;
36  import org.apache.directory.server.xdbm.search.evaluator.PresenceEvaluator;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  
41  /**
42   * A returning candidates satisfying an attribute presence expression.
43   *
44   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
45   */
46  public class PresenceCursor extends AbstractIndexCursor<String>
47  {
48      /** A dedicated log for cursors */
49      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
50  
51      /** Speedup for logs */
52      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
53  
54      private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_724 );
55      private final Cursor<IndexEntry<String, String>> uuidCursor;
56      private final Cursor<IndexEntry<String, String>> presenceCursor;
57      private final PresenceEvaluator presenceEvaluator;
58  
59      /** The prefetched entry, if it's a valid one */
60      private IndexEntry<String, String> prefetched;
61  
62  
63      /**
64       * Creates a new instance of an PresenceCursor
65       * 
66       * @param partitionTxn The transaction to use
67       * @param store The store
68       * @param presenceEvaluator The Presence evaluator
69       * @throws LdapException If the cursor can't be created
70       */
71      public PresenceCursor( PartitionTxn partitionTxn, Store store, PresenceEvaluator presenceEvaluator ) 
72              throws LdapException
73      {
74          if ( IS_DEBUG )
75          {
76              LOG_CURSOR.debug( "Creating PresenceCursor {}", this );
77          }
78  
79          this.presenceEvaluator = presenceEvaluator;
80          this.partitionTxn = partitionTxn;
81          AttributeType type = presenceEvaluator.getAttributeType();
82  
83          // we don't maintain a presence index for objectClass, and entryCSN
84          // as it doesn't make sense because every entry has such an attribute
85          // instead for those attributes and all un-indexed attributes we use the ndn index
86          if ( store.hasUserIndexOn( type ) )
87          {
88              presenceCursor = store.getPresenceIndex().forwardCursor( partitionTxn, type.getOid() );
89              uuidCursor = null;
90          }
91          else
92          {
93              presenceCursor = null;
94              uuidCursor = new AllEntriesCursor( partitionTxn, store );
95          }
96      }
97  
98  
99      /**
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 }