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  import jdbm.helper.TupleBrowser;
27  
28  import org.apache.directory.api.ldap.model.constants.Loggers;
29  import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
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.i18n.I18n;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  
39  /**
40   * A cursor for browsing tables with duplicates which returns the container
41   * for values rather than just the value.
42   *
43   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
44   * @param K The Key
45   * @param V The associated value
46   */
47  public class DupsContainerCursor<K, V> extends AbstractCursor<Tuple<K, DupsContainer<V>>>
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      /** The JDBM table we are building a cursor over */
56      private final JdbmTable<K, V> table;
57  
58      /** A container to pass to the underlying JDBM to get back a tuple */
59      private jdbm.helper.Tuple<K, V> jdbmTuple = new jdbm.helper.Tuple<>();
60  
61      private Tuple<K, DupsContainer<V>> returnedTuple = new Tuple<>();
62  
63      /** A browser over the JDBM Table */
64      private TupleBrowser<K, V> browser;
65  
66      /** Tells if we have a tuple to return */
67      private boolean valueAvailable;
68  
69      /** TODO : do we need this flag ??? */
70      private Boolean forwardDirection;
71  
72  
73      /**
74       * Creates a Cursor over the tuples of a JDBM table.
75       *
76       * @param table the JDBM Table to build a Cursor over
77       */
78      public DupsContainerCursor( JdbmTable<K, V> table )
79      {
80          if ( IS_DEBUG )
81          {
82              LOG_CURSOR.debug( "Creating DupsContainerCursor {}", this );
83          }
84  
85          if ( !table.isDupsEnabled() )
86          {
87              throw new IllegalStateException( I18n.err( I18n.ERR_572 ) );
88          }
89  
90          this.table = table;
91      }
92  
93  
94      /**
95       * Clean the tuples we use to store the returned resut.
96       */
97      private void clearValue()
98      {
99          returnedTuple.setKey( null );
100         returnedTuple.setValue( null );
101         jdbmTuple.setKey( null );
102         jdbmTuple.setValue( null );
103         valueAvailable = false;
104     }
105 
106 
107     /**
108      * {@inheritDoc}
109      */
110     public boolean available()
111     {
112         return valueAvailable;
113     }
114 
115 
116     /**
117      * {@inheritDoc}
118      */
119     @SuppressWarnings("unchecked")
120     public void beforeKey( K key ) throws CursorException
121     {
122         checkNotClosed();
123         try
124         {
125             browser = ( ( BTree<K, V> ) table.getBTree() ).browse( key );
126             forwardDirection = null;
127             clearValue();
128         }
129         catch ( IOException e )
130         {
131             throw new CursorException( e );
132         }
133     }
134 
135 
136     /**
137      * {@inheritDoc}
138      */
139     @SuppressWarnings("unchecked")
140     public void afterKey( K key ) throws CursorException
141     {
142         checkNotClosed();
143 
144         try
145         {
146             browser = ( ( BTree<K, V> ) table.getBTree() ).browse( key );
147             forwardDirection = null;
148 
149             /*
150              * While the next value is less than or equal to the element keep
151              * advancing forward to the next item.  If we cannot advance any
152              * further then stop and return.  If we find a value greater than
153              * the element then we stop, backup, and return so subsequent calls
154              * to getNext() will return a value greater than the element.
155              */
156             while ( browser.getNext( jdbmTuple ) )
157             {
158                 checkNotClosed();
159                 K next = jdbmTuple.getKey();
160 
161                 int nextCompared = table.getKeyComparator().compare( next, key );
162 
163                 if ( nextCompared > 0 )
164                 {
165                     browser.getPrevious( jdbmTuple );
166 
167                     // switch in direction bug workaround: when a JDBM browser
168                     // switches direction with next then previous as is occurring
169                     // here then two previous moves are needed.
170                     browser.getPrevious( jdbmTuple );
171                     forwardDirection = false;
172                     clearValue();
173 
174                     return;
175                 }
176             }
177 
178             clearValue();
179         }
180         catch ( IOException e )
181         {
182             throw new CursorException( e );
183         }
184     }
185 
186 
187     /**
188      * {@inheritDoc}
189      */
190     public void beforeValue( K key, DupsContainer<V> value ) throws Exception
191     {
192         throw new UnsupportedOperationException( I18n.err( I18n.ERR_573 ) );
193     }
194 
195 
196     /**
197      * {@inheritDoc}
198      */
199     public void afterValue( K key, DupsContainer<V> value ) throws Exception
200     {
201         throw new UnsupportedOperationException( I18n.err( I18n.ERR_573 ) );
202     }
203 
204 
205     /**
206      * Positions this Cursor before the key of the supplied tuple.
207      *
208      * @param element the tuple who's key is used to position this Cursor
209      * @throws LdapException if there are failures to position the Cursor
210      * @throws CursorException if there are failures to position the Cursor
211      */
212     public void before( Tuple<K, DupsContainer<V>> element ) throws LdapException, CursorException
213     {
214         beforeKey( element.getKey() );
215     }
216 
217 
218     /**
219      * {@inheritDoc}
220      */
221     public void after( Tuple<K, DupsContainer<V>> element ) throws CursorException
222     {
223         afterKey( element.getKey() );
224     }
225 
226 
227     /**
228      * {@inheritDoc}
229      */
230     @SuppressWarnings("unchecked")
231     public void beforeFirst() throws LdapException, CursorException
232     {
233         checkNotClosed();
234         try
235         {
236             browser = table.getBTree().browse();
237             forwardDirection = null;
238             clearValue();
239         }
240         catch ( IOException e )
241         {
242             throw new CursorException( e );
243         }
244     }
245 
246 
247     /**
248      * {@inheritDoc}
249      */
250     @SuppressWarnings("unchecked")
251     public void afterLast() throws LdapException, CursorException
252     {
253         checkNotClosed();
254         try
255         {
256             browser = table.getBTree().browse( null );
257             forwardDirection = null;
258             clearValue();
259         }
260         catch ( IOException e )
261         {
262             throw new CursorException( e );
263         }
264     }
265 
266 
267     /**
268      * {@inheritDoc}
269      */
270     public boolean first() throws LdapException, CursorException
271     {
272         beforeFirst();
273 
274         return next();
275     }
276 
277 
278     /**
279      * {@inheritDoc}
280      */
281     public boolean last() throws LdapException, CursorException
282     {
283         afterLast();
284 
285         return previous();
286     }
287 
288 
289     /**
290      * {@inheritDoc}
291      */
292     public boolean previous() throws LdapException, CursorException
293     {
294         checkNotClosed();
295 
296         if ( browser == null )
297         {
298             afterLast();
299         }
300 
301         try
302         {
303             boolean advanceSuccess = browser.getPrevious( jdbmTuple );
304 
305             // only want to set this if the advance is a success which means we
306             // are not at front
307             if ( forwardDirection == null )
308             {
309                 if ( advanceSuccess )
310                 {
311                     forwardDirection = false;
312                 }
313                 else
314                 {
315                     clearValue();
316 
317                     return false;
318                 }
319             }
320             else if ( forwardDirection )
321             {
322                 advanceSuccess = browser.getPrevious( jdbmTuple );
323                 forwardDirection = false;
324             }
325 
326             valueAvailable = advanceSuccess;
327 
328             if ( valueAvailable )
329             {
330                 returnedTuple.setKey( jdbmTuple.getKey() );
331                 returnedTuple.setValue( table.getDupsContainer( ( byte[] ) jdbmTuple.getValue() ) );
332             }
333             else
334             {
335                 clearValue();
336             }
337 
338             return valueAvailable;
339         }
340         catch ( IOException e )
341         {
342             throw new CursorException( e );
343         }
344     }
345 
346 
347     /**
348      * {@inheritDoc}
349      */
350     public boolean next() throws LdapException, CursorException
351     {
352         checkNotClosed();
353 
354         if ( browser == null )
355         {
356             // The tuple browser is not initialized : set it to the beginning of the cursor
357             beforeFirst();
358         }
359 
360         try
361         {
362             // Check if we can move forward and grab a tuple
363             boolean advanceSuccess = browser.getNext( jdbmTuple );
364 
365             // only want to set this if the advance is a success which means
366             // we are not at end
367             if ( forwardDirection == null )
368             {
369                 if ( advanceSuccess )
370                 {
371                     forwardDirection = true;
372                 }
373                 else
374                 {
375                     clearValue();
376 
377                     // No value available
378                     return false;
379                 }
380             }
381 
382             if ( !forwardDirection )
383             {
384                 advanceSuccess = browser.getNext( jdbmTuple );
385                 forwardDirection = true;
386             }
387 
388             valueAvailable = advanceSuccess;
389 
390             if ( valueAvailable )
391             {
392                 // create the fetched tuple containing the key and the deserialized value
393                 returnedTuple.setKey( jdbmTuple.getKey() );
394                 returnedTuple.setValue( table.getDupsContainer( ( byte[] ) jdbmTuple.getValue() ) );
395             }
396             else
397             {
398                 clearValue();
399             }
400 
401             return valueAvailable;
402         }
403         catch ( IOException e )
404         {
405             throw new CursorException( e );
406         }
407     }
408 
409 
410     /**
411      * {@inheritDoc}
412      */
413     public Tuple<K, DupsContainer<V>> get() throws CursorException
414     {
415         checkNotClosed();
416 
417         if ( valueAvailable )
418         {
419             return returnedTuple;
420         }
421 
422         throw new InvalidCursorPositionException();
423     }
424 
425 
426     /**
427      * {@inheritDoc}
428      */
429     @Override
430     public void close() throws IOException
431     {
432         if ( IS_DEBUG )
433         {
434             LOG_CURSOR.debug( "Closing DupsContainerCursor {}", this );
435         }
436 
437         super.close();
438     }
439 
440 
441     /**
442      * {@inheritDoc}
443      */
444     @Override
445     public void close( Exception cause ) throws IOException
446     {
447         if ( IS_DEBUG )
448         {
449             LOG_CURSOR.debug( "Closing DupsContainerCursor {}", this );
450         }
451 
452         super.close( cause );
453     }
454 }