View Javadoc

1   /**
2    * Copyright 2007 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
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   * HRegion information.
44   * Contains HRegion id, start and end keys, a reference to this
45   * HRegions' table descriptor, etc.
46   */
47  public class HRegionInfo extends VersionedWritable
48  implements WritableComparable<HRegionInfo> {
49    // VERSION == 0 when HRegionInfo had an HTableDescriptor inside it.
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     * The new format for a region name contains its encodedName at the end.
56     * The encoded name also serves as the directory name for the region
57     * in the filesystem.
58     *
59     * New region name format:
60     *    &lt;tablename>,,&lt;startkey>,&lt;regionIdTimestamp>.&lt;encodedName>.
61     * where,
62     *    &lt;encodedName> is a hex version of the MD5 hash of
63     *    &lt;tablename>,&lt;startkey>,&lt;regionIdTimestamp>
64     * 
65     * The old region name format:
66     *    &lt;tablename>,&lt;startkey>,&lt;regionIdTimestamp>
67     * For region names in the old format, the encoded name is a 32-bit
68     * JenkinsHash integer value (in its decimal notation, string form). 
69     *<p>
70     * **NOTE**
71     *
72     * ROOT, the first META region, and regions created by an older
73     * version of HBase (0.20 or prior) will continue to use the
74     * old region name format.
75     */
76  
77    /** Separator used to demarcate the encodedName in a region name
78     * in the new format. See description on new format above. 
79     */ 
80    private static final int ENC_SEPARATOR = '.';
81    public  static final int MD5_HEX_LENGTH   = 32;
82  
83    /** A non-capture group so that this can be embedded. */
84    public static final String ENCODED_REGION_NAME_REGEX = "(?:[a-f0-9]+)";
85  
86    /**
87     * Does region name contain its encoded name?
88     * @param regionName region name
89     * @return boolean indicating if this a new format region
90     *         name which contains its encoded name.
91     */
92    private static boolean hasEncodedName(final byte[] regionName) {
93      // check if region name ends in ENC_SEPARATOR
94      if ((regionName.length >= 1)
95          && (regionName[regionName.length - 1] == ENC_SEPARATOR)) {
96        // region name is new format. it contains the encoded name.
97        return true; 
98      }
99      return false;
100   }
101   
102   /**
103    * @param regionName
104    * @return the encodedName
105    */
106   public static String encodeRegionName(final byte [] regionName) {
107     String encodedName;
108     if (hasEncodedName(regionName)) {
109       // region is in new format:
110       // <tableName>,<startKey>,<regionIdTimeStamp>/encodedName/
111       encodedName = Bytes.toString(regionName,
112           regionName.length - MD5_HEX_LENGTH - 1,
113           MD5_HEX_LENGTH);
114     } else {
115       // old format region name. ROOT and first META region also 
116       // use this format.EncodedName is the JenkinsHash value.
117       int hashVal = Math.abs(JenkinsHash.getInstance().hash(regionName,
118         regionName.length, 0));
119       encodedName = String.valueOf(hashVal);
120     }
121     return encodedName;
122   }
123 
124   /**
125    * Use logging.
126    * @param encodedRegionName The encoded regionname.
127    * @return <code>-ROOT-</code> if passed <code>70236052</code> or
128    * <code>.META.</code> if passed </code>1028785192</code> else returns
129    * <code>encodedRegionName</code>
130    */
131   public static String prettyPrint(final String encodedRegionName) {
132     if (encodedRegionName.equals("70236052")) {
133       return encodedRegionName + "/-ROOT-";
134     } else if (encodedRegionName.equals("1028785192")) {
135       return encodedRegionName + "/.META.";
136     }
137     return encodedRegionName;
138   }
139 
140   /** delimiter used between portions of a region name */
141   public static final int DELIMITER = ',';
142 
143   /** HRegionInfo for root region */
144   public static final HRegionInfo ROOT_REGIONINFO =
145     new HRegionInfo(0L, Bytes.toBytes("-ROOT-"));
146 
147   /** HRegionInfo for first meta region */
148   public static final HRegionInfo FIRST_META_REGIONINFO =
149     new HRegionInfo(1L, Bytes.toBytes(".META."));
150 
151   private byte [] endKey = HConstants.EMPTY_BYTE_ARRAY;
152   // This flag is in the parent of a split while the parent is still referenced
153   // by daughter regions.  We USED to set this flag when we disabled a table
154   // but now table state is kept up in zookeeper as of 0.90.0 HBase.
155   private boolean offLine = false;
156   private long regionId = -1;
157   private transient byte [] regionName = HConstants.EMPTY_BYTE_ARRAY;
158   private String regionNameStr = "";
159   private boolean split = false;
160   private byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
161   private int hashCode = -1;
162   //TODO: Move NO_HASH to HStoreFile which is really the only place it is used.
163   public static final String NO_HASH = null;
164   private volatile String encodedName = NO_HASH;
165   private byte [] encodedNameAsBytes = null;
166 
167   // Current TableName
168   private byte[] tableName = null;
169 
170   private void setHashCode() {
171     int result = Arrays.hashCode(this.regionName);
172     result ^= this.regionId;
173     result ^= Arrays.hashCode(this.startKey);
174     result ^= Arrays.hashCode(this.endKey);
175     result ^= Boolean.valueOf(this.offLine).hashCode();
176     result ^= Arrays.hashCode(this.tableName);
177     this.hashCode = result;
178   }
179 
180 
181   /**
182    * Private constructor used constructing HRegionInfo for the catalog root and
183    * first meta regions
184    */
185   private HRegionInfo(long regionId, byte[] tableName) {
186     super();
187     this.regionId = regionId;
188     this.tableName = tableName.clone();
189     // Note: Root & First Meta regions names are still in old format
190     this.regionName = createRegionName(tableName, null,
191                                        regionId, false);
192     this.regionNameStr = Bytes.toStringBinary(this.regionName);
193     setHashCode();
194   }
195 
196   /** Default constructor - creates empty object */
197   public HRegionInfo() {
198     super();
199   }
200 
201   /**
202    * Used only for migration
203    * @param other HRegionInfoForMigration
204    */
205   public HRegionInfo(HRegionInfo090x other) {
206     super();
207     this.endKey = other.getEndKey();
208     this.offLine = other.isOffline();
209     this.regionId = other.getRegionId();
210     this.regionName = other.getRegionName();
211     this.regionNameStr = Bytes.toStringBinary(this.regionName);
212     this.split = other.isSplit();
213     this.startKey = other.getStartKey();
214     this.hashCode = other.hashCode();
215     this.encodedName = other.getEncodedName();
216     this.tableName = other.getTableDesc().getName();
217   }
218 
219   public HRegionInfo(final byte[] tableName) {
220     this(tableName, null, null);
221   }
222 
223   /**
224    * Construct HRegionInfo with explicit parameters
225    *
226    * @param tableName the table name
227    * @param startKey first key in region
228    * @param endKey end of key range
229    * @throws IllegalArgumentException
230    */
231   public HRegionInfo(final byte[] tableName, final byte[] startKey,
232                      final byte[] endKey)
233   throws IllegalArgumentException {
234     this(tableName, startKey, endKey, false);
235   }
236 
237 
238   /**
239    * Construct HRegionInfo with explicit parameters
240    *
241    * @param tableName the table descriptor
242    * @param startKey first key in region
243    * @param endKey end of key range
244    * @param split true if this region has split and we have daughter regions
245    * regions that may or may not hold references to this region.
246    * @throws IllegalArgumentException
247    */
248   public HRegionInfo(final byte[] tableName, final byte[] startKey,
249                      final byte[] endKey, final boolean split)
250   throws IllegalArgumentException {
251     this(tableName, startKey, endKey, split, System.currentTimeMillis());
252   }
253 
254 
255   /**
256    * Construct HRegionInfo with explicit parameters
257    *
258    * @param tableName the table descriptor
259    * @param startKey first key in region
260    * @param endKey end of key range
261    * @param split true if this region has split and we have daughter regions
262    * regions that may or may not hold references to this region.
263    * @param regionid Region id to use.
264    * @throws IllegalArgumentException
265    */
266   public HRegionInfo(final byte[] tableName, final byte[] startKey,
267                      final byte[] endKey, final boolean split, final long regionid)
268   throws IllegalArgumentException {
269 
270     super();
271     if (tableName == null) {
272       throw new IllegalArgumentException("tableName cannot be null");
273     }
274     this.tableName = tableName.clone();
275     this.offLine = false;
276     this.regionId = regionid;
277 
278     this.regionName = createRegionName(this.tableName, startKey, regionId, true);
279 
280     this.regionNameStr = Bytes.toStringBinary(this.regionName);
281     this.split = split;
282     this.endKey = endKey == null? HConstants.EMPTY_END_ROW: endKey.clone();
283     this.startKey = startKey == null?
284       HConstants.EMPTY_START_ROW: startKey.clone();
285     this.tableName = tableName.clone();
286     setHashCode();
287   }
288 
289   /**
290    * Costruct a copy of another HRegionInfo
291    *
292    * @param other
293    */
294   public HRegionInfo(HRegionInfo other) {
295     super();
296     this.endKey = other.getEndKey();
297     this.offLine = other.isOffline();
298     this.regionId = other.getRegionId();
299     this.regionName = other.getRegionName();
300     this.regionNameStr = Bytes.toStringBinary(this.regionName);
301     this.split = other.isSplit();
302     this.startKey = other.getStartKey();
303     this.hashCode = other.hashCode();
304     this.encodedName = other.getEncodedName();
305     this.tableName = other.tableName;
306   }
307 
308 
309   /**
310    * Make a region name of passed parameters.
311    * @param tableName
312    * @param startKey Can be null
313    * @param regionid Region id (Usually timestamp from when region was created).
314    * @param newFormat should we create the region name in the new format
315    *                  (such that it contains its encoded name?).
316    * @return Region name made of passed tableName, startKey and id
317    */
318   public static byte [] createRegionName(final byte [] tableName,
319       final byte [] startKey, final long regionid, boolean newFormat) {
320     return createRegionName(tableName, startKey, Long.toString(regionid), newFormat);
321   }
322 
323   /**
324    * Make a region name of passed parameters.
325    * @param tableName
326    * @param startKey Can be null
327    * @param id Region id (Usually timestamp from when region was created).
328    * @param newFormat should we create the region name in the new format
329    *                  (such that it contains its encoded name?).
330    * @return Region name made of passed tableName, startKey and id
331    */
332   public static byte [] createRegionName(final byte [] tableName,
333       final byte [] startKey, final String id, boolean newFormat) {
334     return createRegionName(tableName, startKey, Bytes.toBytes(id), newFormat);
335   }
336 
337   /**
338    * Make a region name of passed parameters.
339    * @param tableName
340    * @param startKey Can be null
341    * @param id Region id (Usually timestamp from when region was created).
342    * @param newFormat should we create the region name in the new format
343    *                  (such that it contains its encoded name?).
344    * @return Region name made of passed tableName, startKey and id
345    */
346   public static byte [] createRegionName(final byte [] tableName,
347       final byte [] startKey, final byte [] id, boolean newFormat) {
348     byte [] b = new byte [tableName.length + 2 + id.length +
349        (startKey == null? 0: startKey.length) +
350        (newFormat ? (MD5_HEX_LENGTH + 2) : 0)];
351 
352     int offset = tableName.length;
353     System.arraycopy(tableName, 0, b, 0, offset);
354     b[offset++] = DELIMITER;
355     if (startKey != null && startKey.length > 0) {
356       System.arraycopy(startKey, 0, b, offset, startKey.length);
357       offset += startKey.length;
358     }
359     b[offset++] = DELIMITER;
360     System.arraycopy(id, 0, b, offset, id.length);
361     offset += id.length;
362 
363     if (newFormat) {
364       //
365       // Encoded name should be built into the region name.
366       //
367       // Use the region name thus far (namely, <tablename>,<startKey>,<id>)
368       // to compute a MD5 hash to be used as the encoded name, and append
369       // it to the byte buffer.
370       //
371       String md5Hash = MD5Hash.getMD5AsHex(b, 0, offset);
372       byte [] md5HashBytes = Bytes.toBytes(md5Hash);
373 
374       if (md5HashBytes.length != MD5_HEX_LENGTH) {
375         LOG.error("MD5-hash length mismatch: Expected=" + MD5_HEX_LENGTH +
376                   "; Got=" + md5HashBytes.length); 
377       }
378 
379       // now append the bytes '.<encodedName>.' to the end
380       b[offset++] = ENC_SEPARATOR;
381       System.arraycopy(md5HashBytes, 0, b, offset, MD5_HEX_LENGTH);
382       offset += MD5_HEX_LENGTH;
383       b[offset++] = ENC_SEPARATOR;
384     }
385     
386     return b;
387   }
388 
389   /**
390    * Gets the table name from the specified region name.
391    * @param regionName
392    * @return Table name.
393    */
394   public static byte [] getTableName(byte [] regionName) {
395     int offset = -1;
396     for (int i = 0; i < regionName.length; i++) {
397       if (regionName[i] == DELIMITER) {
398         offset = i;
399         break;
400       }
401     }
402     byte [] tableName = new byte[offset];
403     System.arraycopy(regionName, 0, tableName, 0, offset);
404     return tableName;
405   }
406 
407   /**
408    * Separate elements of a regionName.
409    * @param regionName
410    * @return Array of byte[] containing tableName, startKey and id
411    * @throws IOException
412    */
413   public static byte [][] parseRegionName(final byte [] regionName)
414   throws IOException {
415     int offset = -1;
416     for (int i = 0; i < regionName.length; i++) {
417       if (regionName[i] == DELIMITER) {
418         offset = i;
419         break;
420       }
421     }
422     if(offset == -1) throw new IOException("Invalid regionName format");
423     byte [] tableName = new byte[offset];
424     System.arraycopy(regionName, 0, tableName, 0, offset);
425     offset = -1;
426     for (int i = regionName.length - 1; i > 0; i--) {
427       if(regionName[i] == DELIMITER) {
428         offset = i;
429         break;
430       }
431     }
432     if(offset == -1) throw new IOException("Invalid regionName format");
433     byte [] startKey = HConstants.EMPTY_BYTE_ARRAY;
434     if(offset != tableName.length + 1) {
435       startKey = new byte[offset - tableName.length - 1];
436       System.arraycopy(regionName, tableName.length + 1, startKey, 0,
437           offset - tableName.length - 1);
438     }
439     byte [] id = new byte[regionName.length - offset - 1];
440     System.arraycopy(regionName, offset + 1, id, 0,
441         regionName.length - offset - 1);
442     byte [][] elements = new byte[3][];
443     elements[0] = tableName;
444     elements[1] = startKey;
445     elements[2] = id;
446     return elements;
447   }
448 
449   /** @return the regionId */
450   public long getRegionId(){
451     return regionId;
452   }
453 
454   /**
455    * @return the regionName as an array of bytes.
456    * @see #getRegionNameAsString()
457    */
458   public byte [] getRegionName(){
459     return regionName;
460   }
461 
462   /**
463    * @return Region name as a String for use in logging, etc.
464    */
465   public String getRegionNameAsString() {
466     if (hasEncodedName(this.regionName)) {
467       // new format region names already have their encoded name.
468       return this.regionNameStr;
469     }
470 
471     // old format. regionNameStr doesn't have the region name.
472     //
473     //
474     return this.regionNameStr + "." + this.getEncodedName();
475   }
476 
477   /** @return the encoded region name */
478   public synchronized String getEncodedName() {
479     if (this.encodedName == NO_HASH) {
480       this.encodedName = encodeRegionName(this.regionName);
481     }
482     return this.encodedName;
483   }
484 
485   public synchronized byte [] getEncodedNameAsBytes() {
486     if (this.encodedNameAsBytes == null) {
487       this.encodedNameAsBytes = Bytes.toBytes(getEncodedName());
488     }
489     return this.encodedNameAsBytes;
490   }
491 
492   /** @return the startKey */
493   public byte [] getStartKey(){
494     return startKey;
495   }
496   
497   /** @return the endKey */
498   public byte [] getEndKey(){
499     return endKey;
500   }
501 
502   /**
503    * Get current table name of the region
504    * @return byte array of table name
505    */
506   public byte[] getTableName() {
507     if (tableName == null || tableName.length == 0) {
508       tableName = getTableName(getRegionName());
509     }
510     return tableName;
511   }
512 
513   /**
514    * Get current table name as string
515    * @return string representation of current table
516    */
517   public String getTableNameAsString() {
518     return Bytes.toString(tableName);
519   }
520 
521   /**
522    * Returns true if the given inclusive range of rows is fully contained
523    * by this region. For example, if the region is foo,a,g and this is
524    * passed ["b","c"] or ["a","c"] it will return true, but if this is passed
525    * ["b","z"] it will return false.
526    * @throws IllegalArgumentException if the range passed is invalid (ie end < start)
527    */
528   public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) {
529     if (Bytes.compareTo(rangeStartKey, rangeEndKey) > 0) {
530       throw new IllegalArgumentException(
531       "Invalid range: " + Bytes.toStringBinary(rangeStartKey) +
532       " > " + Bytes.toStringBinary(rangeEndKey));
533     }
534 
535     boolean firstKeyInRange = Bytes.compareTo(rangeStartKey, startKey) >= 0;
536     boolean lastKeyInRange =
537       Bytes.compareTo(rangeEndKey, endKey) < 0 ||
538       Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY);
539     return firstKeyInRange && lastKeyInRange;
540   }
541   
542   /**
543    * Return true if the given row falls in this region.
544    */
545   public boolean containsRow(byte[] row) {
546     return Bytes.compareTo(row, startKey) >= 0 &&
547       (Bytes.compareTo(row, endKey) < 0 ||
548        Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY));
549   }
550 
551   /**
552    * @return the tableDesc
553    * @deprecated Do not use; expensive call
554    *         use HRegionInfo.getTableNameAsString() in place of
555    *         HRegionInfo.getTableDesc().getNameAsString()
556    */
557    @Deprecated
558   public HTableDescriptor getTableDesc() {
559     Configuration c = HBaseConfiguration.create();
560     c.set("fs.defaultFS", c.get(HConstants.HBASE_DIR));
561     c.set("fs.default.name", c.get(HConstants.HBASE_DIR));
562     FileSystem fs;
563     try {
564       fs = FileSystem.get(c);
565     } catch (IOException e) {
566       throw new RuntimeException(e);
567     }
568     FSTableDescriptors fstd =
569       new FSTableDescriptors(fs, new Path(c.get(HConstants.HBASE_DIR)));
570     try {
571       return fstd.get(this.tableName);
572     } catch (IOException e) {
573       throw new RuntimeException(e);
574     }
575   }
576 
577   /**
578    * @param newDesc new table descriptor to use
579    * @deprecated Do not use; expensive call
580    */
581   @Deprecated
582   public void setTableDesc(HTableDescriptor newDesc) {
583     Configuration c = HBaseConfiguration.create();
584     FileSystem fs;
585     try {
586       fs = FileSystem.get(c);
587     } catch (IOException e) {
588       throw new RuntimeException(e);
589     }
590     FSTableDescriptors fstd =
591       new FSTableDescriptors(fs, new Path(c.get(HConstants.HBASE_DIR)));
592     try {
593       fstd.add(newDesc);
594     } catch (IOException e) {
595       throw new RuntimeException(e);
596     }
597   }
598 
599   /** @return true if this is the root region */
600   public boolean isRootRegion() {
601     return Bytes.equals(tableName, HRegionInfo.ROOT_REGIONINFO.getTableName());
602   }
603 
604   /** @return true if this region is from a table that is a meta table,
605    * either <code>.META.</code> or <code>-ROOT-</code>
606    */
607   public boolean isMetaTable() {
608     return isRootRegion() || isMetaRegion();
609   }
610 
611   /** @return true if this region is a meta region */
612   public boolean isMetaRegion() {
613      return Bytes.equals(tableName, HRegionInfo.FIRST_META_REGIONINFO.getTableName());
614   }
615 
616   /**
617    * @return True if has been split and has daughters.
618    */
619   public boolean isSplit() {
620     return this.split;
621   }
622 
623   /**
624    * @param split set split status
625    */
626   public void setSplit(boolean split) {
627     this.split = split;
628   }
629 
630   /**
631    * @return True if this region is offline.
632    */
633   public boolean isOffline() {
634     return this.offLine;
635   }
636 
637   /**
638    * The parent of a region split is offline while split daughters hold
639    * references to the parent. Offlined regions are closed.
640    * @param offLine Set online/offline status.
641    */
642   public void setOffline(boolean offLine) {
643     this.offLine = offLine;
644   }
645 
646 
647   /**
648    * @return True if this is a split parent region.
649    */
650   public boolean isSplitParent() {
651     if (!isSplit()) return false;
652     if (!isOffline()) {
653       LOG.warn("Region is split but NOT offline: " + getRegionNameAsString());
654     }
655     return true;
656   }
657 
658   /**
659    * @see java.lang.Object#toString()
660    */
661   @Override
662   public String toString() {
663     return "{" + HConstants.NAME + " => '" +
664       this.regionNameStr
665       + "', STARTKEY => '" +
666       Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" +
667       Bytes.toStringBinary(this.endKey) +
668       "', ENCODED => " + getEncodedName() + "," +
669       (isOffline()? " OFFLINE => true,": "") +
670       (isSplit()? " SPLIT => true,": "") + "}";
671   }
672 
673   /**
674    * @see java.lang.Object#equals(java.lang.Object)
675    */
676   @Override
677   public boolean equals(Object o) {
678     if (this == o) {
679       return true;
680     }
681     if (o == null) {
682       return false;
683     }
684     if (!(o instanceof HRegionInfo)) {
685       return false;
686     }
687     return this.compareTo((HRegionInfo)o) == 0;
688   }
689 
690   /**
691    * @see java.lang.Object#hashCode()
692    */
693   @Override
694   public int hashCode() {
695     return this.hashCode;
696   }
697 
698   /** @return the object version number */
699   @Override
700   public byte getVersion() {
701     return VERSION;
702   }
703 
704   //
705   // Writable
706   //
707 
708   @Override
709   public void write(DataOutput out) throws IOException {
710     super.write(out);
711     Bytes.writeByteArray(out, endKey);
712     out.writeBoolean(offLine);
713     out.writeLong(regionId);
714     Bytes.writeByteArray(out, regionName);
715     out.writeBoolean(split);
716     Bytes.writeByteArray(out, startKey);
717     Bytes.writeByteArray(out, tableName);
718     out.writeInt(hashCode);
719   }
720 
721   @Override
722   public void readFields(DataInput in) throws IOException {
723     // Read the single version byte.  We don't ask the super class do it
724     // because freaks out if its not the current classes' version.  This method
725     // can deserialize version 0 and version 1 of HRI.
726     byte version = in.readByte();
727     if (version == 0) {
728       // This is the old HRI that carried an HTD.  Migrate it.  The below
729       // was copied from the old 0.90 HRI readFields.
730       this.endKey = Bytes.readByteArray(in);
731       this.offLine = in.readBoolean();
732       this.regionId = in.readLong();
733       this.regionName = Bytes.readByteArray(in);
734       this.regionNameStr = Bytes.toStringBinary(this.regionName);
735       this.split = in.readBoolean();
736       this.startKey = Bytes.readByteArray(in);
737       try {
738         HTableDescriptor htd = new HTableDescriptor();
739         htd.readFields(in);
740         this.tableName = htd.getName();
741       } catch(EOFException eofe) {
742          throw new IOException("HTD not found in input buffer", eofe);
743       }
744       this.hashCode = in.readInt();
745     } else if (getVersion() == version) {
746       this.endKey = Bytes.readByteArray(in);
747       this.offLine = in.readBoolean();
748       this.regionId = in.readLong();
749       this.regionName = Bytes.readByteArray(in);
750       this.regionNameStr = Bytes.toStringBinary(this.regionName);
751       this.split = in.readBoolean();
752       this.startKey = Bytes.readByteArray(in);
753       this.tableName = Bytes.readByteArray(in);
754       this.hashCode = in.readInt();
755     } else {
756       throw new IOException("Non-migratable/unknown version=" + getVersion());
757     }
758   }
759 
760   //
761   // Comparable
762   //
763 
764   public int compareTo(HRegionInfo o) {
765     if (o == null) {
766       return 1;
767     }
768 
769     // Are regions of same table?
770     int result = Bytes.compareTo(this.tableName, o.tableName);
771     if (result != 0) {
772       return result;
773     }
774 
775     // Compare start keys.
776     result = Bytes.compareTo(this.startKey, o.startKey);
777     if (result != 0) {
778       return result;
779     }
780 
781     // Compare end keys.
782     result = Bytes.compareTo(this.endKey, o.endKey);
783 
784     if (result != 0) {
785       if (this.getStartKey().length != 0
786               && this.getEndKey().length == 0) {
787           return 1; // this is last region
788       }
789       if (o.getStartKey().length != 0
790               && o.getEndKey().length == 0) {
791           return -1; // o is the last region
792       }
793       return result;
794     }
795 
796     // regionId is usually milli timestamp -- this defines older stamps
797     // to be "smaller" than newer stamps in sort order.
798     if (this.regionId > o.regionId) {
799       return 1;
800     } else if (this.regionId < o.regionId) {
801       return -1;
802     }
803 
804     if (this.offLine == o.offLine)
805       return 0;
806     if (this.offLine == true) return -1;
807         
808     return 1;
809   }
810 
811   /**
812    * @return Comparator to use comparing {@link KeyValue}s.
813    */
814   public KVComparator getComparator() {
815     return isRootRegion()? KeyValue.ROOT_COMPARATOR: isMetaRegion()?
816       KeyValue.META_COMPARATOR: KeyValue.COMPARATOR;
817   }
818 }