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