1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.io.hfile;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.DataInput;
24 import java.io.DataInputStream;
25 import java.io.DataOutputStream;
26 import java.io.IOException;
27 import java.nio.ByteBuffer;
28
29 import org.apache.hadoop.hbase.util.ByteStringer;
30 import org.apache.hadoop.hbase.classification.InterfaceAudience;
31 import org.apache.hadoop.fs.FSDataInputStream;
32 import org.apache.hadoop.hbase.KeyValue;
33 import org.apache.hadoop.hbase.KeyValue.KVComparator;
34 import org.apache.hadoop.hbase.io.compress.Compression;
35 import org.apache.hadoop.hbase.protobuf.generated.HFileProtos;
36 import org.apache.hadoop.hbase.util.Bytes;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 @InterfaceAudience.Private
52 public class FixedFileTrailer {
53
54
55
56
57 private static final int MAX_COMPARATOR_NAME_LENGTH = 128;
58
59
60
61
62
63 private long fileInfoOffset;
64
65
66
67
68
69
70
71 private long loadOnOpenDataOffset;
72
73
74 private int dataIndexCount;
75
76
77 private long uncompressedDataIndexSize;
78
79
80 private int metaIndexCount;
81
82
83 private long totalUncompressedBytes;
84
85
86
87
88
89 private long entryCount;
90
91
92 private Compression.Algorithm compressionCodec = Compression.Algorithm.NONE;
93
94
95
96
97
98 private int numDataIndexLevels;
99
100
101 private long firstDataBlockOffset;
102
103
104
105
106
107 private long lastDataBlockOffset;
108
109
110 private String comparatorClassName = KeyValue.COMPARATOR.getLegacyKeyComparatorName();
111
112
113 private byte[] encryptionKey;
114
115
116 private final int majorVersion;
117
118
119 private final int minorVersion;
120
121 FixedFileTrailer(int majorVersion, int minorVersion) {
122 this.majorVersion = majorVersion;
123 this.minorVersion = minorVersion;
124 HFile.checkFormatVersion(majorVersion);
125 }
126
127 private static int[] computeTrailerSizeByVersion() {
128 int versionToSize[] = new int[HFile.MAX_FORMAT_VERSION + 1];
129
130 versionToSize[2] = 212;
131 for (int version = 3; version <= HFile.MAX_FORMAT_VERSION; version++) {
132
133
134
135 versionToSize[version] = 1024 * 4;
136 }
137 return versionToSize;
138 }
139
140 private static int getMaxTrailerSize() {
141 int maxSize = 0;
142 for (int version = HFile.MIN_FORMAT_VERSION;
143 version <= HFile.MAX_FORMAT_VERSION;
144 ++version)
145 maxSize = Math.max(getTrailerSize(version), maxSize);
146 return maxSize;
147 }
148
149 private static final int TRAILER_SIZE[] = computeTrailerSizeByVersion();
150 private static final int MAX_TRAILER_SIZE = getMaxTrailerSize();
151
152 private static final int NOT_PB_SIZE = BlockType.MAGIC_LENGTH + Bytes.SIZEOF_INT;
153
154 static int getTrailerSize(int version) {
155 return TRAILER_SIZE[version];
156 }
157
158 public int getTrailerSize() {
159 return getTrailerSize(majorVersion);
160 }
161
162
163
164
165
166
167
168
169
170 void serialize(DataOutputStream outputStream) throws IOException {
171 HFile.checkFormatVersion(majorVersion);
172
173 ByteArrayOutputStream baos = new ByteArrayOutputStream();
174 DataOutputStream baosDos = new DataOutputStream(baos);
175
176 BlockType.TRAILER.write(baosDos);
177 serializeAsPB(baosDos);
178
179
180 baosDos.writeInt(materializeVersion(majorVersion, minorVersion));
181
182 baos.writeTo(outputStream);
183 }
184
185
186
187
188
189
190 void serializeAsPB(DataOutputStream output) throws IOException {
191 ByteArrayOutputStream baos = new ByteArrayOutputStream();
192 HFileProtos.FileTrailerProto.Builder builder = HFileProtos.FileTrailerProto.newBuilder()
193 .setFileInfoOffset(fileInfoOffset)
194 .setLoadOnOpenDataOffset(loadOnOpenDataOffset)
195 .setUncompressedDataIndexSize(uncompressedDataIndexSize)
196 .setTotalUncompressedBytes(totalUncompressedBytes)
197 .setDataIndexCount(dataIndexCount)
198 .setMetaIndexCount(metaIndexCount)
199 .setEntryCount(entryCount)
200 .setNumDataIndexLevels(numDataIndexLevels)
201 .setFirstDataBlockOffset(firstDataBlockOffset)
202 .setLastDataBlockOffset(lastDataBlockOffset)
203
204
205 .setComparatorClassName(comparatorClassName)
206 .setCompressionCodec(compressionCodec.ordinal());
207 if (encryptionKey != null) {
208 builder.setEncryptionKey(ByteStringer.wrap(encryptionKey));
209 }
210
211
212 builder.build().writeDelimitedTo(baos);
213 baos.writeTo(output);
214
215
216
217
218 int padding = getTrailerSize() - NOT_PB_SIZE - baos.size();
219 if (padding < 0) {
220 throw new IOException("Pbuf encoding size exceeded fixed trailer size limit");
221 }
222 for (int i = 0; i < padding; i++) {
223 output.write(0);
224 }
225 }
226
227
228
229
230
231
232
233
234
235 void deserialize(DataInputStream inputStream) throws IOException {
236 HFile.checkFormatVersion(majorVersion);
237
238 BlockType.TRAILER.readAndCheck(inputStream);
239
240 if (majorVersion > 2
241 || (majorVersion == 2 && minorVersion >= HFileReaderV2.PBUF_TRAILER_MINOR_VERSION)) {
242 deserializeFromPB(inputStream);
243 } else {
244 deserializeFromWritable(inputStream);
245 }
246
247
248 int version = inputStream.readInt();
249 expectMajorVersion(extractMajorVersion(version));
250 expectMinorVersion(extractMinorVersion(version));
251 }
252
253
254
255
256
257
258 void deserializeFromPB(DataInputStream inputStream) throws IOException {
259
260 int start = inputStream.available();
261 HFileProtos.FileTrailerProto trailerProto =
262 HFileProtos.FileTrailerProto.PARSER.parseDelimitedFrom(inputStream);
263 int size = start - inputStream.available();
264 inputStream.skip(getTrailerSize() - NOT_PB_SIZE - size);
265
266
267 if (trailerProto.hasFileInfoOffset()) {
268 fileInfoOffset = trailerProto.getFileInfoOffset();
269 }
270 if (trailerProto.hasLoadOnOpenDataOffset()) {
271 loadOnOpenDataOffset = trailerProto.getLoadOnOpenDataOffset();
272 }
273 if (trailerProto.hasUncompressedDataIndexSize()) {
274 uncompressedDataIndexSize = trailerProto.getUncompressedDataIndexSize();
275 }
276 if (trailerProto.hasTotalUncompressedBytes()) {
277 totalUncompressedBytes = trailerProto.getTotalUncompressedBytes();
278 }
279 if (trailerProto.hasDataIndexCount()) {
280 dataIndexCount = trailerProto.getDataIndexCount();
281 }
282 if (trailerProto.hasMetaIndexCount()) {
283 metaIndexCount = trailerProto.getMetaIndexCount();
284 }
285 if (trailerProto.hasEntryCount()) {
286 entryCount = trailerProto.getEntryCount();
287 }
288 if (trailerProto.hasNumDataIndexLevels()) {
289 numDataIndexLevels = trailerProto.getNumDataIndexLevels();
290 }
291 if (trailerProto.hasFirstDataBlockOffset()) {
292 firstDataBlockOffset = trailerProto.getFirstDataBlockOffset();
293 }
294 if (trailerProto.hasLastDataBlockOffset()) {
295 lastDataBlockOffset = trailerProto.getLastDataBlockOffset();
296 }
297 if (trailerProto.hasComparatorClassName()) {
298
299
300 setComparatorClass(getComparatorClass(trailerProto.getComparatorClassName()));
301 }
302 if (trailerProto.hasCompressionCodec()) {
303 compressionCodec = Compression.Algorithm.values()[trailerProto.getCompressionCodec()];
304 } else {
305 compressionCodec = Compression.Algorithm.NONE;
306 }
307 if (trailerProto.hasEncryptionKey()) {
308 encryptionKey = trailerProto.getEncryptionKey().toByteArray();
309 }
310 }
311
312
313
314
315
316
317 void deserializeFromWritable(DataInput input) throws IOException {
318 fileInfoOffset = input.readLong();
319 loadOnOpenDataOffset = input.readLong();
320 dataIndexCount = input.readInt();
321 uncompressedDataIndexSize = input.readLong();
322 metaIndexCount = input.readInt();
323
324 totalUncompressedBytes = input.readLong();
325 entryCount = input.readLong();
326 compressionCodec = Compression.Algorithm.values()[input.readInt()];
327 numDataIndexLevels = input.readInt();
328 firstDataBlockOffset = input.readLong();
329 lastDataBlockOffset = input.readLong();
330
331
332 setComparatorClass(getComparatorClass(Bytes.readStringFixedSize(input,
333 MAX_COMPARATOR_NAME_LENGTH)));
334 }
335
336 private void append(StringBuilder sb, String s) {
337 if (sb.length() > 0)
338 sb.append(", ");
339 sb.append(s);
340 }
341
342 @Override
343 public String toString() {
344 StringBuilder sb = new StringBuilder();
345 append(sb, "fileinfoOffset=" + fileInfoOffset);
346 append(sb, "loadOnOpenDataOffset=" + loadOnOpenDataOffset);
347 append(sb, "dataIndexCount=" + dataIndexCount);
348 append(sb, "metaIndexCount=" + metaIndexCount);
349 append(sb, "totalUncomressedBytes=" + totalUncompressedBytes);
350 append(sb, "entryCount=" + entryCount);
351 append(sb, "compressionCodec=" + compressionCodec);
352 append(sb, "uncompressedDataIndexSize=" + uncompressedDataIndexSize);
353 append(sb, "numDataIndexLevels=" + numDataIndexLevels);
354 append(sb, "firstDataBlockOffset=" + firstDataBlockOffset);
355 append(sb, "lastDataBlockOffset=" + lastDataBlockOffset);
356 append(sb, "comparatorClassName=" + comparatorClassName);
357 if (majorVersion >= 3) {
358 append(sb, "encryptionKey=" + (encryptionKey != null ? "PRESENT" : "NONE"));
359 }
360 append(sb, "majorVersion=" + majorVersion);
361 append(sb, "minorVersion=" + minorVersion);
362
363 return sb.toString();
364 }
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379 public static FixedFileTrailer readFromStream(FSDataInputStream istream,
380 long fileSize) throws IOException {
381 int bufferSize = MAX_TRAILER_SIZE;
382 long seekPoint = fileSize - bufferSize;
383 if (seekPoint < 0) {
384
385 seekPoint = 0;
386 bufferSize = (int) fileSize;
387 }
388
389 istream.seek(seekPoint);
390 ByteBuffer buf = ByteBuffer.allocate(bufferSize);
391 istream.readFully(buf.array(), buf.arrayOffset(),
392 buf.arrayOffset() + buf.limit());
393
394
395 buf.position(buf.limit() - Bytes.SIZEOF_INT);
396 int version = buf.getInt();
397
398
399 int majorVersion = extractMajorVersion(version);
400 int minorVersion = extractMinorVersion(version);
401
402 HFile.checkFormatVersion(majorVersion);
403
404 int trailerSize = getTrailerSize(majorVersion);
405
406 FixedFileTrailer fft = new FixedFileTrailer(majorVersion, minorVersion);
407 fft.deserialize(new DataInputStream(new ByteArrayInputStream(buf.array(),
408 buf.arrayOffset() + bufferSize - trailerSize, trailerSize)));
409 return fft;
410 }
411
412 public void expectMajorVersion(int expected) {
413 if (majorVersion != expected) {
414 throw new IllegalArgumentException("Invalid HFile major version: "
415 + majorVersion
416 + " (expected: " + expected + ")");
417 }
418 }
419
420 public void expectMinorVersion(int expected) {
421 if (minorVersion != expected) {
422 throw new IllegalArgumentException("Invalid HFile minor version: "
423 + minorVersion + " (expected: " + expected + ")");
424 }
425 }
426
427 public void expectAtLeastMajorVersion(int lowerBound) {
428 if (majorVersion < lowerBound) {
429 throw new IllegalArgumentException("Invalid HFile major version: "
430 + majorVersion
431 + " (expected: " + lowerBound + " or higher).");
432 }
433 }
434
435 public long getFileInfoOffset() {
436 return fileInfoOffset;
437 }
438
439 public void setFileInfoOffset(long fileInfoOffset) {
440 this.fileInfoOffset = fileInfoOffset;
441 }
442
443 public long getLoadOnOpenDataOffset() {
444 return loadOnOpenDataOffset;
445 }
446
447 public void setLoadOnOpenOffset(long loadOnOpenDataOffset) {
448 this.loadOnOpenDataOffset = loadOnOpenDataOffset;
449 }
450
451 public int getDataIndexCount() {
452 return dataIndexCount;
453 }
454
455 public void setDataIndexCount(int dataIndexCount) {
456 this.dataIndexCount = dataIndexCount;
457 }
458
459 public int getMetaIndexCount() {
460 return metaIndexCount;
461 }
462
463 public void setMetaIndexCount(int metaIndexCount) {
464 this.metaIndexCount = metaIndexCount;
465 }
466
467 public long getTotalUncompressedBytes() {
468 return totalUncompressedBytes;
469 }
470
471 public void setTotalUncompressedBytes(long totalUncompressedBytes) {
472 this.totalUncompressedBytes = totalUncompressedBytes;
473 }
474
475 public long getEntryCount() {
476 return entryCount;
477 }
478
479 public void setEntryCount(long newEntryCount) {
480 entryCount = newEntryCount;
481 }
482
483 public Compression.Algorithm getCompressionCodec() {
484 return compressionCodec;
485 }
486
487 public void setCompressionCodec(Compression.Algorithm compressionCodec) {
488 this.compressionCodec = compressionCodec;
489 }
490
491 public int getNumDataIndexLevels() {
492 expectAtLeastMajorVersion(2);
493 return numDataIndexLevels;
494 }
495
496 public void setNumDataIndexLevels(int numDataIndexLevels) {
497 expectAtLeastMajorVersion(2);
498 this.numDataIndexLevels = numDataIndexLevels;
499 }
500
501 public long getLastDataBlockOffset() {
502 expectAtLeastMajorVersion(2);
503 return lastDataBlockOffset;
504 }
505
506 public void setLastDataBlockOffset(long lastDataBlockOffset) {
507 expectAtLeastMajorVersion(2);
508 this.lastDataBlockOffset = lastDataBlockOffset;
509 }
510
511 public long getFirstDataBlockOffset() {
512 expectAtLeastMajorVersion(2);
513 return firstDataBlockOffset;
514 }
515
516 public void setFirstDataBlockOffset(long firstDataBlockOffset) {
517 expectAtLeastMajorVersion(2);
518 this.firstDataBlockOffset = firstDataBlockOffset;
519 }
520
521 public String getComparatorClassName() {
522 return comparatorClassName;
523 }
524
525
526
527
528 public int getMajorVersion() {
529 return majorVersion;
530 }
531
532
533
534
535 public int getMinorVersion() {
536 return minorVersion;
537 }
538
539 public void setComparatorClass(Class<? extends KVComparator> klass) {
540
541 try {
542 KVComparator comp = klass.newInstance();
543
544
545 if (KeyValue.COMPARATOR.getClass().equals(klass)) {
546 comparatorClassName = KeyValue.COMPARATOR.getLegacyKeyComparatorName();
547 } else if (KeyValue.META_COMPARATOR.getClass().equals(klass)) {
548 comparatorClassName = KeyValue.META_COMPARATOR.getLegacyKeyComparatorName();
549 } else if (KeyValue.RAW_COMPARATOR.getClass().equals(klass)) {
550 comparatorClassName = KeyValue.RAW_COMPARATOR.getLegacyKeyComparatorName();
551 } else {
552
553 comparatorClassName = klass.getName();
554 }
555
556 } catch (Exception e) {
557 throw new RuntimeException("Comparator class " + klass.getName() +
558 " is not instantiable", e);
559 }
560
561 }
562
563 @SuppressWarnings("unchecked")
564 private static Class<? extends KVComparator> getComparatorClass(
565 String comparatorClassName) throws IOException {
566 try {
567
568 if (comparatorClassName.equals(KeyValue.COMPARATOR.getLegacyKeyComparatorName())) {
569 comparatorClassName = KeyValue.COMPARATOR.getClass().getName();
570 } else if (comparatorClassName.equals(KeyValue.META_COMPARATOR.getLegacyKeyComparatorName())) {
571 comparatorClassName = KeyValue.META_COMPARATOR.getClass().getName();
572 } else if (comparatorClassName.equals(KeyValue.RAW_COMPARATOR.getLegacyKeyComparatorName())) {
573 comparatorClassName = KeyValue.RAW_COMPARATOR.getClass().getName();
574 } else if (comparatorClassName.equals("org.apache.hadoop.hbase.CellComparator")) {
575
576
577 comparatorClassName = KeyValue.COMPARATOR.getClass().getName();
578 } else if ((comparatorClassName
579 .equals("org.apache.hadoop.hbase.CellComparator$MetaCellComparator"))) {
580
581 comparatorClassName = KeyValue.META_COMPARATOR.getClass().getName();
582 }
583
584
585 return (Class<? extends KVComparator>)
586 Class.forName(comparatorClassName);
587 } catch (ClassNotFoundException ex) {
588 throw new IOException(ex);
589 }
590 }
591
592 public static KVComparator createComparator(
593 String comparatorClassName) throws IOException {
594 try {
595 return getComparatorClass(comparatorClassName).newInstance();
596 } catch (InstantiationException e) {
597 throw new IOException("Comparator class " + comparatorClassName +
598 " is not instantiable", e);
599 } catch (IllegalAccessException e) {
600 throw new IOException("Comparator class " + comparatorClassName +
601 " is not instantiable", e);
602 }
603 }
604
605 KVComparator createComparator() throws IOException {
606 expectAtLeastMajorVersion(2);
607 return createComparator(comparatorClassName);
608 }
609
610 public long getUncompressedDataIndexSize() {
611 return uncompressedDataIndexSize;
612 }
613
614 public void setUncompressedDataIndexSize(
615 long uncompressedDataIndexSize) {
616 expectAtLeastMajorVersion(2);
617 this.uncompressedDataIndexSize = uncompressedDataIndexSize;
618 }
619
620 public byte[] getEncryptionKey() {
621 expectAtLeastMajorVersion(3);
622 return encryptionKey;
623 }
624
625 public void setEncryptionKey(byte[] keyBytes) {
626 this.encryptionKey = keyBytes;
627 }
628
629
630
631
632
633 private static int extractMajorVersion(int serializedVersion) {
634 return (serializedVersion & 0x00ffffff);
635 }
636
637
638
639
640
641 private static int extractMinorVersion(int serializedVersion) {
642 return (serializedVersion >>> 24);
643 }
644
645
646
647
648
649 static int materializeVersion(int majorVersion, int minorVersion) {
650 return ((majorVersion & 0x00ffffff) | (minorVersion << 24));
651 }
652 }