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.mavibot.btree;
21  
22  
23  import java.io.IOException;
24  import java.lang.reflect.Array;
25  import java.util.Comparator;
26  import java.util.Map;
27  import java.util.concurrent.ConcurrentHashMap;
28  import java.util.concurrent.ConcurrentLinkedQueue;
29  import java.util.concurrent.atomic.AtomicBoolean;
30  import java.util.concurrent.atomic.AtomicLong;
31  
32  import org.apache.directory.mavibot.btree.exception.BTreeCreationException;
33  import org.apache.directory.mavibot.btree.exception.DuplicateValueNotAllowedException;
34  import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
35  import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
36  
37  
38  /**
39   * A BTree abstract class containing the methods shared by the PersistedBTree or the InMemoryBTree
40   * implementations.
41   *
42   * @param <K> The Key type
43   * @param <V> The Value type
44   *
45   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
46   */
47  /* No qualifier*/abstract class AbstractBTree<K, V> implements BTree<K, V>
48  {
49      /** The read transaction timeout */
50      protected long readTimeOut = DEFAULT_READ_TIMEOUT;
51  
52      /** The current Header for a managed BTree */
53      protected BTreeHeader<K, V> currentBtreeHeader;
54  
55      /** The Key serializer used for this tree.*/
56      protected ElementSerializer<K> keySerializer;
57  
58      /** The Value serializer used for this tree. */
59      protected ElementSerializer<V> valueSerializer;
60  
61      /** The list of read transactions being executed */
62      protected ConcurrentLinkedQueue<ReadTransaction<K, V>> readTransactions;
63  
64      /** The size of the buffer used to write data in disk */
65      protected int writeBufferSize;
66  
67      /** Flag to enable duplicate key support */
68      protected boolean allowDuplicates;
69  
70      /** The number of elements in a page for this B-tree */
71      protected int pageSize;
72  
73      /** The BTree name */
74      protected String name;
75  
76      /** The FQCN of the Key serializer */
77      protected String keySerializerFQCN;
78  
79      /** The FQCN of the Value serializer */
80      protected String valueSerializerFQCN;
81  
82      /** The thread responsible for the cleanup of timed out reads */
83      protected Thread readTransactionsThread;
84  
85      /** The BTree type : either in-memory, disk backed or persisted */
86      protected BTreeTypeEnum btreeType;
87  
88      /** The current transaction */
89      protected AtomicBoolean transactionStarted = new AtomicBoolean( false );
90  
91      /** The map of all the used BtreeHeaders */
92      protected Map<Long, BTreeHeader<K, V>> btreeRevisions = new ConcurrentHashMap<Long, BTreeHeader<K, V>>();
93  
94      /** The current revision */
95      protected AtomicLong currentRevision = new AtomicLong( 0L );
96  
97      /** The TransactionManager used for this BTree */
98      protected TransactionManager transactionManager;
99  
100 
101     /**
102      * Starts a Read Only transaction. If the transaction is not closed, it will be
103      * automatically closed after the timeout
104      *
105      * @return The created transaction
106      */
107     protected abstract ReadTransaction<K, V> beginReadTransaction();
108 
109 
110     /**
111      * Starts a Read Only transaction. If the transaction is not closed, it will be
112      * automatically closed after the timeout
113      *
114      * @return The created transaction
115      */
116     protected abstract ReadTransaction<K, V> beginReadTransaction( long revision );
117 
118 
119     /**
120      * {@inheritDoc}
121      */
122     public TupleCursor<K, V> browse() throws IOException, KeyNotFoundException
123     {
124         // Check that we have a TransactionManager
125         if ( transactionManager == null )
126         {
127             throw new BTreeCreationException( "We don't have a transactionLManager" );
128         }
129 
130         ReadTransaction<K, V> transaction = beginReadTransaction();
131 
132         if ( transaction == null )
133         {
134             return new EmptyTupleCursor<K, V>( 0L );
135         }
136         else
137         {
138             ParentPos<K, V>[] stack = ( ParentPos<K, V>[] ) Array.newInstance( ParentPos.class, 32 );
139 
140             TupleCursor<K, V> cursor = getRootPage().browse( transaction, stack, 0 );
141 
142             // Set the position before the first element
143             cursor.beforeFirst();
144 
145             return cursor;
146         }
147     }
148 
149 
150     /**
151      * {@inheritDoc}
152      */
153     public TupleCursor<K, V> browse( long revision ) throws IOException, KeyNotFoundException
154     {
155         // Check that we have a TransactionManager
156         if ( transactionManager == null )
157         {
158             throw new BTreeCreationException( "We don't have a transactionLManager" );
159         }
160 
161         ReadTransaction<K, V> transaction = beginReadTransaction( revision );
162 
163         if ( transaction == null )
164         {
165             return new EmptyTupleCursor<K, V>( revision );
166         }
167         else
168         {
169             ParentPos<K, V>[] stack = ( ParentPos<K, V>[] ) Array.newInstance( ParentPos.class, 32 );
170 
171             // And get the cursor
172             TupleCursor<K, V> cursor = getRootPage( transaction.getRevision() ).browse( transaction, stack, 0 );
173 
174             return cursor;
175         }
176     }
177 
178 
179     /**
180      * {@inheritDoc}
181      */
182     public TupleCursor<K, V> browseFrom( K key ) throws IOException
183     {
184         // Check that we have a TransactionManager
185         if ( transactionManager == null )
186         {
187             throw new BTreeCreationException( "We don't have a transactionLManager" );
188         }
189 
190         ReadTransaction<K, V> transaction = beginReadTransaction();
191 
192         ParentPos<K, V>[] stack = ( ParentPos<K, V>[] ) Array.newInstance( ParentPos.class, 32 );
193 
194         TupleCursor<K, V> cursor;
195         try
196         {
197             cursor = getRootPage( transaction.getRevision() ).browse( key, transaction, stack, 0 );
198 
199             return cursor;
200         }
201         catch ( KeyNotFoundException e )
202         {
203             throw new IOException( e.getMessage() );
204         }
205     }
206 
207 
208     /**
209      * {@inheritDoc}
210      */
211     public TupleCursor<K, V> browseFrom( long revision, K key ) throws IOException, KeyNotFoundException
212     {
213         // Check that we have a TransactionManager
214         if ( transactionManager == null )
215         {
216             throw new BTreeCreationException( "We don't have a transactionLManager" );
217         }
218 
219         ReadTransaction<K, V> transaction = beginReadTransaction( revision );
220 
221         if ( transaction == null )
222         {
223             return new EmptyTupleCursor<K, V>( revision );
224         }
225         else
226         {
227             ParentPos<K, V>[] stack = ( ParentPos<K, V>[] ) Array.newInstance( ParentPos.class, 32 );
228 
229             // And get the cursor
230             TupleCursor<K, V> cursor = getRootPage( transaction.getRevision() ).browse( key, transaction, stack, 0 );
231 
232             return cursor;
233         }
234     }
235 
236 
237     /**
238      * {@inheritDoc}
239      */
240     public boolean contains( K key, V value ) throws IOException
241     {
242         // Check that we have a TransactionManager
243         if ( transactionManager == null )
244         {
245             throw new BTreeCreationException( "We don't have a transactionLManager" );
246         }
247 
248         ReadTransaction<K, V> transaction = beginReadTransaction();
249 
250         if ( transaction == null )
251         {
252             return false;
253         }
254         else
255         {
256             try
257             {
258                 return getRootPage( transaction.getRevision() ).contains( key, value );
259             }
260             catch ( KeyNotFoundException knfe )
261             {
262                 throw new IOException( knfe.getMessage() );
263             }
264             finally
265             {
266                 transaction.close();
267             }
268         }
269     }
270 
271 
272     /**
273      * {@inheritDoc}
274      */
275     public boolean contains( long revision, K key, V value ) throws IOException, KeyNotFoundException
276     {
277         // Check that we have a TransactionManager
278         if ( transactionManager == null )
279         {
280             throw new BTreeCreationException( "We don't have a transactionLManager" );
281         }
282 
283         // Fetch the root page for this revision
284         ReadTransaction<K, V> transaction = beginReadTransaction( revision );
285 
286         if ( transaction == null )
287         {
288             return false;
289         }
290         else
291         {
292             try
293             {
294                 return getRootPage( transaction.getRevision() ).contains( key, value );
295             }
296             finally
297             {
298                 transaction.close();
299             }
300         }
301     }
302 
303 
304     /**
305      * {@inheritDoc}
306      */
307     public Tuple<K, V> delete( K key ) throws IOException
308     {
309         // Check that we have a TransactionManager
310         if ( transactionManager == null )
311         {
312             throw new BTreeCreationException( "We don't have a transactionLManager" );
313         }
314 
315         if ( key == null )
316         {
317             throw new IllegalArgumentException( "Key must not be null" );
318         }
319 
320         // Take the lock if it's not already taken by another thread
321         transactionManager.beginTransaction();
322 
323         try
324         {
325             Tuple<K, V> deleted = delete( key, currentRevision.get() + 1 );
326 
327             // Commit now
328             transactionManager.commit();
329 
330             return deleted;
331         }
332         catch ( IOException ioe )
333         {
334             // We have had an exception, we must rollback the transaction
335             transactionManager.rollback();
336 
337             return null;
338         }
339     }
340 
341 
342     /**
343      * {@inheritDoc}
344      */
345     public Tuple<K, V> delete( K key, V value ) throws IOException
346     {
347         // Check that we have a TransactionManager
348         if ( transactionManager == null )
349         {
350             throw new BTreeCreationException( "We don't have a transactionLManager" );
351         }
352 
353         if ( key == null )
354         {
355             throw new IllegalArgumentException( "Key must not be null" );
356         }
357 
358         if ( value == null )
359         {
360             throw new IllegalArgumentException( "Value must not be null" );
361         }
362 
363         transactionManager.beginTransaction();
364 
365         try
366         {
367             Tuple<K, V> deleted = delete( key, value, currentRevision.get() + 1 );
368 
369             transactionManager.commit();
370 
371             return deleted;
372         }
373         catch ( IOException ioe )
374         {
375             transactionManager.rollback();
376 
377             throw ioe;
378         }
379     }
380 
381 
382     /**
383      * Delete the entry which key is given as a parameter. If the entry exists, it will
384      * be removed from the tree, the old tuple will be returned. Otherwise, null is returned.
385      *
386      * @param key The key for the entry we try to remove
387      * @return A Tuple<K, V> containing the removed entry, or null if it's not found.
388      */
389     /*no qualifier*/Tuple<K, V> delete( K key, long revision ) throws IOException
390     {
391         return delete( key, null, revision );
392     }
393 
394 
395     /*no qualifier*/abstract Tuple<K, V> delete( K key, V value, long revision ) throws IOException;
396 
397 
398     /**
399      * {@inheritDoc}
400      */
401     public V insert( K key, V value ) throws IOException
402     {
403         // Check that we have a TransactionManager
404         if ( transactionManager == null )
405         {
406             throw new BTreeCreationException( "We don't have a transactionLManager" );
407         }
408 
409         V existingValue = null;
410 
411         if ( key == null )
412         {
413             throw new IllegalArgumentException( "Key must not be null" );
414         }
415 
416         // Take the lock if it's not already taken by another thread and if we 
417         // aren't on a sub-btree
418         if ( btreeType != BTreeTypeEnum.PERSISTED_SUB )
419         {
420             transactionManager.beginTransaction();
421         }
422 
423         try
424         {
425             InsertResult<K, V> result = insert( key, value, -1L );
426 
427             if ( result instanceof ExistsResult )
428             {
429                 existingValue = value;
430             }
431             else if ( result instanceof ModifyResult )
432             {
433                 existingValue = ( ( ModifyResult<K, V> ) result ).getModifiedValue();
434             }
435 
436             // Commit now if it's not a sub-btree
437             if ( btreeType != BTreeTypeEnum.PERSISTED_SUB )
438             {
439                 //FIXME when result type is ExistsResult then we should avoid writing the headers
440                 transactionManager.commit();
441             }
442 
443             return existingValue;
444         }
445         catch ( IOException ioe )
446         {
447             // We have had an exception, we must rollback the transaction
448             // if it's not a sub-btree
449             if ( btreeType != BTreeTypeEnum.PERSISTED_SUB )
450             {
451                 transactionManager.rollback();
452             }
453 
454             return null;
455         }
456         catch ( DuplicateValueNotAllowedException e )
457         {
458             // We have had an exception, we must rollback the transaction
459             // if it's not a sub-btree
460             if ( btreeType != BTreeTypeEnum.PERSISTED_SUB )
461             {
462                 transactionManager.rollback();
463             }
464 
465             throw e;
466         }
467     }
468 
469 
470     /**
471      * {@inheritDoc}
472      */
473     /* no qualifier */abstract InsertResult<K, V> insert( K key, V value, long revision ) throws IOException;
474 
475 
476     /**
477      * Flush the latest revision to disk. We will replace the current file by the new one, as
478      * we flush in a temporary file.
479      */
480     public void flush() throws IOException
481     {
482     }
483 
484 
485     /**
486      * {@inheritDoc}
487      */
488     public V get( K key ) throws IOException, KeyNotFoundException
489     {
490         // Check that we have a TransactionManager
491         if ( transactionManager == null )
492         {
493             throw new BTreeCreationException( "We don't have a transactionLManager" );
494         }
495 
496         ReadTransaction<K, V> transaction = beginReadTransaction();
497 
498         if ( transaction == null )
499         {
500             return null;
501         }
502         else
503         {
504             try
505             {
506                 return getRootPage( transaction.getRevision() ).get( key );
507             }
508             finally
509             {
510                 transaction.close();
511             }
512         }
513     }
514 
515 
516     /**
517      * {@inheritDoc}
518      */
519     public V get( long revision, K key ) throws IOException, KeyNotFoundException
520     {
521         // Check that we have a TransactionManager
522         if ( transactionManager == null )
523         {
524             throw new BTreeCreationException( "We don't have a transactionLManager" );
525         }
526 
527         ReadTransaction<K, V> transaction = beginReadTransaction( revision );
528 
529         if ( transaction == null )
530         {
531             return null;
532         }
533         else
534         {
535             try
536             {
537                 return getRootPage( transaction.getRevision() ).get( key );
538             }
539             finally
540             {
541                 transaction.close();
542             }
543         }
544     }
545 
546 
547     /**
548      * {@inheritDoc}
549      */
550     public abstract Page<K, V> getRootPage();
551 
552 
553     /**
554      * {@inheritDoc}
555      */
556     /* no qualifier */abstract void setRootPage( Page<K, V> root );
557 
558 
559     /**
560      * {@inheritDoc}
561      */
562     public ValueCursor<V> getValues( K key ) throws IOException, KeyNotFoundException
563     {
564         // Check that we have a TransactionManager
565         if ( transactionManager == null )
566         {
567             throw new BTreeCreationException( "We don't have a transactionLManager" );
568         }
569 
570         ReadTransaction<K, V> transaction = beginReadTransaction();
571 
572         if ( transaction == null )
573         {
574             return new EmptyValueCursor<V>( 0L );
575         }
576         else
577         {
578             try
579             {
580                 return getRootPage( transaction.getRevision() ).getValues( key );
581             }
582             finally
583             {
584                 transaction.close();
585             }
586         }
587     }
588 
589 
590     /**
591      * {@inheritDoc}
592      */
593     public boolean hasKey( K key ) throws IOException, KeyNotFoundException
594     {
595         // Check that we have a TransactionManager
596         if ( transactionManager == null )
597         {
598             throw new BTreeCreationException( "We don't have a transactionLManager" );
599         }
600 
601         if ( key == null )
602         {
603             return false;
604         }
605 
606         ReadTransaction<K, V> transaction = beginReadTransaction();
607 
608         if ( transaction == null )
609         {
610             return false;
611         }
612         else
613         {
614             try
615             {
616                 return getRootPage( transaction.getRevision() ).hasKey( key );
617             }
618             finally
619             {
620                 transaction.close();
621             }
622         }
623     }
624 
625 
626     /**
627      * {@inheritDoc}
628      */
629     public boolean hasKey( long revision, K key ) throws IOException, KeyNotFoundException
630     {
631         // Check that we have a TransactionManager
632         if ( transactionManager == null )
633         {
634             throw new BTreeCreationException( "We don't have a transactionLManager" );
635         }
636 
637         if ( key == null )
638         {
639             return false;
640         }
641 
642         ReadTransaction<K, V> transaction = beginReadTransaction( revision );
643 
644         if ( transaction == null )
645         {
646             return false;
647         }
648         else
649         {
650             try
651             {
652                 return getRootPage( transaction.getRevision() ).hasKey( key );
653             }
654             finally
655             {
656                 transaction.close();
657             }
658         }
659     }
660 
661 
662     /**
663      * {@inheritDoc}
664      */
665     public ElementSerializer<K> getKeySerializer()
666     {
667         return keySerializer;
668     }
669 
670 
671     /**
672      * {@inheritDoc}
673      */
674     public void setKeySerializer( ElementSerializer<K> keySerializer )
675     {
676         this.keySerializer = keySerializer;
677         keySerializerFQCN = keySerializer.getClass().getName();
678     }
679 
680 
681     /**
682      * {@inheritDoc}
683      */
684     public String getKeySerializerFQCN()
685     {
686         return keySerializerFQCN;
687     }
688 
689 
690     /**
691      * {@inheritDoc}
692      */
693     public ElementSerializer<V> getValueSerializer()
694     {
695         return valueSerializer;
696     }
697 
698 
699     /**
700      * {@inheritDoc}
701      */
702     public void setValueSerializer( ElementSerializer<V> valueSerializer )
703     {
704         this.valueSerializer = valueSerializer;
705         valueSerializerFQCN = valueSerializer.getClass().getName();
706     }
707 
708 
709     /**
710      * {@inheritDoc}
711      */
712     public String getValueSerializerFQCN()
713     {
714         return valueSerializerFQCN;
715     }
716 
717 
718     /**
719      * {@inheritDoc}
720      */
721     public long getRevision()
722     {
723         // Check that we have a TransactionManager
724         if ( transactionManager == null )
725         {
726             throw new BTreeCreationException( "We don't have a transactionLManager" );
727         }
728 
729         ReadTransaction<K, V> transaction = beginReadTransaction();
730 
731         if ( transaction == null )
732         {
733             return -1L;
734         }
735         else
736         {
737             try
738             {
739                 return transaction.getRevision();
740             }
741             finally
742             {
743                 transaction.close();
744             }
745         }
746     }
747 
748 
749     /**
750      * {@inheritDoc}
751      */
752     /* no qualifier */void setRevision( long revision )
753     {
754         transactionManager.getBTreeHeader( getName() ).setRevision( revision );
755     }
756 
757 
758     /**
759      * Store the new revision in the map of btrees, increment the current revision
760      */
761     protected void storeRevision( BTreeHeader<K, V> btreeHeader, boolean keepRevisions )
762     {
763         long revision = btreeHeader.getRevision();
764 
765         if ( keepRevisions )
766         {
767             synchronized ( btreeRevisions )
768             {
769                 btreeRevisions.put( revision, btreeHeader );
770             }
771         }
772 
773         currentRevision.set( revision );
774         currentBtreeHeader = btreeHeader;
775 
776         // And update the newBTreeHeaders map
777         if ( btreeHeader.getBtree().getType() != BTreeTypeEnum.PERSISTED_SUB )
778         {
779             transactionManager.updateNewBTreeHeaders( btreeHeader );
780         }
781     }
782 
783 
784     /**
785      * Store the new revision in the map of btrees, increment the current revision
786      */
787     protected void storeRevision( BTreeHeader<K, V> btreeHeader )
788     {
789         long revision = btreeHeader.getRevision();
790 
791         synchronized ( btreeRevisions )
792         {
793             btreeRevisions.put( revision, btreeHeader );
794         }
795 
796         currentRevision.set( revision );
797         currentBtreeHeader = btreeHeader;
798 
799         // And update the newBTreeHeaders map
800         if ( btreeHeader.getBtree().getType() != BTreeTypeEnum.PERSISTED_SUB )
801         {
802             transactionManager.updateNewBTreeHeaders( btreeHeader );
803         }
804     }
805 
806 
807     /**
808      * {@inheritDoc}
809      */
810     public long getReadTimeOut()
811     {
812         return readTimeOut;
813     }
814 
815 
816     /**
817      * {@inheritDoc}
818      */
819     public void setReadTimeOut( long readTimeOut )
820     {
821         this.readTimeOut = readTimeOut;
822     }
823 
824 
825     /**
826      * {@inheritDoc}
827      */
828     public long getNbElems()
829     {
830         // Check that we have a TransactionManager
831         if ( transactionManager == null )
832         {
833             throw new BTreeCreationException( "We don't have a transactionLManager" );
834         }
835 
836         ReadTransaction<K, V> transaction = beginReadTransaction();
837 
838         if ( transaction == null )
839         {
840             return -1L;
841         }
842         else
843         {
844             try
845             {
846                 return transaction.getBtreeHeader().getNbElems();
847             }
848             finally
849             {
850                 transaction.close();
851             }
852         }
853     }
854 
855 
856     /**
857      * {@inheritDoc}
858      */
859     /* no qualifier */void setNbElems( long nbElems )
860     {
861         transactionManager.getBTreeHeader( getName() ).setNbElems( nbElems );
862     }
863 
864 
865     /**
866      * {@inheritDoc}
867      */
868     public int getPageSize()
869     {
870         return pageSize;
871     }
872 
873 
874     /**
875      * {@inheritDoc}
876      */
877     public void setPageSize( int pageSize )
878     {
879         if ( pageSize <= 2 )
880         {
881             this.pageSize = DEFAULT_PAGE_SIZE;
882         }
883         else
884         {
885             this.pageSize = getPowerOf2( pageSize );
886         }
887     }
888 
889 
890     /**
891      * {@inheritDoc}
892      */
893     public String getName()
894     {
895         return name;
896     }
897 
898 
899     /**
900      * {@inheritDoc}
901      */
902     public void setName( String name )
903     {
904         this.name = name;
905     }
906 
907 
908     /**
909      * {@inheritDoc}
910      */
911     public Comparator<K> getKeyComparator()
912     {
913         return keySerializer.getComparator();
914     }
915 
916 
917     /**
918      * {@inheritDoc}
919      */
920     public Comparator<V> getValueComparator()
921     {
922         return valueSerializer.getComparator();
923     }
924 
925 
926     /**
927      * {@inheritDoc}
928      */
929     public int getWriteBufferSize()
930     {
931         return writeBufferSize;
932     }
933 
934 
935     /**
936      * {@inheritDoc}
937      */
938     public void setWriteBufferSize( int writeBufferSize )
939     {
940         this.writeBufferSize = writeBufferSize;
941     }
942 
943 
944     /**
945      * {@inheritDoc}
946      */
947     public boolean isAllowDuplicates()
948     {
949         return allowDuplicates;
950     }
951 
952 
953     /**
954      * {@inheritDoc}
955      */
956     public void setAllowDuplicates( boolean allowDuplicates )
957     {
958         this.allowDuplicates = allowDuplicates;
959     }
960 
961 
962     /**
963      * {@inheritDoc}
964      */
965     public BTreeTypeEnum getType()
966     {
967         return btreeType;
968     }
969 
970 
971     /**
972      * @param type the type to set
973      */
974     public void setType( BTreeTypeEnum type )
975     {
976         this.btreeType = type;
977     }
978 
979 
980     /**
981      * Gets the number which is a power of 2 immediately above the given positive number.
982      */
983     private int getPowerOf2( int size )
984     {
985         int newSize = --size;
986         newSize |= newSize >> 1;
987         newSize |= newSize >> 2;
988         newSize |= newSize >> 4;
989         newSize |= newSize >> 8;
990         newSize |= newSize >> 16;
991         newSize++;
992 
993         return newSize;
994     }
995 
996 
997     /**
998      * @return The current BtreeHeader
999      */
1000     protected BTreeHeader<K, V> getBtreeHeader()
1001     {
1002         return currentBtreeHeader;
1003     }
1004 
1005 
1006     /**
1007      * @return The current BtreeHeader
1008      */
1009     protected BTreeHeader<K, V> getBtreeHeader( long revision )
1010     {
1011         return btreeRevisions.get( revision );
1012     }
1013 
1014 
1015     /**
1016      * {@inheritDoc}
1017      */
1018     public KeyCursor<K> browseKeys() throws IOException, KeyNotFoundException
1019     {
1020         // Check that we have a TransactionManager
1021         if ( transactionManager == null )
1022         {
1023             throw new BTreeCreationException( "We don't have a Transaction Manager" );
1024         }
1025 
1026         ReadTransaction transaction = beginReadTransaction();
1027 
1028         if ( transaction == null )
1029         {
1030             return new KeyCursor<K>();
1031         }
1032         else
1033         {
1034             ParentPos<K, K>[] stack = ( ParentPos<K, K>[] ) Array.newInstance( ParentPos.class, 32 );
1035 
1036             KeyCursor<K> cursor = getRootPage().browseKeys( transaction, stack, 0 );
1037 
1038             // Set the position before the first element
1039             cursor.beforeFirst();
1040 
1041             return cursor;
1042         }
1043     }
1044 
1045 
1046     /**
1047      * Create a thread that is responsible of cleaning the transactions when
1048      * they hit the timeout
1049      */
1050     /*no qualifier*/void createTransactionManager()
1051     {
1052         Runnable readTransactionTask = new Runnable()
1053         {
1054             public void run()
1055             {
1056                 try
1057                 {
1058                     ReadTransaction<K, V> transaction = null;
1059 
1060                     while ( !Thread.currentThread().isInterrupted() )
1061                     {
1062                         long timeoutDate = System.currentTimeMillis() - readTimeOut;
1063                         long t0 = System.currentTimeMillis();
1064                         int nbTxns = 0;
1065 
1066                         // Loop on all the transactions from the queue
1067                         while ( ( transaction = readTransactions.peek() ) != null )
1068                         {
1069                             nbTxns++;
1070 
1071                             if ( transaction.isClosed() )
1072                             {
1073                                 // The transaction is already closed, remove it from the queue
1074                                 readTransactions.poll();
1075                                 continue;
1076                             }
1077 
1078                             // Check if the transaction has timed out
1079                             if ( transaction.getCreationDate() < timeoutDate )
1080                             {
1081                                 transaction.close();
1082                                 readTransactions.poll();
1083 
1084                                 synchronized ( btreeRevisions )
1085                                 {
1086                                     btreeRevisions.remove( transaction.getRevision() );
1087                                 }
1088 
1089                                 continue;
1090                             }
1091 
1092                             // We need to stop now
1093                             break;
1094                         }
1095 
1096                         long t1 = System.currentTimeMillis();
1097 
1098                         if ( nbTxns > 0 )
1099                         {
1100                             System.out.println( "Processing old txn : " + nbTxns + ", " + ( t1 - t0 ) + "ms" );
1101                         }
1102 
1103                         // Wait until we reach the timeout
1104                         Thread.sleep( readTimeOut );
1105                     }
1106                 }
1107                 catch ( InterruptedException ie )
1108                 {
1109                     //System.out.println( "Interrupted" );
1110                 }
1111                 catch ( Exception e )
1112                 {
1113                     throw new RuntimeException( e );
1114                 }
1115             }
1116         };
1117 
1118         readTransactionsThread = new Thread( readTransactionTask );
1119         readTransactionsThread.setDaemon( true );
1120         readTransactionsThread.start();
1121     }
1122 }