1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.core.partition.impl.btree.jdbm;
21
22
23 import java.io.IOException;
24 import java.util.Comparator;
25 import java.util.Map;
26
27 import jdbm.RecordManager;
28 import jdbm.btree.BTree;
29 import jdbm.helper.Serializer;
30 import jdbm.helper.Tuple;
31 import jdbm.helper.TupleBrowser;
32
33 import org.apache.directory.api.ldap.model.cursor.Cursor;
34 import org.apache.directory.api.ldap.model.cursor.CursorException;
35 import org.apache.directory.api.ldap.model.cursor.EmptyCursor;
36 import org.apache.directory.api.ldap.model.cursor.SingletonCursor;
37 import org.apache.directory.api.ldap.model.exception.LdapException;
38 import org.apache.directory.api.ldap.model.exception.LdapOtherException;
39 import org.apache.directory.api.ldap.model.schema.SchemaManager;
40 import org.apache.directory.api.ldap.model.schema.comparators.SerializableComparator;
41 import org.apache.directory.api.util.Strings;
42 import org.apache.directory.api.util.SynchronizedLRUMap;
43 import org.apache.directory.server.core.api.partition.PartitionTxn;
44 import org.apache.directory.server.core.avltree.ArrayMarshaller;
45 import org.apache.directory.server.core.avltree.ArrayTree;
46 import org.apache.directory.server.core.avltree.ArrayTreeCursor;
47 import org.apache.directory.server.core.avltree.Marshaller;
48 import org.apache.directory.server.i18n.I18n;
49 import org.apache.directory.server.xdbm.AbstractTable;
50 import org.apache.directory.server.xdbm.KeyTupleArrayCursor;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54
55
56
57
58
59
60 public class JdbmTable<K, V> extends AbstractTable<K, V>
61 {
62
63 private static final Logger LOG = LoggerFactory.getLogger( JdbmTable.class );
64
65
66 private final RecordManager recMan;
67
68
69 private BTree<K, V> bt;
70
71
72 private int numDupLimit = JdbmIndex.DEFAULT_DUPLICATE_LIMIT;
73
74
75 private final Map<Long, BTree<K, V>> duplicateBtrees;
76
77
78 private final Serializer valueSerializer;
79
80
81 Marshaller<ArrayTree<V>> marshaller;
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104 @SuppressWarnings("unchecked")
105 public JdbmTable( SchemaManager schemaManager, String name, int numDupLimit, RecordManager manager,
106 Comparator<K> keyComparator, Comparator<V> valueComparator,
107 Serializer keySerializer, Serializer valueSerializer )
108 throws IOException
109 {
110 super( schemaManager, name, keyComparator, valueComparator );
111
112 if ( valueComparator == null )
113 {
114 throw new IllegalArgumentException( I18n.err( I18n.ERR_592 ) );
115 }
116
117
118 duplicateBtrees = new SynchronizedLRUMap( 100 );
119
120 if ( valueSerializer != null )
121 {
122 marshaller = new ArrayMarshaller<>( valueComparator,
123 new MarshallerSerializerBridge<V>( valueSerializer ) );
124 }
125 else
126 {
127 marshaller = new ArrayMarshaller<>( valueComparator );
128 }
129
130 this.numDupLimit = numDupLimit;
131 this.recMan = manager;
132 this.valueSerializer = valueSerializer;
133 this.allowsDuplicates = true;
134 long recId = recMan.getNamedObject( name );
135
136 if ( recId == 0 )
137 {
138
139
140
141
142
143 bt = new BTree<>( recMan, keyComparator, keySerializer, null );
144 recId = bt.getRecordId();
145 recMan.setNamedObject( name, recId );
146 }
147 else
148
149 {
150 bt = new BTree<K, V>().load( recMan, recId );
151 ( ( SerializableComparator<K> ) bt.getComparator() ).setSchemaManager( schemaManager );
152
153 count = bt.size();
154 }
155 }
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172 public JdbmTable( SchemaManager schemaManager, String name, RecordManager manager, Comparator<K> keyComparator,
173 Serializer keySerializer, Serializer valueSerializer )
174 throws IOException
175 {
176 super( schemaManager, name, keyComparator, null );
177
178 this.duplicateBtrees = null;
179 this.numDupLimit = Integer.MAX_VALUE;
180 this.recMan = manager;
181
182 this.valueSerializer = valueSerializer;
183
184 this.allowsDuplicates = false;
185
186 long recId = recMan.getNamedObject( name );
187
188 if ( recId != 0 )
189 {
190 bt = new BTree<K, V>().load( recMan, recId );
191 ( ( SerializableComparator<K> ) bt.getComparator() ).setSchemaManager( schemaManager );
192 bt.setValueSerializer( valueSerializer );
193
194 count = bt.size();
195 }
196 else
197 {
198 bt = new BTree<>( recMan, keyComparator, keySerializer, valueSerializer );
199 recId = bt.getRecordId();
200 recMan.setNamedObject( name, recId );
201 }
202 }
203
204
205
206
207
208
209
210
211 @Override
212 public long count( PartitionTxn transaction, K key ) throws LdapException
213 {
214 if ( key == null )
215 {
216 return 0L;
217 }
218
219 try
220 {
221 if ( !allowsDuplicates )
222 {
223 if ( null == bt.find( key ) )
224 {
225 return 0L;
226 }
227 else
228 {
229 return 1L;
230 }
231 }
232
233 DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
234
235 if ( values.isArrayTree() )
236 {
237 return values.getArrayTree().size();
238 }
239
240 return getBTree( values.getBTreeRedirect() ).size();
241 }
242 catch ( IOException ioe )
243 {
244 throw new LdapOtherException( ioe.getMessage() );
245 }
246 }
247
248
249
250
251
252
253
254
255
256 @SuppressWarnings("unchecked")
257 @Override
258 public V get( PartitionTxn transaction, K key ) throws LdapException
259 {
260 if ( key == null )
261 {
262 return null;
263 }
264
265 try
266 {
267 if ( !allowsDuplicates )
268 {
269 return bt.find( key );
270 }
271
272 DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
273
274 if ( values.isArrayTree() )
275 {
276 ArrayTree<V> set = values.getArrayTree();
277
278 if ( set.getFirst() == null )
279 {
280 return null;
281 }
282
283 return set.getFirst();
284 }
285
286
287 BTree tree = getBTree( values.getBTreeRedirect() );
288
289 jdbm.helper.Tuple tuple = new jdbm.helper.Tuple();
290 TupleBrowser<K, V> browser = tree.browse();
291 browser.getNext( tuple );
292
293 return ( V ) tuple.getKey();
294 }
295 catch ( IOException ioe )
296 {
297 throw new LdapOtherException( ioe.getMessage() );
298 }
299
300 }
301
302
303
304
305
306 @Override
307 public boolean hasGreaterOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
308 {
309 if ( key == null )
310 {
311 return false;
312 }
313
314 if ( !allowsDuplicates )
315 {
316 throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) );
317 }
318
319 try
320 {
321 DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
322
323 if ( values.isArrayTree() )
324 {
325 ArrayTree<V> set = values.getArrayTree();
326 V result = set.findGreaterOrEqual( val );
327 return result != null;
328 }
329
330
331 BTree<K, V> tree = getBTree( values.getBTreeRedirect() );
332
333 return tree.size() != 0 && btreeHas( tree, val, true );
334 }
335 catch ( IOException ioe )
336 {
337 throw new LdapOtherException( ioe.getMessage() );
338 }
339 }
340
341
342
343
344
345 @Override
346 public boolean hasLessOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException
347 {
348 if ( key == null )
349 {
350 return false;
351 }
352
353 if ( !allowsDuplicates )
354 {
355 throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) );
356 }
357
358 try
359 {
360 DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
361
362 if ( values.isArrayTree() )
363 {
364 ArrayTree<V> set = values.getArrayTree();
365 V result = set.findLessOrEqual( val );
366 return result != null;
367 }
368
369
370 BTree<K, V> tree = getBTree( values.getBTreeRedirect() );
371
372 return tree.size() != 0 && btreeHas( tree, val, false );
373 }
374 catch ( IOException ioe )
375 {
376 throw new LdapOtherException( ioe.getMessage() );
377 }
378 }
379
380
381
382
383
384 @Override
385 @SuppressWarnings("unchecked")
386 public boolean hasGreaterOrEqual( PartitionTxn transaction, K key ) throws LdapException
387 {
388 try
389 {
390
391
392 jdbm.helper.Tuple tuple = bt.findGreaterOrEqual( key );
393
394
395 if ( null != tuple && keyComparator.compare( ( K ) tuple.getKey(), key ) == 0 )
396 {
397 return true;
398 }
399
400
401
402 return ( null != tuple );
403 }
404 catch ( IOException ioe )
405 {
406 throw new LdapOtherException( ioe.getMessage() );
407 }
408 }
409
410
411
412
413
414 @SuppressWarnings("unchecked")
415 @Override
416 public boolean hasLessOrEqual( PartitionTxn transaction, K key ) throws LdapException
417 {
418 try
419 {
420
421
422 Tuple<K, V> tuple = bt.findGreaterOrEqual( key );
423
424
425 if ( null != tuple && keyComparator.compare( tuple.getKey(), key ) == 0 )
426 {
427 return true;
428 }
429
430 if ( null == tuple )
431 {
432
433
434
435
436
437
438 return count > 0;
439 }
440 else
441 {
442
443
444
445
446
447
448
449 TupleBrowser browser = bt.browse( tuple.getKey() );
450
451 if ( browser.getPrevious( tuple ) )
452 {
453 return true;
454 }
455 }
456
457 return false;
458 }
459 catch ( IOException ioe )
460 {
461 throw new LdapOtherException( ioe.getMessage() );
462 }
463 }
464
465
466
467
468
469 @SuppressWarnings("unchecked")
470 public boolean has( PartitionTxn transaction, K key, V value ) throws LdapException
471 {
472 if ( key == null )
473 {
474 return false;
475 }
476
477 try
478 {
479 if ( !allowsDuplicates )
480 {
481 V stored = bt.find( key );
482 return null != stored && stored.equals( value );
483 }
484
485 DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
486
487 if ( values.isArrayTree() )
488 {
489 return values.getArrayTree().find( value ) != null;
490 }
491
492 return getBTree( values.getBTreeRedirect() ).find( value ) != null;
493 }
494 catch ( IOException ioe )
495 {
496 throw new LdapOtherException( ioe.getMessage() );
497 }
498 }
499
500
501
502
503
504 @Override
505 public boolean has( PartitionTxn transaction, K key ) throws LdapException
506 {
507 try
508 {
509 return key != null && bt.find( key ) != null;
510 }
511 catch ( IOException ioe )
512 {
513 throw new LdapException( ioe );
514 }
515 }
516
517
518
519
520
521 @Override
522 @SuppressWarnings("unchecked")
523 public synchronized void put( PartitionTxn transaction, K key, V value ) throws LdapException
524 {
525 try
526 {
527 if ( LOG.isDebugEnabled() )
528 {
529 LOG.debug( "---> Add {} = {}", name, key );
530 }
531
532 if ( ( value == null ) || ( key == null ) )
533 {
534 throw new IllegalArgumentException( I18n.err( I18n.ERR_594 ) );
535 }
536
537 V replaced;
538
539 if ( !allowsDuplicates )
540 {
541 replaced = ( V ) bt.insert( key, value, true );
542
543 if ( null == replaced )
544 {
545 count++;
546 }
547
548 if ( LOG.isDebugEnabled() )
549 {
550 LOG.debug( "<--- Add ONE {} = {}", name, key );
551 }
552
553 return;
554 }
555
556 DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
557
558 if ( values.isArrayTree() )
559 {
560 ArrayTree<V> set = values.getArrayTree();
561 replaced = set.insert( value );
562
563 if ( replaced != null )
564 {
565 return;
566 }
567
568 if ( set.size() > numDupLimit )
569 {
570 BTree tree = convertToBTree( set );
571 BTreeRedirecterver/core/partition/impl/btree/jdbm/BTreeRedirect.html#BTreeRedirect">BTreeRedirect redirect = new BTreeRedirect( tree.getRecordId() );
572 bt.insert( key, ( V ) BTreeRedirectMarshaller.INSTANCE.serialize( redirect ), true );
573
574 if ( LOG.isDebugEnabled() )
575 {
576 LOG.debug( "<--- Add new BTREE {} = {}", name, key );
577 }
578 }
579 else
580 {
581 bt.insert( key, ( V ) marshaller.serialize( set ), true );
582
583 if ( LOG.isDebugEnabled() )
584 {
585 LOG.debug( "<--- Add AVL {} = {}", name, key );
586 }
587 }
588
589 count++;
590
591 return;
592 }
593
594 BTree tree = getBTree( values.getBTreeRedirect() );
595 replaced = ( V ) tree.insert( value, Strings.EMPTY_BYTES, true );
596
597 if ( replaced == null )
598 {
599 count++;
600 }
601
602 if ( LOG.isDebugEnabled() )
603 {
604 LOG.debug( "<--- Add BTREE {} = {}", name, key );
605 }
606 }
607 catch ( IOException | CursorException | LdapException e )
608 {
609 LOG.error( I18n.err( I18n.ERR_131, key, name ), e );
610 throw new LdapOtherException( e.getMessage(), e );
611 }
612 }
613
614
615
616
617
618 @SuppressWarnings("unchecked")
619 @Override
620 public synchronized void remove( PartitionTxn transaction, K key, V value ) throws LdapException
621 {
622 try
623 {
624 if ( LOG.isDebugEnabled() )
625 {
626 LOG.debug( "---> Remove {} = {}, {}", name, key, value );
627 }
628
629 if ( key == null )
630 {
631 if ( LOG.isDebugEnabled() )
632 {
633 LOG.debug( "<--- Remove NULL key {}", name );
634 }
635
636 return;
637 }
638
639 if ( !allowsDuplicates )
640 {
641 V oldValue = bt.find( key );
642
643
644 if ( ( oldValue != null ) && oldValue.equals( value ) )
645 {
646 bt.remove( key );
647 count--;
648
649 if ( LOG.isDebugEnabled() )
650 {
651 LOG.debug( "<--- Remove ONE {} = {}, {}", name, key, value );
652 }
653 }
654
655 return;
656 }
657
658 DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
659
660 if ( values.isArrayTree() )
661 {
662 ArrayTree<V> set = values.getArrayTree();
663
664
665 if ( set.remove( value ) != null )
666 {
667 if ( set.isEmpty() )
668 {
669 bt.remove( key );
670 }
671 else
672 {
673 bt.insert( key, ( V ) marshaller.serialize( set ), true );
674 }
675
676 count--;
677
678 if ( LOG.isDebugEnabled() )
679 {
680 LOG.debug( "<--- Remove AVL {} = {}, {}", name, key, value );
681 }
682 }
683
684 return;
685 }
686
687
688 BTree tree = getBTree( values.getBTreeRedirect() );
689
690 if ( tree.find( value ) != null && tree.remove( value ) != null )
691 {
692
693
694
695
696 if ( tree.size() <= numDupLimit )
697 {
698 ArrayTree<V> avlTree = convertToArrayTree( tree );
699 bt.insert( key, ( V ) marshaller.serialize( avlTree ), true );
700 recMan.delete( tree.getRecordId() );
701 }
702
703 count--;
704
705 if ( LOG.isDebugEnabled() )
706 {
707 LOG.debug( "<--- Remove BTREE {} = {}, {}", name, key, value );
708 }
709 }
710 }
711 catch ( Exception e )
712 {
713 LOG.error( I18n.err( I18n.ERR_132, key, value, name ), e );
714 }
715 }
716
717
718
719
720
721 @Override
722 public synchronized void remove( PartitionTxn transaction, K key ) throws LdapException
723 {
724 try
725 {
726 if ( LOG.isDebugEnabled() )
727 {
728 LOG.debug( "---> Remove {} = {}", name, key );
729 }
730
731 if ( key == null )
732 {
733 return;
734 }
735
736 Object returned = bt.remove( key );
737
738 if ( null == returned )
739 {
740 if ( LOG.isDebugEnabled() )
741 {
742 LOG.debug( "<--- Remove AVL {} = {} (not found)", name, key );
743 }
744
745 return;
746 }
747
748 if ( !allowsDuplicates )
749 {
750 this.count--;
751
752 if ( LOG.isDebugEnabled() )
753 {
754 LOG.debug( "<--- Remove ONE {} = {}", name, key );
755 }
756
757 return;
758 }
759
760 byte[] serialized = ( byte[] ) returned;
761
762 if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
763 {
764 BTree tree = getBTree( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
765 this.count -= tree.size();
766
767 if ( LOG.isDebugEnabled() )
768 {
769 LOG.debug( "<--- Remove BTree {} = {}", name, key );
770 }
771
772 recMan.delete( tree.getRecordId() );
773 duplicateBtrees.remove( tree.getRecordId() );
774 }
775 else
776 {
777 ArrayTree<V> set = marshaller.deserialize( serialized );
778 this.count -= set.size();
779
780 if ( LOG.isDebugEnabled() )
781 {
782 LOG.debug( "<--- Remove AVL {} = {}", name, key );
783 }
784 }
785 }
786 catch ( Exception e )
787 {
788 LOG.error( I18n.err( I18n.ERR_133, key, name ), e );
789 }
790 }
791
792
793
794
795
796 @Override
797 public Cursor<org.apache.directory.api.ldap.model.cursor.Tuple<K, V>> cursor()
798 {
799 if ( allowsDuplicates )
800 {
801 return new DupsCursor<>( this );
802 }
803
804 return new NoDupsCursor<>( this );
805 }
806
807
808
809
810
811 @Override
812 public Cursor<org.apache.directory.api.ldap.model.cursor.Tuple<K, V>> cursor( PartitionTxn partitionTxn, K key ) throws LdapException
813 {
814 if ( key == null )
815 {
816 return new EmptyCursor<>();
817 }
818
819 try
820 {
821 V raw = bt.find( key );
822
823 if ( null == raw )
824 {
825 return new EmptyCursor<>();
826 }
827
828 if ( !allowsDuplicates )
829 {
830 return new SingletonCursor<>(
831 new org.apache.directory.api.ldap.model.cursor.Tuple<K, V>( key, raw ) );
832 }
833
834 byte[] serialized = ( byte[] ) raw;
835
836 if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
837 {
838 BTree tree = getBTree( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
839 return new KeyTupleBTreeCursor<>( tree, key, valueComparator );
840 }
841
842 ArrayTree<V> set = marshaller.deserialize( serialized );
843
844 return new KeyTupleArrayCursor<>( set, key );
845 }
846 catch ( IOException ioe )
847 {
848 throw new LdapOtherException( ioe.getMessage(), ioe );
849 }
850 }
851
852
853
854
855
856 @SuppressWarnings("unchecked")
857 @Override
858 public Cursor<V> valueCursor( PartitionTxn transaction, K key ) throws LdapException
859 {
860 if ( key == null )
861 {
862 return new EmptyCursor<>();
863 }
864
865 try
866 {
867 V raw = bt.find( key );
868
869 if ( null == raw )
870 {
871 return new EmptyCursor<>();
872 }
873
874 if ( !allowsDuplicates )
875 {
876 return new SingletonCursor<>( raw );
877 }
878
879 byte[] serialized = ( byte[] ) raw;
880
881 if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
882 {
883 BTree tree = getBTree( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
884 return new KeyBTreeCursor<>( tree, valueComparator );
885 }
886
887 return new ArrayTreeCursor<>( marshaller.deserialize( serialized ) );
888 }
889 catch ( IOException ioe )
890 {
891 throw new LdapOtherException( ioe.getMessage(), ioe );
892 }
893 }
894
895
896
897
898
899
900
901
902 @Override
903 public synchronized void close( PartitionTxn transaction ) throws LdapException
904 {
905
906 }
907
908
909 public Marshaller<ArrayTree<V>> getMarshaller()
910 {
911 return marshaller;
912 }
913
914
915
916
917
918
919
920
921
922
923 boolean isKeyUsingBTree( K key ) throws Exception
924 {
925 if ( key == null )
926 {
927 throw new IllegalArgumentException( "key is null" );
928 }
929
930 if ( !allowsDuplicates )
931 {
932 return false;
933 }
934
935 DupsContainer<V> values = getDupsContainer( ( byte[] ) bt.find( key ) );
936
937 return values.isBTreeRedirect();
938 }
939
940
941 DupsContainer<V> getDupsContainer( byte[] serialized ) throws LdapException
942 {
943 if ( serialized == null )
944 {
945 return new DupsContainer<>( new ArrayTree<V>( valueComparator ) );
946 }
947
948 if ( BTreeRedirectMarshaller.isRedirect( serialized ) )
949 {
950 try
951 {
952 return new DupsContainer<>( BTreeRedirectMarshaller.INSTANCE.deserialize( serialized ) );
953 }
954 catch ( IOException ioe )
955 {
956 throw new LdapOtherException( ioe.getMessage() );
957 }
958
959 }
960
961 try
962 {
963 return new DupsContainer<>( marshaller.deserialize( serialized ) );
964 }
965 catch ( IOException ioe )
966 {
967 throw new LdapOtherException( ioe.getMessage() );
968 }
969 }
970
971
972
973
974
975
976
977 BTree getBTree()
978 {
979 return bt;
980 }
981
982
983 BTree getBTree( BTreeRedirect redirect ) throws IOException
984 {
985 if ( duplicateBtrees.containsKey( redirect.getRecId() ) )
986 {
987 return duplicateBtrees.get( redirect.getRecId() );
988 }
989
990 BTree<K, V> tree = new BTree<K, V>().load( recMan, redirect.getRecId() );
991 ( ( SerializableComparator<K> ) tree.getComparator() ).setSchemaManager( schemaManager );
992 duplicateBtrees.put( redirect.getRecId(), tree );
993
994 return tree;
995 }
996
997
998 @SuppressWarnings("unchecked")
999 private boolean btreeHas( BTree tree, V key, boolean isGreaterThan ) throws IOException
1000 {
1001 jdbm.helper.Tuple tuple = new jdbm.helper.Tuple();
1002
1003 TupleBrowser browser = tree.browse( key );
1004
1005 if ( isGreaterThan )
1006 {
1007 return browser.getNext( tuple );
1008 }
1009 else
1010 {
1011 if ( browser.getPrevious( tuple ) )
1012 {
1013 return true;
1014 }
1015 else
1016 {
1017
1018
1019
1020
1021
1022 browser.getNext( tuple );
1023
1024
1025
1026
1027
1028
1029 V firstKey = ( V ) tuple.getKey();
1030
1031 return valueComparator.compare( key, firstKey ) == 0;
1032 }
1033 }
1034 }
1035
1036
1037 @SuppressWarnings("unchecked")
1038 private ArrayTree<V> convertToArrayTree( BTree bTree ) throws IOException
1039 {
1040 ArrayTree<V> avlTree = new ArrayTree<>( valueComparator );
1041 TupleBrowser browser = bTree.browse();
1042 jdbm.helper.Tuple tuple = new jdbm.helper.Tuple();
1043
1044 while ( browser.getNext( tuple ) )
1045 {
1046 avlTree.insert( ( V ) tuple.getKey() );
1047 }
1048
1049 return avlTree;
1050 }
1051
1052
1053 private BTree<V, K> convertToBTree( ArrayTree<V> arrayTree ) throws IOException, CursorException, LdapException
1054 {
1055 BTree<V, K> bTree;
1056
1057 if ( valueSerializer != null )
1058 {
1059 bTree = new BTree<>( recMan, valueComparator, valueSerializer, null );
1060 }
1061 else
1062 {
1063 bTree = new BTree<>( recMan, valueComparator );
1064 }
1065
1066 try ( Cursor<V> keys = new ArrayTreeCursor<>( arrayTree ) )
1067 {
1068 keys.beforeFirst();
1069
1070 while ( keys.next() )
1071 {
1072 bTree.insert( keys.get(), ( K ) Strings.EMPTY_BYTES, true );
1073 }
1074 }
1075
1076 return bTree;
1077 }
1078 }