View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.mapreduce;
20  
21  import java.io.DataInput;
22  import java.io.DataOutput;
23  import java.io.IOException;
24  import java.util.Arrays;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.classification.InterfaceAudience;
29  import org.apache.hadoop.classification.InterfaceStability;
30  import org.apache.hadoop.hbase.HConstants;
31  import org.apache.hadoop.hbase.client.Scan;
32  import org.apache.hadoop.hbase.util.Bytes;
33  import org.apache.hadoop.io.Writable;
34  import org.apache.hadoop.io.WritableUtils;
35  import org.apache.hadoop.mapreduce.InputSplit;
36  
37  /**
38   * A table split corresponds to a key range (low, high) and an optional scanner.
39   * All references to row below refer to the key of the row.
40   */
41  @InterfaceAudience.Public
42  @InterfaceStability.Evolving
43  public class TableSplit extends InputSplit
44  implements Writable, Comparable<TableSplit> {
45    public static final Log LOG = LogFactory.getLog(TableSplit.class);
46    
47    // should be < 0 (@see #readFields(DataInput))
48    // version 1 supports Scan data member
49    enum Version {
50      UNVERSIONED(0),
51      // Initial number we put on TableSplit when we introduced versioning.
52      INITIAL(-1);
53  
54      final int code;
55      static final Version[] byCode;
56      static {
57        byCode = Version.values();
58        for (int i = 0; i < byCode.length; i++) {
59          if (byCode[i].code != -1 * i) {
60            throw new AssertionError("Values in this enum should be descending by one");
61          }
62        }
63      }
64  
65      Version(int code) {
66        this.code = code;
67      }
68  
69      boolean atLeast(Version other) {
70        return code <= other.code;
71      }
72  
73      static Version fromCode(int code) {
74        return byCode[code * -1];
75      }
76    }
77    
78    private static final Version VERSION = Version.INITIAL;
79    private byte [] tableName;
80    private byte [] startRow;
81    private byte [] endRow;
82    private String regionLocation;
83    private String scan = ""; // stores the serialized form of the Scan
84  
85    /** Default constructor. */
86    public TableSplit() {
87      this(HConstants.EMPTY_BYTE_ARRAY, null, HConstants.EMPTY_BYTE_ARRAY,
88        HConstants.EMPTY_BYTE_ARRAY, "");
89    }
90  
91    /**
92     * Creates a new instance while assigning all variables.
93     *
94     * @param tableName  The name of the current table.
95     * @param scan The scan associated with this split.
96     * @param startRow  The start row of the split.
97     * @param endRow  The end row of the split.
98     * @param location  The location of the region.
99     */
100   public TableSplit(byte [] tableName, Scan scan, byte [] startRow, byte [] endRow,
101       final String location) {
102     this.tableName = tableName;
103     try {
104       this.scan =
105         (null == scan) ? "" : TableMapReduceUtil.convertScanToString(scan);
106     } catch (IOException e) {
107       LOG.warn("Failed to convert Scan to String", e);
108     }
109     this.startRow = startRow;
110     this.endRow = endRow;
111     this.regionLocation = location;
112   }
113   
114   /**
115    * Creates a new instance without a scanner.
116    *
117    * @param tableName The name of the current table.
118    * @param startRow The start row of the split.
119    * @param endRow The end row of the split.
120    * @param location The location of the region.
121    */
122   public TableSplit(byte[] tableName, byte[] startRow, byte[] endRow,
123       final String location) {
124     this(tableName, null, startRow, endRow, location);
125   }
126 
127   /**
128    * Returns a Scan object from the stored string representation.
129    *
130    * @return Returns a Scan object based on the stored scanner.
131    * @throws IOException
132    */
133   public Scan getScan() throws IOException {
134     return TableMapReduceUtil.convertStringToScan(this.scan);
135   }
136 
137   /**
138    * Returns the table name.
139    *
140    * @return The table name.
141    */
142   public byte [] getTableName() {
143     return tableName;
144   }
145 
146   /**
147    * Returns the start row.
148    *
149    * @return The start row.
150    */
151   public byte [] getStartRow() {
152     return startRow;
153   }
154 
155   /**
156    * Returns the end row.
157    *
158    * @return The end row.
159    */
160   public byte [] getEndRow() {
161     return endRow;
162   }
163 
164   /**
165    * Returns the region location.
166    *
167    * @return The region's location.
168    */
169   public String getRegionLocation() {
170     return regionLocation;
171   }
172 
173   /**
174    * Returns the region's location as an array.
175    *
176    * @return The array containing the region location.
177    * @see org.apache.hadoop.mapreduce.InputSplit#getLocations()
178    */
179   @Override
180   public String[] getLocations() {
181     return new String[] {regionLocation};
182   }
183 
184   /**
185    * Returns the length of the split.
186    *
187    * @return The length of the split.
188    * @see org.apache.hadoop.mapreduce.InputSplit#getLength()
189    */
190   @Override
191   public long getLength() {
192     // Not clear how to obtain this... seems to be used only for sorting splits
193     return 0;
194   }
195 
196   /**
197    * Reads the values of each field.
198    *
199    * @param in  The input to read from.
200    * @throws IOException When reading the input fails.
201    */
202   @Override
203   public void readFields(DataInput in) throws IOException {
204     Version version = Version.UNVERSIONED;
205     // TableSplit was not versioned in the beginning.
206     // In order to introduce it now, we make use of the fact
207     // that tableName was written with Bytes.writeByteArray,
208     // which encodes the array length as a vint which is >= 0.
209     // Hence if the vint is >= 0 we have an old version and the vint
210     // encodes the length of tableName.
211     // If < 0 we just read the version and the next vint is the length.
212     // @see Bytes#readByteArray(DataInput)
213     int len = WritableUtils.readVInt(in);
214     if (len < 0) {
215       // what we just read was the version
216       version = Version.fromCode(len);
217       len = WritableUtils.readVInt(in);
218     }
219     tableName = new byte[len];
220     in.readFully(tableName);
221     startRow = Bytes.readByteArray(in);
222     endRow = Bytes.readByteArray(in);
223     regionLocation = Bytes.toString(Bytes.readByteArray(in));
224     if (version.atLeast(Version.INITIAL)) {
225       scan = Bytes.toString(Bytes.readByteArray(in));
226     }
227   }
228 
229   /**
230    * Writes the field values to the output.
231    *
232    * @param out  The output to write to.
233    * @throws IOException When writing the values to the output fails.
234    */
235   @Override
236   public void write(DataOutput out) throws IOException {
237     WritableUtils.writeVInt(out, VERSION.code);
238     Bytes.writeByteArray(out, tableName);
239     Bytes.writeByteArray(out, startRow);
240     Bytes.writeByteArray(out, endRow);
241     Bytes.writeByteArray(out, Bytes.toBytes(regionLocation));
242     Bytes.writeByteArray(out, Bytes.toBytes(scan));
243   }
244 
245   /**
246    * Returns the details about this instance as a string.
247    *
248    * @return The values of this instance as a string.
249    * @see java.lang.Object#toString()
250    */
251   @Override
252   public String toString() {
253     return regionLocation + ":" +
254       Bytes.toStringBinary(startRow) + "," + Bytes.toStringBinary(endRow);
255   }
256 
257   /**
258    * Compares this split against the given one.
259    *
260    * @param split  The split to compare to.
261    * @return The result of the comparison.
262    * @see java.lang.Comparable#compareTo(java.lang.Object)
263    */
264   @Override
265   public int compareTo(TableSplit split) {
266     // If The table name of the two splits is the same then compare start row
267     // otherwise compare based on table names
268     int tableNameComparison =
269         Bytes.compareTo(getTableName(), split.getTableName());
270     return tableNameComparison != 0 ? tableNameComparison : Bytes.compareTo(
271         getStartRow(), split.getStartRow());
272   }
273 
274   @Override
275   public boolean equals(Object o) {
276     if (o == null || !(o instanceof TableSplit)) {
277       return false;
278     }
279     return Bytes.equals(tableName, ((TableSplit)o).tableName) &&
280       Bytes.equals(startRow, ((TableSplit)o).startRow) &&
281       Bytes.equals(endRow, ((TableSplit)o).endRow) &&
282       regionLocation.equals(((TableSplit)o).regionLocation);
283   }
284 
285     @Override
286     public int hashCode() {
287         int result = tableName != null ? Arrays.hashCode(tableName) : 0;
288         result = 31 * result + (scan != null ? scan.hashCode() : 0);
289         result = 31 * result + (startRow != null ? Arrays.hashCode(startRow) : 0);
290         result = 31 * result + (endRow != null ? Arrays.hashCode(endRow) : 0);
291         result = 31 * result + (regionLocation != null ? regionLocation.hashCode() : 0);
292         return result;
293     }
294 }