001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019import java.util.Arrays;
020import org.apache.hadoop.hbase.HConstants;
021import org.apache.hadoop.hbase.TableName;
022import org.apache.hadoop.hbase.util.Bytes;
023import org.apache.yetus.audience.InterfaceAudience;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027/**
028 * An implementation of RegionInfo that adds mutable methods so can build a RegionInfo instance.
029 * Package private. Use {@link RegionInfoBuilder} creating instances of {@link RegionInfo}s.
030 */
031@InterfaceAudience.Private
032class MutableRegionInfo implements RegionInfo {
033  private static final Logger LOG = LoggerFactory.getLogger(MutableRegionInfo.class);
034  private static final int MAX_REPLICA_ID = 0xFFFF;
035
036  /**
037   * The new format for a region name contains its encodedName at the end.
038   * The encoded name also serves as the directory name for the region
039   * in the filesystem.
040   *
041   * New region name format:
042   *    <tablename>,,<startkey>,<regionIdTimestamp>.<encodedName>.
043   * where,
044   *    <encodedName> is a hex version of the MD5 hash of
045   *    <tablename>,<startkey>,<regionIdTimestamp>
046   *
047   * The old region name format:
048   *    <tablename>,<startkey>,<regionIdTimestamp>
049   * For region names in the old format, the encoded name is a 32-bit
050   * JenkinsHash integer value (in its decimal notation, string form).
051   *<p>
052   * **NOTE**
053   *
054   * The first hbase:meta region, and regions created by an older
055   * version of HBase (0.20 or prior) will continue to use the
056   * old region name format.
057   */
058
059  // This flag is in the parent of a split while the parent is still referenced by daughter
060  // regions. We USED to set this flag when we disabled a table but now table state is kept up in
061  // zookeeper as of 0.90.0 HBase. And now in DisableTableProcedure, finally we will create bunch
062  // of UnassignProcedures and at the last of the procedure we will set the region state to
063  // CLOSED, and will not change the offLine flag.
064  private boolean offLine;
065  private boolean split;
066  private final long regionId;
067  private final int replicaId;
068  private final byte[] regionName;
069  private final byte[] startKey;
070  private final byte[] endKey;
071  private final int hashCode;
072  private final String encodedName;
073  private final byte[] encodedNameAsBytes;
074  private final TableName tableName;
075
076  private static int generateHashCode(final TableName tableName, final byte[] startKey,
077      final byte[] endKey, final long regionId,
078      final int replicaId, boolean offLine, byte[] regionName) {
079    int result = Arrays.hashCode(regionName);
080    result = (int) (result ^ regionId);
081    result ^= Arrays.hashCode(checkStartKey(startKey));
082    result ^= Arrays.hashCode(checkEndKey(endKey));
083    result ^= Boolean.valueOf(offLine).hashCode();
084    result ^= Arrays.hashCode(tableName.getName());
085    result ^= replicaId;
086    return result;
087  }
088
089  private static byte[] checkStartKey(byte[] startKey) {
090    return startKey == null? HConstants.EMPTY_START_ROW: startKey;
091  }
092
093  private static byte[] checkEndKey(byte[] endKey) {
094    return endKey == null? HConstants.EMPTY_END_ROW: endKey;
095  }
096
097  private static TableName checkTableName(TableName tableName) {
098    if (tableName == null) {
099      throw new IllegalArgumentException("TableName cannot be null");
100    }
101    return tableName;
102  }
103
104  private static int checkReplicaId(int regionId) {
105    if (regionId > MAX_REPLICA_ID) {
106      throw new IllegalArgumentException("ReplicaId cannot be greater than" + MAX_REPLICA_ID);
107    }
108    return regionId;
109  }
110
111  /**
112   * Package private constructor used constructing MutableRegionInfo for the first meta regions
113   */
114  MutableRegionInfo(long regionId, TableName tableName, int replicaId) {
115    this(tableName, HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW, false, regionId,
116      replicaId, false);
117  }
118
119  MutableRegionInfo(final TableName tableName, final byte[] startKey, final byte[] endKey,
120      final boolean split, final long regionId, final int replicaId, boolean offLine) {
121    this.tableName = checkTableName(tableName);
122    this.startKey = checkStartKey(startKey);
123    this.endKey = checkEndKey(endKey);
124    this.split = split;
125    this.regionId = regionId;
126    this.replicaId = checkReplicaId(replicaId);
127    this.offLine = offLine;
128    this.regionName = RegionInfo.createRegionName(this.tableName, this.startKey, this.regionId,
129      this.replicaId, !this.tableName.equals(TableName.META_TABLE_NAME));
130    this.encodedName = RegionInfo.encodeRegionName(this.regionName);
131    this.hashCode = generateHashCode(this.tableName, this.startKey, this.endKey, this.regionId,
132      this.replicaId, this.offLine, this.regionName);
133    this.encodedNameAsBytes = Bytes.toBytes(this.encodedName);
134  }
135
136  /**
137   * @return Return a short, printable name for this region (usually encoded name) for us logging.
138   */
139  @Override
140  public String getShortNameToLog() {
141    return RegionInfo.prettyPrint(this.getEncodedName());
142  }
143
144  /** @return the regionId */
145  @Override
146  public long getRegionId(){
147    return regionId;
148  }
149
150
151  /**
152   * @return the regionName as an array of bytes.
153   * @see #getRegionNameAsString()
154   */
155  @Override
156  public byte[] getRegionName() {
157    return regionName;
158  }
159
160  /**
161   * @return Region name as a String for use in logging, etc.
162   */
163  @Override
164  public String getRegionNameAsString() {
165    return RegionInfo.getRegionNameAsString(this, this.regionName);
166  }
167
168  /** @return the encoded region name */
169  @Override
170  public String getEncodedName() {
171    return this.encodedName;
172  }
173
174  @Override
175  public byte[] getEncodedNameAsBytes() {
176    return this.encodedNameAsBytes;
177  }
178
179  /** @return the startKey */
180  @Override
181  public byte[] getStartKey() {
182    return startKey;
183  }
184
185  /** @return the endKey */
186  @Override
187  public byte[] getEndKey() {
188    return endKey;
189  }
190
191  /**
192   * Get current table name of the region
193   * @return TableName
194   */
195  @Override
196  public TableName getTable() {
197    return this.tableName;
198  }
199
200  /**
201   * Returns true if the given inclusive range of rows is fully contained
202   * by this region. For example, if the region is foo,a,g and this is
203   * passed ["b","c"] or ["a","c"] it will return true, but if this is passed
204   * ["b","z"] it will return false.
205   * @throws IllegalArgumentException if the range passed is invalid (ie. end &lt; start)
206   */
207  @Override
208  public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) {
209    if (Bytes.compareTo(rangeStartKey, rangeEndKey) > 0) {
210      throw new IllegalArgumentException(
211      "Invalid range: " + Bytes.toStringBinary(rangeStartKey) +
212      " > " + Bytes.toStringBinary(rangeEndKey));
213    }
214
215    boolean firstKeyInRange = Bytes.compareTo(rangeStartKey, startKey) >= 0;
216    boolean lastKeyInRange =
217      Bytes.compareTo(rangeEndKey, endKey) < 0 ||
218      Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY);
219    return firstKeyInRange && lastKeyInRange;
220  }
221
222  /**
223   * Return true if the given row falls in this region.
224   */
225  @Override
226  public boolean containsRow(byte[] row) {
227    return Bytes.compareTo(row, startKey) >= 0 &&
228      (Bytes.compareTo(row, endKey) < 0 ||
229       Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY));
230  }
231
232  /** @return true if this region is a meta region */
233  @Override
234  public boolean isMetaRegion() {
235    return tableName.equals(TableName.META_TABLE_NAME);
236  }
237
238  /**
239   * @return True if has been split and has daughters.
240   */
241  @Override
242  public boolean isSplit() {
243    return this.split;
244  }
245
246  /**
247   * @param split set split status
248   * @return MutableRegionInfo
249   */
250  public MutableRegionInfo setSplit(boolean split) {
251    this.split = split;
252    return this;
253  }
254
255  /**
256   * @return True if this region is offline.
257   * @deprecated since 3.0.0 and will be removed in 4.0.0
258   * @see <a href="https://issues.apache.org/jira/browse/HBASE-25210">HBASE-25210</a>
259   */
260  @Override
261  @Deprecated
262  public boolean isOffline() {
263    return this.offLine;
264  }
265
266  /**
267   * The parent of a region split is offline while split daughters hold
268   * references to the parent. Offlined regions are closed.
269   * @param offLine Set online/offline status.
270   * @return MutableRegionInfo
271   */
272  public MutableRegionInfo setOffline(boolean offLine) {
273    this.offLine = offLine;
274    return this;
275  }
276
277  /**
278   * @return True if this is a split parent region.
279   * @deprecated since 3.0.0 and will be removed in 4.0.0, Use {@link #isSplit()} instead.
280   * @see <a href="https://issues.apache.org/jira/browse/HBASE-25210">HBASE-25210</a>
281   */
282  @Override
283  @Deprecated
284  public boolean isSplitParent() {
285    if (!isSplit()) {
286      return false;
287    }
288    if (!isOffline()) {
289      LOG.warn("Region is split but NOT offline: " + getRegionNameAsString());
290    }
291    return true;
292  }
293
294  /**
295   * Returns the region replica id
296   * @return returns region replica id
297   */
298  @Override
299  public int getReplicaId() {
300    return replicaId;
301  }
302
303  /**
304   * @see Object#toString()
305   */
306  @Override
307  public String toString() {
308    return "{ENCODED => " + getEncodedName() + ", " +
309      HConstants.NAME + " => '" + Bytes.toStringBinary(this.regionName)
310      + "', STARTKEY => '" +
311      Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" +
312      Bytes.toStringBinary(this.endKey) + "'" +
313      (isOffline()? ", OFFLINE => true": "") +
314      (isSplit()? ", SPLIT => true": "") +
315      ((replicaId > 0)? ", REPLICA_ID => " + replicaId : "") + "}";
316  }
317
318  /**
319   * @see Object#equals(Object)
320   */
321  @Override
322  public boolean equals(Object o) {
323    if (this == o) {
324      return true;
325    }
326    if (o == null) {
327      return false;
328    }
329    if (!(o instanceof RegionInfo)) {
330      return false;
331    }
332    return compareTo((RegionInfo)o) == 0;
333  }
334
335  /**
336   * @see Object#hashCode()
337   */
338  @Override
339  public int hashCode() {
340    return this.hashCode;
341  }
342}