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.impl.avl;
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.AbstractCursor;
27  import org.apache.directory.api.ldap.model.cursor.Cursor;
28  import org.apache.directory.api.ldap.model.cursor.CursorException;
29  import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
30  import org.apache.directory.api.ldap.model.cursor.SingletonCursor;
31  import org.apache.directory.api.ldap.model.cursor.Tuple;
32  import org.apache.directory.api.ldap.model.exception.LdapException;
33  import org.apache.directory.server.core.avltree.AvlSingletonOrOrderedSetCursor;
34  import org.apache.directory.server.core.avltree.AvlTree;
35  import org.apache.directory.server.core.avltree.AvlTreeCursor;
36  import org.apache.directory.server.core.avltree.SingletonOrOrderedSet;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  
41  /**
42   * A Cursor which walks and advance over AvlTables that may contain duplicate
43   * keys with values stored in an AvlTree.  All duplicate keys are traversed
44   * returning the key and the value in a Tuple.
45   *
46   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
47   */
48  public class AvlTableDupsCursor<K, V> extends AbstractCursor<Tuple<K, V>>
49  {
50      private static final Logger LOG = LoggerFactory.getLogger( AvlTableDupsCursor.class );
51  
52      /** A dedicated log for cursors */
53      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
54  
55      /** Speedup for logs */
56      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
57  
58      /** The AVL backed table this Cursor traverses over. */
59      private final AvlTable<K, V> table;
60  
61      /**
62       * The underlying wrapped cursor which returns Tuples whose values are
63       * either V objects or AvlTree objects.
64       */
65      private final AvlSingletonOrOrderedSetCursor<K, V> wrappedCursor;
66  
67      /**
68       * A Cursor over a set of value objects for the current key held in the
69       * containerTuple.  A new Cursor will be set for each new key as we
70       * traverse.  The Cursor traverses over either a AvlTree object full
71       * of values in a multi-valued key or it traverses over a BTree which
72       * contains the values in the key field of it's Tuples.
73       */
74      private Cursor<V> dupsCursor;
75  
76      /** The current Tuple returned from the wrapped cursor. */
77      private final Tuple<K, SingletonOrOrderedSet<V>> wrappedTuple = new Tuple<>();
78  
79      /**
80       * The Tuple that is used to return values via the get() method. This
81       * same Tuple instance will be returned every time.  At different
82       * positions it may return different values for the same key.
83       */
84      private final Tuple<K, V> returnedTuple = new Tuple<>();
85  
86      /** Whether or not a value is available when get() is called. */
87      private boolean valueAvailable;
88  
89  
90      /**
91       * Creates a new instance of AvlTableDupsCursor.
92       *
93       * @param table the AvlTable to build a Cursor on.
94       */
95      public AvlTableDupsCursor( AvlTable<K, V> table )
96      {
97          if ( IS_DEBUG )
98          {
99              LOG_CURSOR.debug( "Creating AvlTableDupsCursor {}", this );
100         }
101 
102         this.table = table;
103         this.wrappedCursor = new AvlSingletonOrOrderedSetCursor<>( table.getAvlTreeMap() );
104         LOG.debug( "Created on table {}", table.getName() );
105     }
106 
107 
108     /**
109      * {@inheritDoc}
110      */
111     public boolean available()
112     {
113         return valueAvailable;
114     }
115 
116 
117     /**
118      * {@inheritDoc}
119      */
120     public void beforeKey( K key ) throws Exception
121     {
122         beforeValue( key, null );
123     }
124 
125 
126     /**
127      * {@inheritDoc}
128      */
129     public void beforeValue( K key, V value ) throws LdapException, CursorException
130     {
131         checkNotClosed();
132         wrappedCursor.beforeKey( key );
133 
134         if ( dupsCursor != null )
135         {
136             try
137             {
138                 dupsCursor.close();
139             }
140             catch ( IOException ioe )
141             {
142                 throw new LdapException( ioe.getMessage(), ioe );
143             }
144         }
145 
146         if ( wrappedCursor.next() )
147         {
148             wrappedTuple.setBoth( wrappedCursor.get() );
149 
150             if ( wrappedTuple.getValue().isOrderedSet() )
151             {
152                 AvlTree<V> avlTree = wrappedTuple.getValue().getOrderedSet();
153                 dupsCursor = new AvlTreeCursor<>( avlTree );
154             }
155             else
156             {
157                 dupsCursor = new SingletonCursor<>(
158                     wrappedTuple.getValue().getSingleton(), wrappedCursor.getValuComparator() );
159             }
160 
161             if ( value == null )
162             {
163                 clearValue();
164                 return;
165             }
166 
167             /*
168              * The cursor over the values is only advanced if we're on the
169              * same key as the primary cursor.  This is because we want this
170              * method to really position us within a set of duplicate key
171              * entries at the proper value.
172              */
173             if ( table.getKeyComparator().compare( wrappedTuple.getKey(), key ) == 0 )
174             {
175                 dupsCursor.before( value );
176             }
177 
178             clearValue();
179             return;
180         }
181 
182         clearValue();
183         wrappedTuple.setKey( null );
184         wrappedTuple.setValue( null );
185     }
186 
187 
188     /**
189      * {@inheritDoc}
190      */
191     public void afterKey( K key ) throws Exception
192     {
193         afterValue( key, null );
194     }
195 
196 
197     /**
198      * {@inheritDoc}
199      */
200     public void afterValue( K key, V value ) throws LdapException, CursorException
201     {
202         checkNotClosed();
203 
204         if ( dupsCursor != null )
205         {
206             try
207             {
208                 dupsCursor.close();
209             }
210             catch ( IOException ioe )
211             {
212                 throw new LdapException( ioe.getMessage(), ioe );
213             }
214         }
215 
216         /*
217          * There is a subtle difference between after and before handling
218          * with dupicate key values.  Say we have the following tuples:
219          *
220          * (0, 0)
221          * (1, 1)
222          * (1, 2)
223          * (1, 3)
224          * (2, 2)
225          *
226          * If we request an after cursor on (1, 2).  We must make sure that
227          * the container cursor does not advance after the entry with key 1
228          * since this would result in us skip returning (1. 3) on the call to
229          * next which will incorrectly return (2, 2) instead.
230          *
231          * So if the value is null in the element then we don't care about
232          * this obviously since we just want to advance past the duplicate key
233          * values all together.  But when it is not null, then we want to
234          * go right before this key instead of after it.
235          */
236 
237         if ( value == null )
238         {
239             wrappedCursor.afterKey( key );
240         }
241         else
242         {
243             wrappedCursor.beforeKey( key );
244         }
245 
246         if ( wrappedCursor.next() )
247         {
248             wrappedTuple.setBoth( wrappedCursor.get() );
249             SingletonOrOrderedSet<V> values = wrappedTuple.getValue();
250 
251             if ( values.isOrderedSet() )
252             {
253                 AvlTree<V> set = values.getOrderedSet();
254                 dupsCursor = new AvlTreeCursor<>( set );
255             }
256             else
257             {
258                 dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() );
259             }
260 
261             if ( value == null )
262             {
263                 clearValue();
264 
265                 return;
266             }
267 
268             // only advance the dupsCursor if we're on same key
269             if ( table.getKeyComparator().compare( wrappedTuple.getKey(), key ) == 0 )
270             {
271                 dupsCursor.after( value );
272             }
273 
274             clearValue();
275             return;
276         }
277 
278         clearValue();
279         wrappedTuple.setKey( null );
280         wrappedTuple.setValue( null );
281     }
282 
283 
284     /**
285      * {@inheritDoc}
286      */
287     public void after( Tuple<K, V> element ) throws LdapException, CursorException
288     {
289         afterValue( element.getKey(), element.getValue() );
290     }
291 
292 
293     /**
294      * {@inheritDoc}
295      */
296     public void afterLast() throws LdapException, CursorException
297     {
298         checkNotClosed();
299         clearValue();
300         wrappedCursor.afterLast();
301         wrappedTuple.setKey( null );
302         wrappedTuple.setValue( null );
303 
304         if ( dupsCursor != null )
305         {
306             try
307             {            
308                 dupsCursor.close();
309             }
310             catch ( IOException ioe )
311             {
312                 throw new LdapException( ioe.getMessage(), ioe );
313             }
314         }
315 
316         dupsCursor = null;
317     }
318 
319 
320     /**
321      * {@inheritDoc}
322      */
323     public void before( Tuple<K, V> element ) throws LdapException, CursorException
324     {
325         beforeValue( element.getKey(), element.getValue() );
326     }
327 
328 
329     /**
330      * {@inheritDoc}
331      */
332     public void beforeFirst() throws LdapException, CursorException
333     {
334         checkNotClosed();
335         clearValue();
336         wrappedCursor.beforeFirst();
337         wrappedTuple.setKey( null );
338         wrappedTuple.setValue( null );
339 
340         if ( dupsCursor != null )
341         {
342             try
343             {
344                 dupsCursor.close();
345             }
346             catch ( IOException ioe )
347             {
348                 throw new LdapException( ioe.getMessage(), ioe );
349             }
350         }
351 
352         dupsCursor = null;
353     }
354 
355 
356     /**
357      * {@inheritDoc}
358      */
359     public boolean first() throws LdapException, CursorException
360     {
361         checkNotClosed();
362         clearValue();
363 
364         if ( dupsCursor != null )
365         {
366             try
367             {
368                 dupsCursor.close();
369             }
370             catch ( IOException ioe )
371             {
372                 throw new LdapException( ioe.getMessage(), ioe );
373             }
374         }
375 
376         dupsCursor = null;
377 
378         if ( wrappedCursor.first() )
379         {
380             wrappedTuple.setBoth( wrappedCursor.get() );
381             SingletonOrOrderedSet<V> values = wrappedTuple.getValue();
382 
383             if ( values.isOrderedSet() )
384             {
385                 dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() );
386             }
387             else
388             {
389                 dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() );
390             }
391 
392             /*
393              * Since only tables with duplicate keys enabled use this
394              * cursor, entries must have at least one value, and therefore
395              * call to last() will always return true.
396              */
397             dupsCursor.first();
398             valueAvailable = true;
399             returnedTuple.setKey( wrappedTuple.getKey() );
400             returnedTuple.setValue( dupsCursor.get() );
401 
402             return true;
403         }
404 
405         return false;
406     }
407 
408 
409     /**
410      * {@inheritDoc}
411      */
412     public Tuple<K, V> get() throws CursorException
413     {
414         checkNotClosed();
415 
416         if ( !valueAvailable )
417         {
418             throw new InvalidCursorPositionException();
419         }
420 
421         return returnedTuple;
422     }
423 
424 
425     /**
426      * {@inheritDoc}
427      */
428     public boolean last() throws LdapException, CursorException
429     {
430         checkNotClosed();
431         clearValue();
432 
433         if ( dupsCursor != null )
434         {
435             try
436             {
437                 dupsCursor.close();
438             }
439             catch ( IOException ioe )
440             {
441                 throw new LdapException( ioe.getMessage(), ioe );
442             }
443         }
444 
445         if ( wrappedCursor.last() )
446         {
447             wrappedTuple.setBoth( wrappedCursor.get() );
448             SingletonOrOrderedSet<V> values = wrappedTuple.getValue();
449 
450             if ( values.isOrderedSet() )
451             {
452                 dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() );
453             }
454             else
455             {
456                 dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() );
457             }
458 
459             /*
460              * Since only tables with duplicate keys enabled use this
461              * cursor, entries must have at least one value, and therefore
462              * call to last() will always return true.
463              */
464             dupsCursor.last();
465             valueAvailable = true;
466             returnedTuple.setKey( wrappedTuple.getKey() );
467             returnedTuple.setValue( dupsCursor.get() );
468 
469             return true;
470         }
471 
472         return false;
473     }
474 
475 
476     /**
477      * {@inheritDoc}
478      */
479     public boolean next() throws LdapException, CursorException
480     {
481         checkNotClosed();
482 
483         /*
484          * If the cursor over the values of the current key is null or is
485          * extinguished then we need to advance to the next key.
486          */
487         if ( null == dupsCursor || !dupsCursor.next() )
488         {
489             if ( dupsCursor != null )
490             {
491                 try
492                 {
493                     dupsCursor.close();
494                 }
495                 catch ( IOException ioe )
496                 {
497                     throw new LdapException( ioe.getMessage(), ioe );
498                 }
499             }
500 
501             /*
502              * If the wrappedCursor cursor has more elements we get the next
503              * key/AvlTree Tuple to work with and get a cursor over it.
504              */
505             if ( wrappedCursor.next() )
506             {
507                 wrappedTuple.setBoth( wrappedCursor.get() );
508                 SingletonOrOrderedSet<V> values = wrappedTuple.getValue();
509 
510                 if ( values.isOrderedSet() )
511                 {
512                     dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() );
513                 }
514                 else
515                 {
516                     dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() );
517                 }
518 
519                 /*
520                  * Since only tables with duplicate keys enabled use this
521                  * cursor, entries must have at least one value, and therefore
522                  * call to next() after bringing the cursor to beforeFirst()
523                  * will always return true.
524                  */
525                 dupsCursor.beforeFirst();
526                 dupsCursor.next();
527             }
528             else
529             {
530                 dupsCursor = null;
531 
532                 return false;
533             }
534         }
535 
536         /*
537          * If we get to this point then cursor has more elements and
538          * wrappedTuple holds the Tuple containing the key and the
539          * AvlTree of values for that key which the Cursor traverses.  All we
540          * need to do is populate our tuple object with the key and the value
541          * in the cursor.
542          */
543         returnedTuple.setKey( wrappedTuple.getKey() );
544         returnedTuple.setValue( dupsCursor.get() );
545 
546         valueAvailable = true;
547         return true;
548     }
549 
550 
551     /**
552      * {@inheritDoc}
553      */
554     public boolean previous() throws LdapException, CursorException
555     {
556         checkNotClosed();
557         /*
558          * If the cursor over the values of the current key is null or is
559          * extinguished then we need to advance to the previous key.
560          */
561         if ( ( null == dupsCursor ) || !dupsCursor.previous() )
562         {
563             if ( dupsCursor != null )
564             {
565                 try
566                 {
567                     dupsCursor.close();
568                 }
569                 catch ( IOException ioe )
570                 {
571                     throw new LdapException( ioe.getMessage(), ioe );
572                 }
573             }
574 
575             /*
576              * If the wrappedCursor cursor has more elements we get the previous
577              * key/AvlTree Tuple to work with and get a cursor over it's
578              * values.
579              */
580             if ( wrappedCursor.previous() )
581             {
582                 wrappedTuple.setBoth( wrappedCursor.get() );
583                 SingletonOrOrderedSet<V> values = wrappedTuple.getValue();
584 
585                 if ( values.isOrderedSet() )
586                 {
587                     dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() );
588                 }
589                 else
590                 {
591                     dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() );
592                 }
593 
594                 /*
595                  * Since only tables with duplicate keys enabled use this
596                  * cursor, entries must have at least one value, and therefore
597                  * call to previous() after bringing the cursor to afterLast()
598                  * will always return true.
599                  */
600                 dupsCursor.afterLast();
601                 dupsCursor.previous();
602             }
603             else
604             {
605                 dupsCursor = null;
606 
607                 return false;
608             }
609         }
610 
611         returnedTuple.setKey( wrappedTuple.getKey() );
612         returnedTuple.setValue( dupsCursor.get() );
613 
614         valueAvailable = true;
615         return true;
616     }
617 
618 
619     @Override
620     public void close() throws IOException
621     {
622         if ( IS_DEBUG )
623         {
624             LOG_CURSOR.debug( "Closing AvlTableDupsCursor {}", this );
625         }
626 
627         if ( dupsCursor != null )
628         {
629             dupsCursor.close();
630         }
631 
632         wrappedCursor.close();
633     }
634 
635 
636     @Override
637     public void close( Exception reason ) throws IOException
638     {
639         if ( IS_DEBUG )
640         {
641             LOG_CURSOR.debug( "Closing AvlTableDupsCursor {}", this );
642         }
643 
644         if ( dupsCursor != null )
645         {
646             dupsCursor.close();
647         }
648 
649         wrappedCursor.close();
650     }
651 
652 
653     /**
654      * Clears the returned Tuple and makes sure valueAvailable returns false.
655      */
656     private void clearValue()
657     {
658         returnedTuple.setKey( null );
659         returnedTuple.setValue( null );
660         valueAvailable = false;
661     }
662 }