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