1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.mavibot.btree;
21
22
23 import java.io.BufferedReader;
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.InputStreamReader;
27 import java.io.RandomAccessFile;
28 import java.nio.BufferUnderflowException;
29 import java.nio.ByteBuffer;
30 import java.nio.channels.FileChannel;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Map;
34 import java.util.Set;
35
36 import org.apache.directory.mavibot.btree.exception.InvalidBTreeException;
37 import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
38 import org.apache.directory.mavibot.btree.serializer.LongSerializer;
39 import org.apache.directory.mavibot.btree.serializer.StringSerializer;
40 import org.apache.directory.mavibot.btree.util.Strings;
41
42
43
44
45
46
47
48 public class MavibotInspector
49 {
50
51 private File dbFile;
52
53
54 private RecordManager rm;
55
56 private BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
57
58
59 private static final String GLOBAL_PAGES_NAME = "__global__";
60 private static final String FREE_PAGES_NAME = "__free-pages__";
61
62
63 private static Set<String> knownPagesArrays = new HashSet<String>();
64
65 static
66 {
67 knownPagesArrays.add( GLOBAL_PAGES_NAME );
68 knownPagesArrays.add( FREE_PAGES_NAME );
69 knownPagesArrays.add( RecordManager.BTREE_OF_BTREES_NAME );
70 knownPagesArrays.add( RecordManager.COPIED_PAGE_BTREE_NAME );
71 }
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 public MavibotInspector( File dbFile )
89 {
90 this.dbFile = dbFile;
91 }
92
93
94
95
96
97 private boolean checkFilePresence()
98 {
99 if ( dbFile == null )
100 {
101 System.out.println( "No mavibot database file was given" );
102 return false;
103 }
104
105 if ( !dbFile.exists() )
106 {
107 System.out.println( "Given mavibot database file " + dbFile + " doesn't exist" );
108 return false;
109 }
110
111 return true;
112 }
113
114
115
116
117
118 public void printFileSize() throws IOException
119 {
120 FileChannel fileChannel = new RandomAccessFile( dbFile, "r" ).getChannel();
121
122 long l = fileChannel.size();
123
124 fileChannel.close();
125
126 String msg;
127
128 if ( l < 1024 )
129 {
130 msg = l + " bytes";
131 }
132 else
133 {
134 msg = ( l / 1024 ) + " KB";
135 }
136
137 System.out.println( msg );
138
139 fileChannel.close();
140 }
141
142
143
144
145
146 public void printNumberOfBTrees()
147 {
148 int nbBtrees = 0;
149
150 if ( rm != null )
151 {
152 nbBtrees = rm.getNbManagedTrees();
153 }
154
155
156 System.out.println( "Total Number of BTrees: " + nbBtrees );
157 }
158
159
160
161
162
163 public void printBTreeNames()
164 {
165 if ( rm == null )
166 {
167 System.out.println( "Couldn't find the number of managed btrees" );
168 return;
169 }
170
171 Set<String> trees = rm.getManagedTrees();
172 System.out.println( "\nManaged BTrees:" );
173
174 for ( String tree : trees )
175 {
176 System.out.println( tree );
177 }
178
179 System.out.println();
180 }
181
182
183
184
185
186 public void checkBTree()
187 {
188 if ( rm == null )
189 {
190 System.out.println( "Cannot check BTree(s)" );
191 return;
192 }
193
194 System.out.print( "BTree Name: " );
195 String name = readLine();
196
197 PersistedBTree<?, ?> pb = ( PersistedBTree<?, ?> ) rm.getManagedTree( name );
198
199 if ( pb == null )
200 {
201 System.out.println( "No BTree exists with the name '" + name + "'" );
202 return;
203 }
204
205 System.out.println( "\nBTree offset: " + String.format( "0x%1$08x", pb.getBtreeOffset() ) );
206 System.out.println( "BTree _info_ offset: " + String.format( "0x%1$08x", pb.getBtreeInfoOffset() ) );
207 System.out.println( "BTree root page offset: " + String.format( "0x%1$08x", pb.getRootPageOffset() ) );
208 System.out.println( "Number of elements present: " + pb.getNbElems() );
209 System.out.println( "BTree Page size: " + pb.getPageSize() );
210 System.out.println( "BTree revision: " + pb.getRevision() );
211 System.out.println( "Key serializer: " + pb.getKeySerializerFQCN() );
212 System.out.println( "Value serializer: " + pb.getValueSerializerFQCN() );
213 System.out.println();
214 }
215
216
217
218
219
220 private boolean loadRm()
221 {
222 try
223 {
224 if( rm != null )
225 {
226 System.out.println("Closing record manager");
227 rm.close();
228 }
229
230 rm = new RecordManager( dbFile.getAbsolutePath() );
231 System.out.println("Loaded record manager");
232 }
233 catch ( Exception e )
234 {
235 System.out.println( "Given database file seems to be corrupted. " + e.getMessage() );
236 return false;
237 }
238
239 return true;
240 }
241
242
243
244
245
246 static void check( RecordManager recordManager )
247 {
248 try
249 {
250
251 ByteBuffer recordManagerHeader = ByteBuffer.allocate( RecordManager.RECORD_MANAGER_HEADER_SIZE );
252 long fileSize = recordManager.fileChannel.size();
253
254 if ( fileSize < RecordManager.RECORD_MANAGER_HEADER_SIZE )
255 {
256 throw new InvalidBTreeException( "File size too small : " + fileSize );
257 }
258
259
260 recordManager.fileChannel.read( recordManagerHeader, 0L );
261 recordManagerHeader.flip();
262
263
264 int pageSize = recordManagerHeader.getInt();
265
266 if ( ( pageSize != recordManager.pageSize ) || ( pageSize < 32 ) || ( ( pageSize & ( ~pageSize + 1 ) ) != pageSize ) )
267 {
268 throw new InvalidBTreeException( "Wrong page size : " + pageSize );
269 }
270
271
272 long nbPages = ( fileSize - RecordManager.RECORD_MANAGER_HEADER_SIZE ) / pageSize;
273
274
275 int nbBtrees = recordManagerHeader.getInt();
276
277 if ( ( nbBtrees < 0 ) || ( nbBtrees != recordManager.nbBtree ) )
278 {
279 throw new InvalidBTreeException( "Wrong nb trees : " + nbBtrees );
280 }
281
282
283
284 long firstFreePage = recordManagerHeader.getLong();
285
286 if ( firstFreePage != RecordManager.NO_PAGE )
287 {
288 checkOffset( recordManager, firstFreePage );
289 }
290
291 int nbPageBits = ( int ) ( nbPages / 32 );
292
293
294
295
296
297 Map<String, int[]> checkedPages = new HashMap<String, int[]>(nbBtrees + 4);
298
299
300 checkedPages.put( GLOBAL_PAGES_NAME, new int[nbPageBits + 1] );
301
302
303 checkedPages.put( FREE_PAGES_NAME, new int[nbPageBits + 1] );
304
305
306 checkedPages.put( RecordManager.BTREE_OF_BTREES_NAME, new int[nbPageBits + 1] );
307
308
309 checkedPages.put( RecordManager.COPIED_PAGE_BTREE_NAME, new int[nbPageBits + 1] );
310
311
312 checkFreePages( recordManager, checkedPages );
313
314
315 long currentBtreeOfBtreesOffset = recordManagerHeader.getLong();
316 long previousBtreeOfBtreesOffset = recordManagerHeader.getLong();
317 long currentCopiedPagesBtreeOffset = recordManagerHeader.getLong();
318 long previousCopiedPagesBtreeOffset = recordManagerHeader.getLong();
319
320
321 if ( previousBtreeOfBtreesOffset != RecordManager.NO_PAGE )
322 {
323 System.out.println( "The previous Btree of Btrees offset is not valid : "
324 + previousBtreeOfBtreesOffset );
325 return;
326 }
327
328
329 if ( previousCopiedPagesBtreeOffset != RecordManager.NO_PAGE )
330 {
331 System.out.println( "The previous Copied Pages Btree offset is not valid : "
332 + previousCopiedPagesBtreeOffset );
333 return;
334 }
335
336
337 checkOffset( recordManager, currentBtreeOfBtreesOffset );
338
339
340 checkOffset( recordManager, currentCopiedPagesBtreeOffset );
341
342
343 checkBtreeOfBtrees( recordManager, checkedPages );
344
345
346 checkBtree( recordManager, currentCopiedPagesBtreeOffset, checkedPages );
347
348
349 dumpCheckedPages( recordManager, checkedPages );
350 }
351 catch ( Exception e )
352 {
353
354
355 e.printStackTrace();
356 throw new InvalidBTreeException( "Error : " + e.getMessage() );
357 }
358 }
359
360
361
362
363
364 private static <K, V> void checkBtreeOfBtrees( RecordManager recordManager, Map<String, int[]> checkedPages ) throws Exception
365 {
366
367 PageIO[] bobHeaderPageIos = recordManager.readPageIOs( recordManager.currentBtreeOfBtreesOffset, Long.MAX_VALUE );
368
369
370 updateCheckedPages( checkedPages.get( RecordManager.BTREE_OF_BTREES_NAME), recordManager.pageSize, bobHeaderPageIos );
371 updateCheckedPages( checkedPages.get( GLOBAL_PAGES_NAME ), recordManager.pageSize, bobHeaderPageIos );
372
373 long dataPos = 0L;
374
375
376 recordManager.readLong( bobHeaderPageIos, dataPos );
377 dataPos += RecordManager.LONG_SIZE;
378
379
380 recordManager.readLong( bobHeaderPageIos, dataPos );
381 dataPos += RecordManager.LONG_SIZE;
382
383
384 long rootPageOffset = recordManager.readLong( bobHeaderPageIos, dataPos );
385
386 checkOffset( recordManager, rootPageOffset );
387
388 dataPos += RecordManager.LONG_SIZE;
389
390
391 long btreeInfoOffset = recordManager.readLong( bobHeaderPageIos, dataPos );
392
393 checkOffset( recordManager, btreeInfoOffset );
394
395 checkBtreeInfo( recordManager, checkedPages, btreeInfoOffset, -1L );
396
397
398
399 checkBtreeOfBtreesPage( recordManager, checkedPages, rootPageOffset );
400 }
401
402
403
404
405
406 private static <K, V> void checkBtree( RecordManager recordManager, long btreeOffset, Map<String, int[]> checkedPages ) throws Exception
407 {
408
409 PageIO[] btreeHeaderPageIos = recordManager.readPageIOs( btreeOffset, Long.MAX_VALUE );
410
411 long dataPos = 0L;
412
413
414 long btreeRevision = recordManager.readLong( btreeHeaderPageIos, dataPos );
415 dataPos += RecordManager.LONG_SIZE;
416
417
418 recordManager.readLong( btreeHeaderPageIos, dataPos );
419 dataPos += RecordManager.LONG_SIZE;
420
421
422 long rootPageOffset = recordManager.readLong( btreeHeaderPageIos, dataPos );
423
424 checkOffset( recordManager, rootPageOffset );
425
426 dataPos += RecordManager.LONG_SIZE;
427
428
429 long btreeInfoOffset = recordManager.readLong( btreeHeaderPageIos, dataPos );
430
431 checkOffset( recordManager, btreeInfoOffset );
432
433 BtreeInfo<K, V> btreeInfo = checkBtreeInfo( recordManager, checkedPages, btreeInfoOffset, btreeRevision );
434
435
436 updateCheckedPages( checkedPages.get( btreeInfo.btreeName ), recordManager.pageSize, btreeHeaderPageIos );
437 updateCheckedPages( checkedPages.get( GLOBAL_PAGES_NAME ), recordManager.pageSize, btreeHeaderPageIos );
438
439
440 checkBtreePage( recordManager, btreeInfo, checkedPages, rootPageOffset );
441 }
442
443
444
445
446
447 private static <K, V> void checkBtreePage( RecordManager recordManager, BtreeInfo<K, V> btreeInfo, Map<String, int[]> checkedPages, long pageOffset ) throws Exception
448 {
449 PageIO[] pageIos = recordManager.readPageIOs( pageOffset, Long.MAX_VALUE );
450
451
452 updateCheckedPages( checkedPages.get( btreeInfo.btreeName ), recordManager.pageSize, pageIos );
453 updateCheckedPages( checkedPages.get( GLOBAL_PAGES_NAME ), recordManager.pageSize, pageIos );
454
455
456 long position = 0L;
457
458
459 long revision = recordManager.readLong( pageIos, position );
460 position += RecordManager.LONG_SIZE;
461
462
463 int nbElems = recordManager.readInt( pageIos, position );
464 position += RecordManager.INT_SIZE;
465
466
467
468
469
470 ByteBuffer byteBuffer = recordManager.readBytes( pageIos, position );
471
472
473
474
475 if ( nbElems >= 0 )
476 {
477
478 checkBtreeLeaf( recordManager, btreeInfo, checkedPages, nbElems, revision, byteBuffer, pageIos );
479 }
480 else
481 {
482
483 long[] children = checkBtreeNode( recordManager, btreeInfo, checkedPages, -nbElems, revision, byteBuffer, pageIos );
484
485 for ( int pos = 0; pos <= -nbElems; pos++ )
486 {
487
488 checkBtreePage( recordManager, btreeInfo, checkedPages, children[pos] );
489 }
490 }
491 }
492
493
494
495
496
497
498 private static <K, V> BtreeInfo<K, V> checkBtreeInfo( RecordManager recordManager, Map<String, int[]> checkedPages, long btreeInfoOffset, long btreeRevision ) throws IOException
499 {
500 BtreeInfo<K, V> btreeInfo = new BtreeInfo<K, V>();
501
502 PageIO[] btreeInfoPagesIos = recordManager.readPageIOs( btreeInfoOffset, Long.MAX_VALUE );
503
504 long dataPos = 0L;
505
506
507 recordManager.readInt( btreeInfoPagesIos, dataPos );
508 dataPos += RecordManager.INT_SIZE;
509
510
511 ByteBuffer btreeNameBytes = recordManager.readBytes( btreeInfoPagesIos, dataPos );
512 dataPos += RecordManager.INT_SIZE + btreeNameBytes.limit();
513 String btreeName = Strings.utf8ToString( btreeNameBytes );
514
515
516 ByteBuffer keySerializerBytes = recordManager.readBytes( btreeInfoPagesIos, dataPos );
517
518 if ( keySerializerBytes != null )
519 {
520 String keySerializerFqcn = Strings.utf8ToString( keySerializerBytes );
521
522 btreeInfo.keySerializer = getSerializer( keySerializerFqcn );
523 }
524
525 dataPos += RecordManager.INT_SIZE + keySerializerBytes.limit();
526
527
528 ByteBuffer valueSerializerBytes = recordManager.readBytes( btreeInfoPagesIos, dataPos );
529
530 if ( valueSerializerBytes != null )
531 {
532 String valueSerializerFqcn = Strings.utf8ToString( valueSerializerBytes );
533
534 btreeInfo.valueSerializer = getSerializer( valueSerializerFqcn );
535 }
536
537 dataPos += RecordManager.INT_SIZE + valueSerializerBytes.limit();
538
539
540 recordManager.readInt( btreeInfoPagesIos, dataPos );
541 dataPos += RecordManager.INT_SIZE;
542
543
544 if ( !RecordManager.COPIED_PAGE_BTREE_NAME.equals( btreeName ) && !RecordManager.BTREE_OF_BTREES_NAME.equals( btreeName ) )
545 {
546 btreeName = btreeName + "<" + btreeRevision + ">";
547 }
548
549 btreeInfo.btreeName = btreeName;
550
551
552 int[] checkedPagesArray = checkedPages.get( btreeName );
553
554 if ( checkedPagesArray == null )
555 {
556
557 checkedPagesArray = createPageArray( recordManager );
558 checkedPages.put( btreeName, checkedPagesArray );
559 }
560
561 updateCheckedPages( checkedPagesArray, recordManager.pageSize, btreeInfoPagesIos );
562 updateCheckedPages( checkedPages.get( GLOBAL_PAGES_NAME ), recordManager.pageSize, btreeInfoPagesIos );
563
564 return btreeInfo;
565 }
566
567
568
569
570
571 @SuppressWarnings("unchecked")
572 private static <T> ElementSerializer<T> getSerializer( String serializerFqcn )
573 {
574 try
575 {
576 Class<?> serializerClass = Class.forName( serializerFqcn );
577 ElementSerializer<T> serializer = null;
578
579 try
580 {
581 serializer = ( ElementSerializer<T> ) serializerClass.getDeclaredField( "INSTANCE" ).get( null );
582 }
583 catch( NoSuchFieldException e )
584 {
585
586 }
587
588 if ( serializer == null )
589 {
590 serializer = ( ElementSerializer<T> ) serializerClass.newInstance();
591 }
592
593 return serializer;
594 }
595 catch ( Exception e )
596 {
597 throw new InvalidBTreeException( "Error : " + e.getMessage() );
598 }
599 }
600
601
602
603
604
605 private static <K, V> void checkBtreeOfBtreesPage( RecordManager recordManager, Map<String, int[]> checkedPages, long pageOffset ) throws Exception
606 {
607 PageIO[] pageIos = recordManager.readPageIOs( pageOffset, Long.MAX_VALUE );
608
609
610 updateCheckedPages( checkedPages.get( RecordManager.BTREE_OF_BTREES_NAME), recordManager.pageSize, pageIos );
611 updateCheckedPages( checkedPages.get( GLOBAL_PAGES_NAME ), recordManager.pageSize, pageIos );
612
613
614 long position = 0L;
615
616
617 long revision = recordManager.readLong( pageIos, position );
618 position += RecordManager.LONG_SIZE;
619
620
621 int nbElems = recordManager.readInt( pageIos, position );
622 position += RecordManager.INT_SIZE;
623
624
625
626
627
628 ByteBuffer byteBuffer = recordManager.readBytes( pageIos, position );
629
630
631
632
633 if ( nbElems >= 0 )
634 {
635
636 checkBtreeOfBtreesLeaf( recordManager, checkedPages, nbElems, revision, byteBuffer, pageIos );
637 }
638 else
639 {
640
641 long[] children = checkBtreeOfBtreesNode( recordManager, checkedPages, -nbElems, revision, byteBuffer, pageIos );
642
643 for ( int pos = 0; pos <= -nbElems; pos++ )
644 {
645
646 checkBtreeOfBtreesPage( recordManager, checkedPages, children[pos] );
647 }
648 }
649 }
650
651
652
653
654
655 private static <K, V> void checkBtreeOfBtreesLeaf( RecordManager recordManager, Map<String, int[]> checkedPages, int nbElems, long revision, ByteBuffer byteBuffer, PageIO[] pageIos ) throws Exception
656 {
657
658 for ( int i = 0; i < nbElems; i++ )
659 {
660 try
661 {
662
663 int nbValues = byteBuffer.getInt();
664
665 if ( nbValues != 1 )
666 {
667 throw new InvalidBTreeException( "We should have only one value for a BOB " + nbValues );
668 }
669
670
671
672 int valueLength = byteBuffer.getInt();
673
674 if ( valueLength != RecordManager.LONG_SIZE + RecordManager.INT_SIZE )
675 {
676 throw new InvalidBTreeException( "The BOB value length is invalid " + valueLength );
677 }
678
679
680 int offsetLength = byteBuffer.getInt();
681
682 if ( offsetLength != RecordManager.LONG_SIZE )
683 {
684 throw new InvalidBTreeException( "The BOB value offset length is invalid " + offsetLength );
685 }
686
687
688 long btreeOffset = byteBuffer.getLong();
689
690 checkOffset( recordManager, btreeOffset );
691
692
693
694 int keyLength = byteBuffer.getInt();
695
696
697 if ( keyLength < RecordManager.LONG_SIZE + RecordManager.INT_SIZE )
698 {
699 throw new InvalidBTreeException( "The BOB key length is invalid " + keyLength );
700 }
701
702
703 long btreeRevision = byteBuffer.getLong();
704
705
706 int btreeNameLength = byteBuffer.getInt();
707
708
709 if ( keyLength != RecordManager.LONG_SIZE + RecordManager.INT_SIZE + btreeNameLength )
710 {
711 throw new InvalidBTreeException( "The BOB key length is not the expected value " +
712 ( RecordManager.LONG_SIZE + RecordManager.INT_SIZE + btreeNameLength ) + ", expected " + keyLength );
713 }
714
715 byte[] bytes = new byte[btreeNameLength];
716 byteBuffer.get( bytes );
717 String btreeName = Strings.utf8ToString( bytes );
718
719
720 int[] btreePagesArray = createPageArray( recordManager );
721 checkedPages.put( btreeName + "<" + btreeRevision + ">", btreePagesArray );
722
723
724 checkBtree( recordManager, btreeOffset, checkedPages );
725
726
727 }
728 catch ( BufferUnderflowException bue )
729 {
730 throw new InvalidBTreeException( "The BOB leaf byte buffer is too short : " + bue.getMessage() );
731 }
732 }
733 }
734
735
736
737
738
739 private static <K, V> void checkBtreeLeaf( RecordManager recordManager, BtreeInfo<K, V> btreeInfo, Map<String, int[]> checkedPages, int nbElems, long revision, ByteBuffer byteBuffer, PageIO[] pageIos ) throws Exception
740 {
741
742 for ( int i = 0; i < nbElems; i++ )
743 {
744 try
745 {
746
747 int nbValues = byteBuffer.getInt();
748
749 if ( nbValues < 0 )
750 {
751
752 long subBtreeOffset = byteBuffer.getLong();
753
754
755 checkBtree( recordManager, subBtreeOffset, checkedPages );
756
757
758
759 byteBuffer.getInt();
760
761
762 btreeInfo.keySerializer.deserialize( byteBuffer );
763 }
764 else
765 {
766
767
768 byteBuffer.getInt();
769 btreeInfo.valueSerializer.deserialize( byteBuffer );
770
771
772 byteBuffer.getInt();
773
774 btreeInfo.keySerializer.deserialize( byteBuffer );
775 }
776 }
777 catch ( BufferUnderflowException bue )
778 {
779 throw new InvalidBTreeException( "The BOB leaf byte buffer is too short : " + bue.getMessage() );
780 }
781 }
782 }
783
784
785
786
787 private static <K, V> long[] checkBtreeOfBtreesNode( RecordManager recordManager, Map<String, int[]> checkedPages, int nbElems, long revision,
788 ByteBuffer byteBuffer, PageIO[] pageIos ) throws IOException
789 {
790 long[] children = new long[nbElems + 1];
791
792
793 for ( int i = 0; i < nbElems; i++ )
794 {
795
796 long firstOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
797
798 checkOffset( recordManager, firstOffset );
799
800 long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
801
802 checkOffset( recordManager, lastOffset );
803
804 children[i] = firstOffset;
805
806
807 int keyLength = byteBuffer.getInt();
808
809
810 if ( keyLength < RecordManager.LONG_SIZE + RecordManager.INT_SIZE )
811 {
812 throw new InvalidBTreeException( "The BOB key length is invalid " + keyLength );
813 }
814
815
816 byteBuffer.getLong();
817
818
819 int btreeNameLength = byteBuffer.getInt();
820
821
822 if ( keyLength != RecordManager.LONG_SIZE + RecordManager.INT_SIZE + btreeNameLength )
823 {
824 throw new InvalidBTreeException( "The BOB key length is not the expected value " +
825 ( RecordManager.LONG_SIZE + RecordManager.INT_SIZE + btreeNameLength ) + ", expected " + keyLength );
826 }
827
828
829 byte[] bytes = new byte[btreeNameLength];
830 byteBuffer.get( bytes );
831 }
832
833
834
835 long firstOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
836
837 checkOffset( recordManager, firstOffset );
838
839 long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
840
841 checkOffset( recordManager, lastOffset );
842
843 children[nbElems] = firstOffset;
844
845
846 return children;
847 }
848
849
850
851
852
853 private static <K, V> long[] checkBtreeNode( RecordManager recordManager, BtreeInfo<K, V> btreeInfo, Map<String, int[]> checkedPages, int nbElems, long revision, ByteBuffer byteBuffer, PageIO[] pageIos ) throws Exception
854 {
855 long[] children = new long[nbElems + 1];
856
857
858 for ( int i = 0; i < nbElems; i++ )
859 {
860 try
861 {
862
863 long firstOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
864
865 checkOffset( recordManager, firstOffset );
866
867 long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
868
869 checkOffset( recordManager, lastOffset );
870
871 children[i] = firstOffset;
872
873
874
875 byteBuffer.getInt();
876
877
878 btreeInfo.keySerializer.deserialize( byteBuffer );
879 }
880 catch ( BufferUnderflowException bue )
881 {
882 throw new InvalidBTreeException( "The BOB leaf byte buffer is too short : " + bue.getMessage() );
883 }
884 }
885
886
887
888 long firstOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
889
890 checkOffset( recordManager, firstOffset );
891
892 long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
893
894 checkOffset( recordManager, lastOffset );
895
896 children[nbElems] = firstOffset;
897
898 return children;
899 }
900
901
902
903
904
905 private static int[] createPageArray( RecordManager recordManager ) throws IOException
906 {
907 long fileSize = recordManager.fileChannel.size();
908 int pageSize = recordManager.pageSize;
909 long nbPages = ( fileSize - RecordManager.RECORD_MANAGER_HEADER_SIZE ) / pageSize;
910 int nbPageBits = ( int ) ( nbPages / 32 );
911
912 return new int[nbPageBits + 1];
913 }
914
915
916
917
918
919 private static void updateCheckedPages( int[] checkedPages, int pageSize, PageIO... pageIos )
920 {
921 for ( PageIO pageIO : pageIos )
922 {
923 long offset = pageIO.getOffset();
924
925 if ( ( offset % pageSize ) != 0 )
926 {
927 throw new InvalidBTreeException( "Offset invalid : " + offset );
928 }
929
930 int pageNumber = (int)(offset / pageSize);
931 int nbBitsPage = ( RecordManager.INT_SIZE << 3 );
932 int pageMask = checkedPages[ pageNumber / nbBitsPage ];
933 int mask = 1 << pageNumber % nbBitsPage;
934
935 if ( ( pageMask & mask ) != 0 )
936 {
937
938 }
939
940 pageMask |= mask;
941 checkedPages[ pageNumber / nbBitsPage ] = pageMask;
942 }
943 }
944
945
946
947
948
949
950
951
952
953
954 private static void checkOffset( RecordManager recordManager, long offset ) throws IOException
955 {
956 if ( ( offset == RecordManager.NO_PAGE ) ||
957 ( ( ( offset - RecordManager.RECORD_MANAGER_HEADER_SIZE ) % recordManager.pageSize ) != 0 ) ||
958 ( offset > recordManager.fileChannel.size() ) )
959 {
960 throw new InvalidBTreeException( "Invalid Offset : " + offset );
961 }
962 }
963
964
965
966
967
968 private static void checkFreePages( RecordManager recordManager, Map<String, int[]> checkedPages )
969 throws IOException
970 {
971 if ( recordManager.firstFreePage == RecordManager.NO_PAGE )
972 {
973 return;
974 }
975
976
977 long currentOffset = recordManager.firstFreePage;
978 long fileSize = recordManager.fileChannel.size();
979
980 while ( currentOffset != RecordManager.NO_PAGE )
981 {
982 if ( currentOffset > fileSize )
983 {
984 System.out.println( "Wrong free page offset, above file size : " + currentOffset );
985 return;
986 }
987
988 try
989 {
990 PageIO pageIo = recordManager.fetchPage( currentOffset );
991
992 if ( currentOffset != pageIo.getOffset() )
993 {
994 System.out.println( "PageIO offset is incorrect : " + currentOffset + "-"
995 + pageIo.getOffset() );
996 return;
997 }
998
999 setCheckedPage( recordManager, checkedPages.get( GLOBAL_PAGES_NAME ), currentOffset );
1000 setCheckedPage( recordManager, checkedPages.get( FREE_PAGES_NAME ), currentOffset );
1001
1002 long newOffset = pageIo.getNextPage();
1003 currentOffset = newOffset;
1004 }
1005 catch ( IOException ioe )
1006 {
1007 throw new InvalidBTreeException( "Cannot fetch page at : " + currentOffset );
1008 }
1009 }
1010 }
1011
1012
1013
1014
1015
1016 private static void setCheckedPage( RecordManager recordManager, int[] checkedPages, long offset )
1017 {
1018 int pageNumber = ( int ) offset / recordManager.pageSize;
1019 int nbBitsPage = ( RecordManager.INT_SIZE << 3 );
1020 long pageMask = checkedPages[ pageNumber / nbBitsPage ];
1021 long mask = 1L << pageNumber % nbBitsPage;
1022
1023 if ( ( pageMask & mask ) != 0 )
1024 {
1025
1026 }
1027
1028 pageMask |= mask;
1029
1030 checkedPages[ pageNumber / nbBitsPage ] |= pageMask;
1031 }
1032
1033
1034
1035
1036
1037
1038 private static void dumpCheckedPages( RecordManager recordManager, Map<String, int[]> checkedPages ) throws IOException
1039 {
1040
1041 int[] globalArray = checkedPages.get( GLOBAL_PAGES_NAME );
1042 String result = dumpPageArray( recordManager, globalArray );
1043
1044 String dump = String.format( "%1$-40s : %2$s", GLOBAL_PAGES_NAME, result );
1045 System.out.println( dump );
1046
1047
1048 int[] freePagesArray = checkedPages.get( FREE_PAGES_NAME );
1049 result = dumpPageArray( recordManager, freePagesArray );
1050
1051 dump = String.format( "%1$-40s : %2$s", FREE_PAGES_NAME, result );
1052 System.out.println( dump );
1053
1054
1055 int[] btreeOfBtreesArray = checkedPages.get( RecordManager.BTREE_OF_BTREES_NAME );
1056 result = dumpPageArray( recordManager, btreeOfBtreesArray );
1057
1058 dump = String.format( "%1$-40s : %2$s", RecordManager.BTREE_OF_BTREES_NAME, result );
1059 System.out.println( dump );
1060
1061
1062 int[] copiedPagesArray = checkedPages.get( RecordManager.COPIED_PAGE_BTREE_NAME );
1063 result = dumpPageArray( recordManager, copiedPagesArray );
1064
1065 dump = String.format( "%1$-40s : %2$s", RecordManager.COPIED_PAGE_BTREE_NAME, result );
1066 System.out.println( dump );
1067
1068
1069 for ( String btreeName : checkedPages.keySet() )
1070 {
1071
1072 if ( knownPagesArrays.contains( btreeName ) )
1073 {
1074 continue;
1075 }
1076
1077 int[] btreePagesArray = checkedPages.get( btreeName );
1078 result = dumpPageArray( recordManager, btreePagesArray );
1079
1080 dump = String.format( "%1$-40s : %2$s", btreeName, result );
1081 System.out.println( dump );
1082 }
1083 }
1084
1085
1086
1087
1088
1089 private static String dumpPageArray( RecordManager recordManager, int[] checkedPages ) throws IOException
1090 {
1091 StringBuilder sb = new StringBuilder();
1092 int i = -1;
1093 int nbPagesChecked = 0;
1094 long fileSize = recordManager.fileChannel.size();
1095 long nbPages = ( fileSize - RecordManager.RECORD_MANAGER_HEADER_SIZE ) / recordManager.pageSize;
1096
1097 for ( int checkedPage : checkedPages )
1098 {
1099 if ( i == 0 )
1100 {
1101 sb.append( " " );
1102 i++;
1103 }
1104 else
1105 {
1106 i = 0;
1107 }
1108
1109 sb.append( "[" ).append( i ).append( "] " );
1110
1111
1112 for ( int j = 0; j < 32; j++ )
1113 {
1114 if ( nbPagesChecked >= nbPages + 1 )
1115 {
1116 sb.append( "." );
1117 }
1118 else
1119 {
1120 if ( ( checkedPage & ( 1 << j ) ) == 0 )
1121 {
1122 sb.append( "0" );
1123 }
1124 else
1125 {
1126 sb.append( "1" );
1127 }
1128 }
1129
1130 nbPagesChecked++;
1131 }
1132 }
1133
1134 return sb.toString();
1135 }
1136
1137
1138
1139
1140
1141 public void start() throws Exception
1142 {
1143 if ( !checkFilePresence() )
1144 {
1145 return;
1146 }
1147
1148 if ( !loadRm() )
1149 {
1150 return;
1151 }
1152
1153 boolean stop = false;
1154
1155 while ( !stop )
1156 {
1157 System.out.println( "Choose an option:" );
1158 System.out.println( "1. Print Number of BTrees" );
1159 System.out.println( "2. Print BTree Names" );
1160 System.out.println( "3. Inspect BTree" );
1161 System.out.println( "4. Check Free Pages" );
1162 System.out.println( "5. Get database file size" );
1163 System.out.println( "6. Dump RecordManager" );
1164 System.out.println( "7. Reload RecordManager" );
1165 System.out.println( "q. Quit" );
1166
1167 char c = readOption();
1168
1169 switch ( c )
1170 {
1171 case '1':
1172 printNumberOfBTrees();
1173 break;
1174
1175 case '2':
1176 printBTreeNames();
1177 break;
1178
1179 case '3':
1180 checkBTree();
1181 break;
1182
1183 case '4':
1184 long fileSize = rm.fileChannel.size();
1185 long nbPages = fileSize / rm.pageSize;
1186 int nbPageBits = ( int ) ( nbPages / RecordManager.INT_SIZE );
1187
1188 Map<String, int[]> checkedPages = new HashMap<String, int[]>(2);
1189
1190
1191 checkedPages.put( GLOBAL_PAGES_NAME, new int[nbPageBits + 1] );
1192
1193
1194 checkedPages.put( FREE_PAGES_NAME, new int[nbPageBits + 1] );
1195
1196 checkFreePages( rm, checkedPages );
1197 break;
1198
1199 case '5':
1200 printFileSize();
1201 break;
1202
1203 case '6':
1204 check( rm );
1205 break;
1206
1207 case '7':
1208 loadRm();
1209 break;
1210
1211 case 'q':
1212 stop = true;
1213 break;
1214
1215 default:
1216 System.out.println( "Invalid option" );
1217
1218 break;
1219 }
1220 }
1221
1222 try
1223 {
1224 rm.close();
1225 br.close();
1226 }
1227 catch ( Exception e )
1228 {
1229
1230 }
1231 }
1232
1233
1234
1235
1236
1237 private String readLine()
1238 {
1239 try
1240 {
1241 return br.readLine().trim();
1242 }
1243 catch ( Exception e )
1244 {
1245 throw new RuntimeException( e );
1246 }
1247 }
1248
1249
1250
1251
1252
1253 private char readOption()
1254 {
1255 try
1256 {
1257 String s = br.readLine();
1258
1259 if ( s.length() == 0 )
1260 {
1261 return ' ';
1262 }
1263
1264 return s.charAt( 0 );
1265 }
1266 catch ( Exception e )
1267 {
1268 throw new RuntimeException( e );
1269 }
1270 }
1271
1272
1273
1274
1275
1276 public static void main( String[] args ) throws Exception
1277 {
1278 File f = new File( "/tmp/mavibotispector.db" );
1279
1280 RecordManager rm = new RecordManager( f.getAbsolutePath() );
1281 String name1 = "corpus";
1282 String name2 = "multiValues";
1283
1284
1285
1286
1287
1288
1289 if ( !rm.getManagedTrees().contains( name2 ) )
1290 {
1291 rm.addBTree( name2, StringSerializer.INSTANCE, StringSerializer.INSTANCE, true );
1292 }
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305 BTree<String, String> btree2 = rm.getManagedTree( name2 );
1306
1307 for ( int i = 0; i < 1; i++ )
1308 {
1309 for ( int j = 0; j < 10; j++)
1310 {
1311 btree2.insert( Integer.toString( i ), Integer.toString( j ) );
1312 }
1313 }
1314
1315 rm.close();
1316
1317 MavibotInspector mi = new MavibotInspector( f );
1318 mi.start();
1319 }
1320 }
1321
1322
1323
1324
1325
1326 final class BtreeInfo<K, V>
1327 {
1328
1329 String btreeName;
1330
1331
1332 ElementSerializer<K> keySerializer;
1333
1334
1335 ElementSerializer<V> valueSerializer;
1336
1337 public String toString()
1338 {
1339 StringBuilder sb = new StringBuilder();
1340
1341 sb.append( "B-tree Info :" );
1342 sb.append( "\n name : " ).append( btreeName );
1343 sb.append( "\n key serializer : " ).append( keySerializer.getClass().getName() );
1344 sb.append( "\n value serializer : " ).append( valueSerializer.getClass().getName() );
1345
1346 return sb.toString();
1347 }
1348 }