001/* 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.io.hfile; 020 021 022import java.io.ByteArrayInputStream; 023import java.io.ByteArrayOutputStream; 024import java.io.DataInput; 025import java.io.DataInputStream; 026import java.io.DataOutputStream; 027import java.io.IOException; 028import java.nio.ByteBuffer; 029import org.apache.hadoop.fs.FSDataInputStream; 030import org.apache.hadoop.hbase.CellComparator; 031import org.apache.hadoop.hbase.CellComparatorImpl; 032import org.apache.hadoop.hbase.CellComparatorImpl.MetaCellComparator; 033import org.apache.hadoop.hbase.KeyValue; 034import org.apache.hadoop.hbase.io.compress.Compression; 035import org.apache.hadoop.hbase.util.Bytes; 036import org.apache.yetus.audience.InterfaceAudience; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; 040import org.apache.hadoop.hbase.shaded.protobuf.generated.HFileProtos; 041 042/** 043 * The {@link HFile} has a fixed trailer which contains offsets to other 044 * variable parts of the file. Also includes basic metadata on this file. The 045 * trailer size is fixed within a given {@link HFile} format version only, but 046 * we always store the version number as the last four-byte integer of the file. 047 * The version number itself is split into two portions, a major 048 * version and a minor version. The last three bytes of a file are the major 049 * version and a single preceding byte is the minor number. The major version 050 * determines which readers/writers to use to read/write a hfile while a minor 051 * version determines smaller changes in hfile format that do not need a new 052 * reader/writer type. 053 */ 054@InterfaceAudience.Private 055public class FixedFileTrailer { 056 private static final Logger LOG = LoggerFactory.getLogger(FixedFileTrailer.class); 057 058 /** 059 * We store the comparator class name as a fixed-length field in the trailer. 060 */ 061 private static final int MAX_COMPARATOR_NAME_LENGTH = 128; 062 063 /** 064 * Offset to the fileinfo data, a small block of vitals. Necessary in v1 but 065 * only potentially useful for pretty-printing in v2. 066 */ 067 private long fileInfoOffset; 068 069 /** 070 * In version 1, the offset to the data block index. Starting from version 2, 071 * the meaning of this field is the offset to the section of the file that 072 * should be loaded at the time the file is being opened: i.e. on open we load 073 * the root index, file info, etc. See http://hbase.apache.org/book.html#_hfile_format_2 074 * in the reference guide. 075 */ 076 private long loadOnOpenDataOffset; 077 078 /** 079 * The number of entries in the root data index. 080 */ 081 private int dataIndexCount; 082 083 /** 084 * Total uncompressed size of all blocks of the data index 085 */ 086 private long uncompressedDataIndexSize; 087 088 /** 089 * The number of entries in the meta index 090 */ 091 private int metaIndexCount; 092 093 /** 094 * The total uncompressed size of keys/values stored in the file. 095 */ 096 private long totalUncompressedBytes; 097 098 /** 099 * The number of key/value pairs in the file. This field was int in version 1, 100 * but is now long. 101 */ 102 private long entryCount; 103 104 /** 105 * The compression codec used for all blocks. 106 */ 107 private Compression.Algorithm compressionCodec = Compression.Algorithm.NONE; 108 109 /** 110 * The number of levels in the potentially multi-level data index. Used from 111 * version 2 onwards. 112 */ 113 private int numDataIndexLevels; 114 115 /** 116 * The offset of the first data block. 117 */ 118 private long firstDataBlockOffset; 119 120 /** 121 * It is guaranteed that no key/value data blocks start after this offset in 122 * the file. 123 */ 124 private long lastDataBlockOffset; 125 126 /** 127 * Raw key comparator class name in version 3 128 */ 129 // We could write the actual class name from 2.0 onwards and handle BC 130 private String comparatorClassName = CellComparator.getInstance().getClass().getName(); 131 132 /** 133 * The encryption key 134 */ 135 private byte[] encryptionKey; 136 137 /** 138 * The {@link HFile} format major version. 139 */ 140 private final int majorVersion; 141 142 /** 143 * The {@link HFile} format minor version. 144 */ 145 private final int minorVersion; 146 147 FixedFileTrailer(int majorVersion, int minorVersion) { 148 this.majorVersion = majorVersion; 149 this.minorVersion = minorVersion; 150 HFile.checkFormatVersion(majorVersion); 151 } 152 153 private static int[] computeTrailerSizeByVersion() { 154 int[] versionToSize = new int[HFile.MAX_FORMAT_VERSION + 1]; 155 // We support only 2 major versions now. ie. V2, V3 156 versionToSize[2] = 212; 157 for (int version = 3; version <= HFile.MAX_FORMAT_VERSION; version++) { 158 // Max FFT size for V3 and above is taken as 4KB for future enhancements 159 // if any. 160 // Unless the trailer size exceeds 4K this can continue 161 versionToSize[version] = 1024 * 4; 162 } 163 return versionToSize; 164 } 165 166 private static int getMaxTrailerSize() { 167 int maxSize = 0; 168 for (int version = HFile.MIN_FORMAT_VERSION; version <= HFile.MAX_FORMAT_VERSION; ++version) { 169 maxSize = Math.max(getTrailerSize(version), maxSize); 170 } 171 return maxSize; 172 } 173 174 private static final int[] TRAILER_SIZE = computeTrailerSizeByVersion(); 175 private static final int MAX_TRAILER_SIZE = getMaxTrailerSize(); 176 177 private static final int NOT_PB_SIZE = BlockType.MAGIC_LENGTH + Bytes.SIZEOF_INT; 178 179 static int getTrailerSize(int version) { 180 return TRAILER_SIZE[version]; 181 } 182 183 public int getTrailerSize() { 184 return getTrailerSize(majorVersion); 185 } 186 187 /** 188 * Write the trailer to a data stream. We support writing version 1 for 189 * testing and for determining version 1 trailer size. It is also easy to see 190 * what fields changed in version 2. 191 */ 192 void serialize(DataOutputStream outputStream) throws IOException { 193 HFile.checkFormatVersion(majorVersion); 194 195 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 196 DataOutputStream baosDos = new DataOutputStream(baos); 197 198 BlockType.TRAILER.write(baosDos); 199 serializeAsPB(baosDos); 200 201 // The last 4 bytes of the file encode the major and minor version universally 202 baosDos.writeInt(materializeVersion(majorVersion, minorVersion)); 203 204 baos.writeTo(outputStream); 205 } 206 207 @org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting 208 HFileProtos.FileTrailerProto toProtobuf() { 209 HFileProtos.FileTrailerProto.Builder builder = HFileProtos.FileTrailerProto.newBuilder() 210 .setFileInfoOffset(fileInfoOffset) 211 .setLoadOnOpenDataOffset(loadOnOpenDataOffset) 212 .setUncompressedDataIndexSize(uncompressedDataIndexSize) 213 .setTotalUncompressedBytes(totalUncompressedBytes) 214 .setDataIndexCount(dataIndexCount) 215 .setMetaIndexCount(metaIndexCount) 216 .setEntryCount(entryCount) 217 .setNumDataIndexLevels(numDataIndexLevels) 218 .setFirstDataBlockOffset(firstDataBlockOffset) 219 .setLastDataBlockOffset(lastDataBlockOffset) 220 .setComparatorClassName(getHBase1CompatibleName(comparatorClassName)) 221 .setCompressionCodec(compressionCodec.ordinal()); 222 if (encryptionKey != null) { 223 builder.setEncryptionKey(UnsafeByteOperations.unsafeWrap(encryptionKey)); 224 } 225 return builder.build(); 226 } 227 228 /** 229 * Write trailer data as protobuf. 230 * NOTE: we run a translation on the comparator name and will serialize the old hbase-1.x where 231 * it makes sense. See {@link #getHBase1CompatibleName(String)}. 232 */ 233 void serializeAsPB(DataOutputStream output) throws IOException { 234 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 235 // We need this extra copy unfortunately to determine the final size of the 236 // delimited output, see use of baos.size() below. 237 toProtobuf().writeDelimitedTo(baos); 238 baos.writeTo(output); 239 // Pad to make up the difference between variable PB encoding length and the 240 // length when encoded as writable under earlier V2 formats. Failure to pad 241 // properly or if the PB encoding is too big would mean the trailer wont be read 242 // in properly by HFile. 243 int padding = getTrailerSize() - NOT_PB_SIZE - baos.size(); 244 if (padding < 0) { 245 throw new IOException("Pbuf encoding size exceeded fixed trailer size limit"); 246 } 247 for (int i = 0; i < padding; i++) { 248 output.write(0); 249 } 250 } 251 252 /** 253 * Deserialize the fixed file trailer from the given stream. The version needs 254 * to already be specified. Make sure this is consistent with 255 * {@link #serialize(DataOutputStream)}. 256 */ 257 void deserialize(DataInputStream inputStream) throws IOException { 258 HFile.checkFormatVersion(majorVersion); 259 260 BlockType.TRAILER.readAndCheck(inputStream); 261 262 if (majorVersion > 2 263 || (majorVersion == 2 && minorVersion >= HFileReaderImpl.PBUF_TRAILER_MINOR_VERSION)) { 264 deserializeFromPB(inputStream); 265 } else { 266 deserializeFromWritable(inputStream); 267 } 268 269 // The last 4 bytes of the file encode the major and minor version universally 270 int version = inputStream.readInt(); 271 expectMajorVersion(extractMajorVersion(version)); 272 expectMinorVersion(extractMinorVersion(version)); 273 } 274 275 /** 276 * Deserialize the file trailer as protobuf 277 */ 278 void deserializeFromPB(DataInputStream inputStream) throws IOException { 279 // read PB and skip padding 280 int start = inputStream.available(); 281 HFileProtos.FileTrailerProto trailerProto = 282 HFileProtos.FileTrailerProto.PARSER.parseDelimitedFrom(inputStream); 283 int size = start - inputStream.available(); 284 inputStream.skip(getTrailerSize() - NOT_PB_SIZE - size); 285 286 // process the PB 287 if (trailerProto.hasFileInfoOffset()) { 288 fileInfoOffset = trailerProto.getFileInfoOffset(); 289 } 290 if (trailerProto.hasLoadOnOpenDataOffset()) { 291 loadOnOpenDataOffset = trailerProto.getLoadOnOpenDataOffset(); 292 } 293 if (trailerProto.hasUncompressedDataIndexSize()) { 294 uncompressedDataIndexSize = trailerProto.getUncompressedDataIndexSize(); 295 } 296 if (trailerProto.hasTotalUncompressedBytes()) { 297 totalUncompressedBytes = trailerProto.getTotalUncompressedBytes(); 298 } 299 if (trailerProto.hasDataIndexCount()) { 300 dataIndexCount = trailerProto.getDataIndexCount(); 301 } 302 if (trailerProto.hasMetaIndexCount()) { 303 metaIndexCount = trailerProto.getMetaIndexCount(); 304 } 305 if (trailerProto.hasEntryCount()) { 306 entryCount = trailerProto.getEntryCount(); 307 } 308 if (trailerProto.hasNumDataIndexLevels()) { 309 numDataIndexLevels = trailerProto.getNumDataIndexLevels(); 310 } 311 if (trailerProto.hasFirstDataBlockOffset()) { 312 firstDataBlockOffset = trailerProto.getFirstDataBlockOffset(); 313 } 314 if (trailerProto.hasLastDataBlockOffset()) { 315 lastDataBlockOffset = trailerProto.getLastDataBlockOffset(); 316 } 317 if (trailerProto.hasComparatorClassName()) { 318 setComparatorClass(getComparatorClass(trailerProto.getComparatorClassName())); 319 } 320 if (trailerProto.hasCompressionCodec()) { 321 compressionCodec = Compression.Algorithm.values()[trailerProto.getCompressionCodec()]; 322 } else { 323 compressionCodec = Compression.Algorithm.NONE; 324 } 325 if (trailerProto.hasEncryptionKey()) { 326 encryptionKey = trailerProto.getEncryptionKey().toByteArray(); 327 } 328 } 329 330 /** 331 * Deserialize the file trailer as writable data 332 */ 333 void deserializeFromWritable(DataInput input) throws IOException { 334 fileInfoOffset = input.readLong(); 335 loadOnOpenDataOffset = input.readLong(); 336 dataIndexCount = input.readInt(); 337 uncompressedDataIndexSize = input.readLong(); 338 metaIndexCount = input.readInt(); 339 340 totalUncompressedBytes = input.readLong(); 341 entryCount = input.readLong(); 342 compressionCodec = Compression.Algorithm.values()[input.readInt()]; 343 numDataIndexLevels = input.readInt(); 344 firstDataBlockOffset = input.readLong(); 345 lastDataBlockOffset = input.readLong(); 346 // TODO this is a classname encoded into an HFile's trailer. We are going to need to have 347 // some compat code here. 348 setComparatorClass(getComparatorClass(Bytes.readStringFixedSize(input, 349 MAX_COMPARATOR_NAME_LENGTH))); 350 } 351 352 private void append(StringBuilder sb, String s) { 353 if (sb.length() > 0) { 354 sb.append(", "); 355 } 356 sb.append(s); 357 } 358 359 @Override 360 public String toString() { 361 StringBuilder sb = new StringBuilder(); 362 append(sb, "fileinfoOffset=" + fileInfoOffset); 363 append(sb, "loadOnOpenDataOffset=" + loadOnOpenDataOffset); 364 append(sb, "dataIndexCount=" + dataIndexCount); 365 append(sb, "metaIndexCount=" + metaIndexCount); 366 append(sb, "totalUncomressedBytes=" + totalUncompressedBytes); 367 append(sb, "entryCount=" + entryCount); 368 append(sb, "compressionCodec=" + compressionCodec); 369 append(sb, "uncompressedDataIndexSize=" + uncompressedDataIndexSize); 370 append(sb, "numDataIndexLevels=" + numDataIndexLevels); 371 append(sb, "firstDataBlockOffset=" + firstDataBlockOffset); 372 append(sb, "lastDataBlockOffset=" + lastDataBlockOffset); 373 append(sb, "comparatorClassName=" + comparatorClassName); 374 if (majorVersion >= 3) { 375 append(sb, "encryptionKey=" + (encryptionKey != null ? "PRESENT" : "NONE")); 376 } 377 append(sb, "majorVersion=" + majorVersion); 378 append(sb, "minorVersion=" + minorVersion); 379 380 return sb.toString(); 381 } 382 383 /** 384 * Reads a file trailer from the given file. 385 * 386 * @param istream the input stream with the ability to seek. Does not have to 387 * be buffered, as only one read operation is made. 388 * @param fileSize the file size. Can be obtained using 389 * {@link org.apache.hadoop.fs.FileSystem#getFileStatus( 390 *org.apache.hadoop.fs.Path)}. 391 * @return the fixed file trailer read 392 * @throws IOException if failed to read from the underlying stream, or the 393 * trailer is corrupted, or the version of the trailer is 394 * unsupported 395 */ 396 public static FixedFileTrailer readFromStream(FSDataInputStream istream, 397 long fileSize) throws IOException { 398 int bufferSize = MAX_TRAILER_SIZE; 399 long seekPoint = fileSize - bufferSize; 400 if (seekPoint < 0) { 401 // It is hard to imagine such a small HFile. 402 seekPoint = 0; 403 bufferSize = (int) fileSize; 404 } 405 406 HFileUtil.seekOnMultipleSources(istream, seekPoint); 407 408 ByteBuffer buf = ByteBuffer.allocate(bufferSize); 409 istream.readFully(buf.array(), buf.arrayOffset(), 410 buf.arrayOffset() + buf.limit()); 411 412 // Read the version from the last int of the file. 413 buf.position(buf.limit() - Bytes.SIZEOF_INT); 414 int version = buf.getInt(); 415 416 // Extract the major and minor versions. 417 int majorVersion = extractMajorVersion(version); 418 int minorVersion = extractMinorVersion(version); 419 420 HFile.checkFormatVersion(majorVersion); // throws IAE if invalid 421 422 int trailerSize = getTrailerSize(majorVersion); 423 424 FixedFileTrailer fft = new FixedFileTrailer(majorVersion, minorVersion); 425 fft.deserialize(new DataInputStream(new ByteArrayInputStream(buf.array(), 426 buf.arrayOffset() + bufferSize - trailerSize, trailerSize))); 427 return fft; 428 } 429 430 public void expectMajorVersion(int expected) { 431 if (majorVersion != expected) { 432 throw new IllegalArgumentException("Invalid HFile major version: " 433 + majorVersion 434 + " (expected: " + expected + ")"); 435 } 436 } 437 438 public void expectMinorVersion(int expected) { 439 if (minorVersion != expected) { 440 throw new IllegalArgumentException("Invalid HFile minor version: " 441 + minorVersion + " (expected: " + expected + ")"); 442 } 443 } 444 445 public void expectAtLeastMajorVersion(int lowerBound) { 446 if (majorVersion < lowerBound) { 447 throw new IllegalArgumentException("Invalid HFile major version: " 448 + majorVersion 449 + " (expected: " + lowerBound + " or higher)."); 450 } 451 } 452 453 public long getFileInfoOffset() { 454 return fileInfoOffset; 455 } 456 457 public void setFileInfoOffset(long fileInfoOffset) { 458 this.fileInfoOffset = fileInfoOffset; 459 } 460 461 public long getLoadOnOpenDataOffset() { 462 return loadOnOpenDataOffset; 463 } 464 465 public void setLoadOnOpenOffset(long loadOnOpenDataOffset) { 466 this.loadOnOpenDataOffset = loadOnOpenDataOffset; 467 } 468 469 public int getDataIndexCount() { 470 return dataIndexCount; 471 } 472 473 public void setDataIndexCount(int dataIndexCount) { 474 this.dataIndexCount = dataIndexCount; 475 } 476 477 public int getMetaIndexCount() { 478 return metaIndexCount; 479 } 480 481 public void setMetaIndexCount(int metaIndexCount) { 482 this.metaIndexCount = metaIndexCount; 483 } 484 485 public long getTotalUncompressedBytes() { 486 return totalUncompressedBytes; 487 } 488 489 public void setTotalUncompressedBytes(long totalUncompressedBytes) { 490 this.totalUncompressedBytes = totalUncompressedBytes; 491 } 492 493 public long getEntryCount() { 494 return entryCount; 495 } 496 497 public void setEntryCount(long newEntryCount) { 498 entryCount = newEntryCount; 499 } 500 501 public Compression.Algorithm getCompressionCodec() { 502 return compressionCodec; 503 } 504 505 public void setCompressionCodec(Compression.Algorithm compressionCodec) { 506 this.compressionCodec = compressionCodec; 507 } 508 509 public int getNumDataIndexLevels() { 510 expectAtLeastMajorVersion(2); 511 return numDataIndexLevels; 512 } 513 514 public void setNumDataIndexLevels(int numDataIndexLevels) { 515 expectAtLeastMajorVersion(2); 516 this.numDataIndexLevels = numDataIndexLevels; 517 } 518 519 public long getLastDataBlockOffset() { 520 expectAtLeastMajorVersion(2); 521 return lastDataBlockOffset; 522 } 523 524 public void setLastDataBlockOffset(long lastDataBlockOffset) { 525 expectAtLeastMajorVersion(2); 526 this.lastDataBlockOffset = lastDataBlockOffset; 527 } 528 529 public long getFirstDataBlockOffset() { 530 expectAtLeastMajorVersion(2); 531 return firstDataBlockOffset; 532 } 533 534 public void setFirstDataBlockOffset(long firstDataBlockOffset) { 535 expectAtLeastMajorVersion(2); 536 this.firstDataBlockOffset = firstDataBlockOffset; 537 } 538 539 public String getComparatorClassName() { 540 return comparatorClassName; 541 } 542 543 /** 544 * Returns the major version of this HFile format 545 */ 546 public int getMajorVersion() { 547 return majorVersion; 548 } 549 550 /** 551 * Returns the minor version of this HFile format 552 */ 553 public int getMinorVersion() { 554 return minorVersion; 555 } 556 557 public void setComparatorClass(Class<? extends CellComparator> klass) { 558 // Is the comparator instantiable? 559 try { 560 // If null, it should be the Bytes.BYTES_RAWCOMPARATOR 561 if (klass != null) { 562 CellComparator comp = klass.getDeclaredConstructor().newInstance(); 563 // if the name wasn't one of the legacy names, maybe its a legit new 564 // kind of comparator. 565 this.comparatorClassName = klass.getName(); 566 } 567 } catch (Exception e) { 568 throw new RuntimeException("Comparator class " + klass.getName() + " is not instantiable", e); 569 } 570 } 571 572 /** 573 * If a 'standard' Comparator, write the old name for the Comparator when we serialize rather 574 * than the new name; writing the new name will make it so newly-written hfiles are not parseable 575 * by hbase-1.x, a facility we'd like to preserve across rolling upgrade and hbase-1.x clusters 576 * reading hbase-2.x produce. 577 * <p> 578 * The Comparators in hbase-2.x work the same as they did in hbase-1.x; they compare 579 * KeyValues. In hbase-2.x they were renamed making use of the more generic 'Cell' 580 * nomenclature to indicate that we intend to move away from KeyValues post hbase-2. A naming 581 * change is not reason enough to make it so hbase-1.x cannot read hbase-2.x files given the 582 * structure goes unchanged (hfile v3). So, lets write the old names for Comparators into the 583 * hfile tails in hbase-2. Here is where we do the translation. 584 * {@link #getComparatorClass(String)} does translation going the other way. 585 * 586 * <p>The translation is done on the serialized Protobuf only.</p> 587 * 588 * @param comparator String class name of the Comparator used in this hfile. 589 * @return What to store in the trailer as our comparator name. 590 * @see #getComparatorClass(String) 591 * @since hbase-2.0.0. 592 * @deprecated Since hbase-2.0.0. Will be removed in hbase-3.0.0. 593 */ 594 @Deprecated 595 private String getHBase1CompatibleName(final String comparator) { 596 if (comparator.equals(CellComparatorImpl.class.getName())) { 597 return KeyValue.COMPARATOR.getClass().getName(); 598 } 599 if (comparator.equals(MetaCellComparator.class.getName())) { 600 return KeyValue.META_COMPARATOR.getClass().getName(); 601 } 602 return comparator; 603 } 604 605 @SuppressWarnings("unchecked") 606 private static Class<? extends CellComparator> getComparatorClass(String comparatorClassName) 607 throws IOException { 608 Class<? extends CellComparator> comparatorKlass; 609 // for BC 610 if (comparatorClassName.equals(KeyValue.COMPARATOR.getLegacyKeyComparatorName()) 611 || comparatorClassName.equals(KeyValue.COMPARATOR.getClass().getName()) 612 || (comparatorClassName.equals("org.apache.hadoop.hbase.CellComparator"))) { 613 comparatorKlass = CellComparatorImpl.class; 614 } else if (comparatorClassName.equals(KeyValue.META_COMPARATOR.getLegacyKeyComparatorName()) 615 || comparatorClassName.equals(KeyValue.META_COMPARATOR.getClass().getName()) 616 || (comparatorClassName 617 .equals("org.apache.hadoop.hbase.CellComparator$MetaCellComparator"))) { 618 comparatorKlass = MetaCellComparator.class; 619 } else if (comparatorClassName.equals("org.apache.hadoop.hbase.KeyValue$RawBytesComparator") 620 || comparatorClassName.equals("org.apache.hadoop.hbase.util.Bytes$ByteArrayComparator")) { 621 // When the comparator to be used is Bytes.BYTES_RAWCOMPARATOR, we just return null from here 622 // Bytes.BYTES_RAWCOMPARATOR is not a CellComparator 623 comparatorKlass = null; 624 } else { 625 // if the name wasn't one of the legacy names, maybe its a legit new kind of comparator. 626 try { 627 comparatorKlass = (Class<? extends CellComparator>) Class.forName(comparatorClassName); 628 } catch (ClassNotFoundException e) { 629 throw new IOException(e); 630 } 631 } 632 return comparatorKlass; 633 } 634 635 static CellComparator createComparator(String comparatorClassName) throws IOException { 636 if (comparatorClassName.equals(CellComparatorImpl.COMPARATOR.getClass().getName())) { 637 return CellComparatorImpl.COMPARATOR; 638 } else if (comparatorClassName.equals( 639 CellComparatorImpl.META_COMPARATOR.getClass().getName())) { 640 return CellComparatorImpl.META_COMPARATOR; 641 } 642 try { 643 Class<? extends CellComparator> comparatorClass = getComparatorClass(comparatorClassName); 644 if (comparatorClass != null) { 645 return comparatorClass.getDeclaredConstructor().newInstance(); 646 } 647 LOG.warn("No Comparator class for " + comparatorClassName + ". Returning Null."); 648 return null; 649 } catch (Exception e) { 650 throw new IOException("Comparator class " + comparatorClassName + " is not instantiable", e); 651 } 652 } 653 654 CellComparator createComparator() throws IOException { 655 expectAtLeastMajorVersion(2); 656 return createComparator(comparatorClassName); 657 } 658 659 public long getUncompressedDataIndexSize() { 660 return uncompressedDataIndexSize; 661 } 662 663 public void setUncompressedDataIndexSize( 664 long uncompressedDataIndexSize) { 665 expectAtLeastMajorVersion(2); 666 this.uncompressedDataIndexSize = uncompressedDataIndexSize; 667 } 668 669 public byte[] getEncryptionKey() { 670 // This is a v3 feature but if reading a v2 file the encryptionKey will just be null which 671 // if fine for this feature. 672 expectAtLeastMajorVersion(2); 673 return encryptionKey; 674 } 675 676 public void setEncryptionKey(byte[] keyBytes) { 677 this.encryptionKey = keyBytes; 678 } 679 680 /** 681 * Extracts the major version for a 4-byte serialized version data. 682 * The major version is the 3 least significant bytes 683 */ 684 private static int extractMajorVersion(int serializedVersion) { 685 return (serializedVersion & 0x00ffffff); 686 } 687 688 /** 689 * Extracts the minor version for a 4-byte serialized version data. 690 * The major version are the 3 the most significant bytes 691 */ 692 private static int extractMinorVersion(int serializedVersion) { 693 return (serializedVersion >>> 24); 694 } 695 696 /** 697 * Create a 4 byte serialized version number by combining the 698 * minor and major version numbers. 699 */ 700 static int materializeVersion(int majorVersion, int minorVersion) { 701 return ((majorVersion & 0x00ffffff) | (minorVersion << 24)); 702 } 703}