1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase;
21
22 import java.io.DataInput;
23 import java.io.DataOutput;
24 import java.io.EOFException;
25 import java.io.IOException;
26 import java.util.Arrays;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.fs.FileSystem;
32 import org.apache.hadoop.fs.Path;
33 import org.apache.hadoop.hbase.KeyValue.KVComparator;
34 import org.apache.hadoop.hbase.migration.HRegionInfo090x;
35 import org.apache.hadoop.hbase.util.Bytes;
36 import org.apache.hadoop.hbase.util.FSTableDescriptors;
37 import org.apache.hadoop.hbase.util.JenkinsHash;
38 import org.apache.hadoop.hbase.util.MD5Hash;
39 import org.apache.hadoop.io.VersionedWritable;
40 import org.apache.hadoop.io.WritableComparable;
41
42
43
44
45
46
47 public class HRegionInfo extends VersionedWritable
48 implements WritableComparable<HRegionInfo> {
49
50 public static final byte VERSION_PRE_092 = 0;
51 public static final byte VERSION = 1;
52 private static final Log LOG = LogFactory.getLog(HRegionInfo.class);
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 private static final int ENC_SEPARATOR = '.';
81 public static final int MD5_HEX_LENGTH = 32;
82
83
84
85
86
87
88
89 private static boolean hasEncodedName(final byte[] regionName) {
90
91 if ((regionName.length >= 1)
92 && (regionName[regionName.length - 1] == ENC_SEPARATOR)) {
93
94 return true;
95 }
96 return false;
97 }
98
99
100
101
102
103 public static String encodeRegionName(final byte [] regionName) {
104 String encodedName;
105 if (hasEncodedName(regionName)) {
106
107
108 encodedName = Bytes.toString(regionName,
109 regionName.length - MD5_HEX_LENGTH - 1,
110 MD5_HEX_LENGTH);
111 } else {
112
113
114 int hashVal = Math.abs(JenkinsHash.getInstance().hash(regionName,
115 regionName.length, 0));
116 encodedName = String.valueOf(hashVal);
117 }
118 return encodedName;
119 }
120
121
122
123
124
125
126
127
128 public static String prettyPrint(final String encodedRegionName) {
129 if (encodedRegionName.equals("70236052")) {
130 return encodedRegionName + "/-ROOT-";
131 } else if (encodedRegionName.equals("1028785192")) {
132 return encodedRegionName + "/.META.";
133 }
134 return encodedRegionName;
135 }
136
137
138 public static final int DELIMITER = ',';
139
140
141 public static final HRegionInfo ROOT_REGIONINFO =
142 new HRegionInfo(0L, Bytes.toBytes("-ROOT-"));
143
144
145 public static final HRegionInfo FIRST_META_REGIONINFO =
146 new HRegionInfo(1L, Bytes.toBytes(".META."));
147
148 private byte [] endKey = HConstants.EMPTY_BYTE_ARRAY;
149
150
151
152 private boolean offLine = false;
153 private long regionId = -1;
154 private transient byte [] regionName = HConstants.EMPTY_BYTE_ARRAY;
155 private String regionNameStr = "";
156 private boolean split = false;
157 private byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
158 private int hashCode = -1;
159
160 public static final String NO_HASH = null;
161 private volatile String encodedName = NO_HASH;
162 private byte [] encodedNameAsBytes = null;
163
164
165 private byte[] tableName = null;
166
167 private void setHashCode() {
168 int result = Arrays.hashCode(this.regionName);
169 result ^= this.regionId;
170 result ^= Arrays.hashCode(this.startKey);
171 result ^= Arrays.hashCode(this.endKey);
172 result ^= Boolean.valueOf(this.offLine).hashCode();
173 result ^= Arrays.hashCode(this.tableName);
174 this.hashCode = result;
175 }
176
177
178
179
180
181
182 private HRegionInfo(long regionId, byte[] tableName) {
183 super();
184 this.regionId = regionId;
185 this.tableName = tableName.clone();
186
187 this.regionName = createRegionName(tableName, null,
188 regionId, false);
189 this.regionNameStr = Bytes.toStringBinary(this.regionName);
190 setHashCode();
191 }
192
193
194 public HRegionInfo() {
195 super();
196 }
197
198
199
200
201
202 public HRegionInfo(HRegionInfo090x other) {
203 super();
204 this.endKey = other.getEndKey();
205 this.offLine = other.isOffline();
206 this.regionId = other.getRegionId();
207 this.regionName = other.getRegionName();
208 this.regionNameStr = Bytes.toStringBinary(this.regionName);
209 this.split = other.isSplit();
210 this.startKey = other.getStartKey();
211 this.hashCode = other.hashCode();
212 this.encodedName = other.getEncodedName();
213 this.tableName = other.getTableDesc().getName();
214 }
215
216 public HRegionInfo(final byte[] tableName) {
217 this(tableName, null, null);
218 }
219
220
221
222
223
224
225
226
227
228 public HRegionInfo(final byte[] tableName, final byte[] startKey,
229 final byte[] endKey)
230 throws IllegalArgumentException {
231 this(tableName, startKey, endKey, false);
232 }
233
234
235
236
237
238
239
240
241
242
243
244
245 public HRegionInfo(final byte[] tableName, final byte[] startKey,
246 final byte[] endKey, final boolean split)
247 throws IllegalArgumentException {
248 this(tableName, startKey, endKey, split, System.currentTimeMillis());
249 }
250
251
252
253
254
255
256
257
258
259
260
261
262
263 public HRegionInfo(final byte[] tableName, final byte[] startKey,
264 final byte[] endKey, final boolean split, final long regionid)
265 throws IllegalArgumentException {
266
267 super();
268 if (tableName == null) {
269 throw new IllegalArgumentException("tableName cannot be null");
270 }
271 this.tableName = tableName.clone();
272 this.offLine = false;
273 this.regionId = regionid;
274
275 this.regionName = createRegionName(this.tableName, startKey, regionId, true);
276
277 this.regionNameStr = Bytes.toStringBinary(this.regionName);
278 this.split = split;
279 this.endKey = endKey == null? HConstants.EMPTY_END_ROW: endKey.clone();
280 this.startKey = startKey == null?
281 HConstants.EMPTY_START_ROW: startKey.clone();
282 this.tableName = tableName.clone();
283 setHashCode();
284 }
285
286
287
288
289
290
291 public HRegionInfo(HRegionInfo other) {
292 super();
293 this.endKey = other.getEndKey();
294 this.offLine = other.isOffline();
295 this.regionId = other.getRegionId();
296 this.regionName = other.getRegionName();
297 this.regionNameStr = Bytes.toStringBinary(this.regionName);
298 this.split = other.isSplit();
299 this.startKey = other.getStartKey();
300 this.hashCode = other.hashCode();
301 this.encodedName = other.getEncodedName();
302 this.tableName = other.tableName;
303 }
304
305
306
307
308
309
310
311
312
313
314
315 public static byte [] createRegionName(final byte [] tableName,
316 final byte [] startKey, final long regionid, boolean newFormat) {
317 return createRegionName(tableName, startKey, Long.toString(regionid), newFormat);
318 }
319
320
321
322
323
324
325
326
327
328
329 public static byte [] createRegionName(final byte [] tableName,
330 final byte [] startKey, final String id, boolean newFormat) {
331 return createRegionName(tableName, startKey, Bytes.toBytes(id), newFormat);
332 }
333
334
335
336
337
338
339
340
341
342
343 public static byte [] createRegionName(final byte [] tableName,
344 final byte [] startKey, final byte [] id, boolean newFormat) {
345 byte [] b = new byte [tableName.length + 2 + id.length +
346 (startKey == null? 0: startKey.length) +
347 (newFormat ? (MD5_HEX_LENGTH + 2) : 0)];
348
349 int offset = tableName.length;
350 System.arraycopy(tableName, 0, b, 0, offset);
351 b[offset++] = DELIMITER;
352 if (startKey != null && startKey.length > 0) {
353 System.arraycopy(startKey, 0, b, offset, startKey.length);
354 offset += startKey.length;
355 }
356 b[offset++] = DELIMITER;
357 System.arraycopy(id, 0, b, offset, id.length);
358 offset += id.length;
359
360 if (newFormat) {
361
362
363
364
365
366
367
368 String md5Hash = MD5Hash.getMD5AsHex(b, 0, offset);
369 byte [] md5HashBytes = Bytes.toBytes(md5Hash);
370
371 if (md5HashBytes.length != MD5_HEX_LENGTH) {
372 LOG.error("MD5-hash length mismatch: Expected=" + MD5_HEX_LENGTH +
373 "; Got=" + md5HashBytes.length);
374 }
375
376
377 b[offset++] = ENC_SEPARATOR;
378 System.arraycopy(md5HashBytes, 0, b, offset, MD5_HEX_LENGTH);
379 offset += MD5_HEX_LENGTH;
380 b[offset++] = ENC_SEPARATOR;
381 }
382
383 return b;
384 }
385
386
387
388
389
390
391 public static byte [] getTableName(byte [] regionName) {
392 int offset = -1;
393 for (int i = 0; i < regionName.length; i++) {
394 if (regionName[i] == DELIMITER) {
395 offset = i;
396 break;
397 }
398 }
399 byte [] tableName = new byte[offset];
400 System.arraycopy(regionName, 0, tableName, 0, offset);
401 return tableName;
402 }
403
404
405
406
407
408
409
410 public static byte [][] parseRegionName(final byte [] regionName)
411 throws IOException {
412 int offset = -1;
413 for (int i = 0; i < regionName.length; i++) {
414 if (regionName[i] == DELIMITER) {
415 offset = i;
416 break;
417 }
418 }
419 if(offset == -1) throw new IOException("Invalid regionName format");
420 byte [] tableName = new byte[offset];
421 System.arraycopy(regionName, 0, tableName, 0, offset);
422 offset = -1;
423 for (int i = regionName.length - 1; i > 0; i--) {
424 if(regionName[i] == DELIMITER) {
425 offset = i;
426 break;
427 }
428 }
429 if(offset == -1) throw new IOException("Invalid regionName format");
430 byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
431 if(offset != tableName.length + 1) {
432 startKey = new byte[offset - tableName.length - 1];
433 System.arraycopy(regionName, tableName.length + 1, startKey, 0,
434 offset - tableName.length - 1);
435 }
436 byte [] id = new byte[regionName.length - offset - 1];
437 System.arraycopy(regionName, offset + 1, id, 0,
438 regionName.length - offset - 1);
439 byte [][] elements = new byte[3][];
440 elements[0] = tableName;
441 elements[1] = startKey;
442 elements[2] = id;
443 return elements;
444 }
445
446
447 public long getRegionId(){
448 return regionId;
449 }
450
451
452
453
454
455 public byte [] getRegionName(){
456 return regionName;
457 }
458
459
460
461
462 public String getRegionNameAsString() {
463 if (hasEncodedName(this.regionName)) {
464
465 return this.regionNameStr;
466 }
467
468
469
470
471 return this.regionNameStr + "." + this.getEncodedName();
472 }
473
474
475 public synchronized String getEncodedName() {
476 if (this.encodedName == NO_HASH) {
477 this.encodedName = encodeRegionName(this.regionName);
478 }
479 return this.encodedName;
480 }
481
482 public synchronized byte [] getEncodedNameAsBytes() {
483 if (this.encodedNameAsBytes == null) {
484 this.encodedNameAsBytes = Bytes.toBytes(getEncodedName());
485 }
486 return this.encodedNameAsBytes;
487 }
488
489
490 public byte [] getStartKey(){
491 return startKey;
492 }
493
494
495 public byte [] getEndKey(){
496 return endKey;
497 }
498
499
500
501
502
503 public byte[] getTableName() {
504 if (tableName == null || tableName.length == 0) {
505 tableName = getTableName(getRegionName());
506 }
507 return tableName;
508 }
509
510
511
512
513
514 public String getTableNameAsString() {
515 return Bytes.toString(tableName);
516 }
517
518
519
520
521
522
523
524
525 public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) {
526 if (Bytes.compareTo(rangeStartKey, rangeEndKey) > 0) {
527 throw new IllegalArgumentException(
528 "Invalid range: " + Bytes.toStringBinary(rangeStartKey) +
529 " > " + Bytes.toStringBinary(rangeEndKey));
530 }
531
532 boolean firstKeyInRange = Bytes.compareTo(rangeStartKey, startKey) >= 0;
533 boolean lastKeyInRange =
534 Bytes.compareTo(rangeEndKey, endKey) < 0 ||
535 Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY);
536 return firstKeyInRange && lastKeyInRange;
537 }
538
539
540
541
542 public boolean containsRow(byte[] row) {
543 return Bytes.compareTo(row, startKey) >= 0 &&
544 (Bytes.compareTo(row, endKey) < 0 ||
545 Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY));
546 }
547
548
549
550
551
552
553
554 @Deprecated
555 public HTableDescriptor getTableDesc() {
556 Configuration c = HBaseConfiguration.create();
557 FileSystem fs;
558 try {
559 fs = FileSystem.get(c);
560 } catch (IOException e) {
561 throw new RuntimeException(e);
562 }
563 FSTableDescriptors fstd =
564 new FSTableDescriptors(fs, new Path(c.get(HConstants.HBASE_DIR)));
565 try {
566 return fstd.get(this.tableName);
567 } catch (IOException e) {
568 throw new RuntimeException(e);
569 }
570 }
571
572
573
574
575
576 @Deprecated
577 public void setTableDesc(HTableDescriptor newDesc) {
578 Configuration c = HBaseConfiguration.create();
579 FileSystem fs;
580 try {
581 fs = FileSystem.get(c);
582 } catch (IOException e) {
583 throw new RuntimeException(e);
584 }
585 FSTableDescriptors fstd =
586 new FSTableDescriptors(fs, new Path(c.get(HConstants.HBASE_DIR)));
587 try {
588 fstd.add(newDesc);
589 } catch (IOException e) {
590 throw new RuntimeException(e);
591 }
592 }
593
594
595 public boolean isRootRegion() {
596 return Bytes.equals(tableName, HRegionInfo.ROOT_REGIONINFO.getTableName());
597 }
598
599
600
601
602 public boolean isMetaTable() {
603 return isRootRegion() || isMetaRegion();
604 }
605
606
607 public boolean isMetaRegion() {
608 return Bytes.equals(tableName, HRegionInfo.FIRST_META_REGIONINFO.getTableName());
609 }
610
611
612
613
614 public boolean isSplit() {
615 return this.split;
616 }
617
618
619
620
621 public void setSplit(boolean split) {
622 this.split = split;
623 }
624
625
626
627
628 public boolean isOffline() {
629 return this.offLine;
630 }
631
632
633
634
635
636
637 public void setOffline(boolean offLine) {
638 this.offLine = offLine;
639 }
640
641
642
643
644
645 public boolean isSplitParent() {
646 if (!isSplit()) return false;
647 if (!isOffline()) {
648 LOG.warn("Region is split but NOT offline: " + getRegionNameAsString());
649 }
650 return true;
651 }
652
653
654
655
656 @Override
657 public String toString() {
658 return "{" + HConstants.NAME + " => '" +
659 this.regionNameStr
660 + "', STARTKEY => '" +
661 Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" +
662 Bytes.toStringBinary(this.endKey) +
663 "', ENCODED => " + getEncodedName() + "," +
664 (isOffline()? " OFFLINE => true,": "") +
665 (isSplit()? " SPLIT => true,": "") + "}";
666 }
667
668
669
670
671 @Override
672 public boolean equals(Object o) {
673 if (this == o) {
674 return true;
675 }
676 if (o == null) {
677 return false;
678 }
679 if (!(o instanceof HRegionInfo)) {
680 return false;
681 }
682 return this.compareTo((HRegionInfo)o) == 0;
683 }
684
685
686
687
688 @Override
689 public int hashCode() {
690 return this.hashCode;
691 }
692
693
694 @Override
695 public byte getVersion() {
696 return VERSION;
697 }
698
699
700
701
702
703 @Override
704 public void write(DataOutput out) throws IOException {
705 super.write(out);
706 Bytes.writeByteArray(out, endKey);
707 out.writeBoolean(offLine);
708 out.writeLong(regionId);
709 Bytes.writeByteArray(out, regionName);
710 out.writeBoolean(split);
711 Bytes.writeByteArray(out, startKey);
712 Bytes.writeByteArray(out, tableName);
713 out.writeInt(hashCode);
714 }
715
716 @Override
717 public void readFields(DataInput in) throws IOException {
718
719
720
721 byte version = in.readByte();
722 if (version == 0) {
723
724
725 this.endKey = Bytes.readByteArray(in);
726 this.offLine = in.readBoolean();
727 this.regionId = in.readLong();
728 this.regionName = Bytes.readByteArray(in);
729 this.regionNameStr = Bytes.toStringBinary(this.regionName);
730 this.split = in.readBoolean();
731 this.startKey = Bytes.readByteArray(in);
732 try {
733 HTableDescriptor htd = new HTableDescriptor();
734 htd.readFields(in);
735 this.tableName = htd.getName();
736 } catch(EOFException eofe) {
737 throw new IOException("HTD not found in input buffer", eofe);
738 }
739 this.hashCode = in.readInt();
740 } else if (getVersion() == VERSION) {
741 this.endKey = Bytes.readByteArray(in);
742 this.offLine = in.readBoolean();
743 this.regionId = in.readLong();
744 this.regionName = Bytes.readByteArray(in);
745 this.regionNameStr = Bytes.toStringBinary(this.regionName);
746 this.split = in.readBoolean();
747 this.startKey = Bytes.readByteArray(in);
748 this.tableName = Bytes.readByteArray(in);
749 this.hashCode = in.readInt();
750 } else {
751 throw new IOException("Non-migratable/unknown version=" + getVersion());
752 }
753 }
754
755
756
757
758
759 public int compareTo(HRegionInfo o) {
760 if (o == null) {
761 return 1;
762 }
763
764
765 int result = Bytes.compareTo(this.tableName, o.tableName);
766 if (result != 0) {
767 return result;
768 }
769
770
771 result = Bytes.compareTo(this.startKey, o.startKey);
772 if (result != 0) {
773 return result;
774 }
775
776
777 result = Bytes.compareTo(this.endKey, o.endKey);
778 if (result != 0) {
779 return result;
780 }
781 if (this.offLine == o.offLine)
782 return 0;
783 if (this.offLine == true) return -1;
784
785 return 1;
786 }
787
788
789
790
791 public KVComparator getComparator() {
792 return isRootRegion()? KeyValue.ROOT_COMPARATOR: isMetaRegion()?
793 KeyValue.META_COMPARATOR: KeyValue.COMPARATOR;
794 }
795 }