View Javadoc

1   /**
2    * Copyright 2011 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.thrift2;
21  
22  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.deleteFromThrift;
23  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.deletesFromHBase;
24  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.deletesFromThrift;
25  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.getFromThrift;
26  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.getsFromThrift;
27  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.incrementFromThrift;
28  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.putFromThrift;
29  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.putsFromThrift;
30  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.resultFromHBase;
31  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.resultsFromHBase;
32  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.scanFromThrift;
33  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.rowMutationsFromThrift;
34  import static org.apache.thrift.TBaseHelper.byteBufferToByteArray;
35  
36  import java.io.IOException;
37  import java.lang.reflect.InvocationHandler;
38  import java.lang.reflect.InvocationTargetException;
39  import java.lang.reflect.Method;
40  import java.lang.reflect.Proxy;
41  import java.nio.ByteBuffer;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.concurrent.ConcurrentHashMap;
45  import java.util.concurrent.atomic.AtomicInteger;
46  
47  import org.apache.commons.logging.Log;
48  import org.apache.commons.logging.LogFactory;
49  import org.apache.hadoop.conf.Configuration;
50  import org.apache.hadoop.hbase.client.Delete;
51  import org.apache.hadoop.hbase.client.HTableInterface;
52  import org.apache.hadoop.hbase.client.HTablePool;
53  import org.apache.hadoop.hbase.client.ResultScanner;
54  import org.apache.hadoop.hbase.thrift.ThriftMetrics;
55  import org.apache.hadoop.hbase.thrift2.generated.TDelete;
56  import org.apache.hadoop.hbase.thrift2.generated.TGet;
57  import org.apache.hadoop.hbase.thrift2.generated.THBaseService;
58  import org.apache.hadoop.hbase.thrift2.generated.TIOError;
59  import org.apache.hadoop.hbase.thrift2.generated.TIllegalArgument;
60  import org.apache.hadoop.hbase.thrift2.generated.TIncrement;
61  import org.apache.hadoop.hbase.thrift2.generated.TPut;
62  import org.apache.hadoop.hbase.thrift2.generated.TResult;
63  import org.apache.hadoop.hbase.thrift2.generated.TScan;
64  import org.apache.hadoop.hbase.thrift2.generated.TRowMutations;
65  import org.apache.thrift.TException;
66  
67  /**
68   * This class is a glue object that connects Thrift RPC calls to the HBase client API primarily
69   * defined in the HTableInterface.
70   */
71  public class ThriftHBaseServiceHandler implements THBaseService.Iface {
72  
73    // TODO: Size of pool configuraple
74    private final HTablePool htablePool;
75    private static final Log LOG = LogFactory.getLog(ThriftHBaseServiceHandler.class);
76  
77    // nextScannerId and scannerMap are used to manage scanner state
78    // TODO: Cleanup thread for Scanners, Scanner id wrap
79    private final AtomicInteger nextScannerId = new AtomicInteger(0);
80    private final Map<Integer, ResultScanner> scannerMap =
81        new ConcurrentHashMap<Integer, ResultScanner>();
82  
83    public static THBaseService.Iface newInstance(Configuration conf, ThriftMetrics metrics) {
84      THBaseService.Iface handler = new ThriftHBaseServiceHandler(conf);
85      return (THBaseService.Iface) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
86        new Class[] { THBaseService.Iface.class }, new THBaseServiceMetricsProxy(handler, metrics));
87    }
88  
89    private static class THBaseServiceMetricsProxy implements InvocationHandler {
90      private final THBaseService.Iface handler;
91      private final ThriftMetrics metrics;
92  
93      private THBaseServiceMetricsProxy(THBaseService.Iface handler, ThriftMetrics metrics) {
94        this.handler = handler;
95        this.metrics = metrics;
96      }
97  
98      @Override
99      public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
100       Object result;
101       try {
102         long start = now();
103         result = m.invoke(handler, args);
104         int processTime = (int) (now() - start);
105         metrics.incMethodTime(m.getName(), processTime);
106       } catch (InvocationTargetException e) {
107         throw e.getTargetException();
108       } catch (Exception e) {
109         throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
110       }
111       return result;
112     }
113   }
114 
115   private static long now() {
116     return System.nanoTime();
117   }
118 
119   ThriftHBaseServiceHandler(Configuration conf) {
120     htablePool = new HTablePool(conf, Integer.MAX_VALUE);
121   }
122 
123   private HTableInterface getTable(ByteBuffer tableName) {
124     return htablePool.getTable(byteBufferToByteArray(tableName));
125   }
126 
127   private void closeTable(HTableInterface table) throws TIOError {
128     try {
129       table.close();
130     } catch (IOException e) {
131       throw getTIOError(e);
132     }
133   }
134 
135   private TIOError getTIOError(IOException e) {
136     TIOError err = new TIOError();
137     err.setMessage(e.getMessage());
138     return err;
139   }
140 
141   /**
142    * Assigns a unique ID to the scanner and adds the mapping to an internal HashMap.
143    * @param scanner to add
144    * @return Id for this Scanner
145    */
146   private int addScanner(ResultScanner scanner) {
147     int id = nextScannerId.getAndIncrement();
148     scannerMap.put(id, scanner);
149     return id;
150   }
151 
152   /**
153    * Returns the Scanner associated with the specified Id.
154    * @param id of the Scanner to get
155    * @return a Scanner, or null if the Id is invalid
156    */
157   private ResultScanner getScanner(int id) {
158     return scannerMap.get(id);
159   }
160 
161   /**
162    * Removes the scanner associated with the specified ID from the internal HashMap.
163    * @param id of the Scanner to remove
164    * @return the removed Scanner, or <code>null</code> if the Id is invalid
165    */
166   protected ResultScanner removeScanner(int id) {
167     return scannerMap.remove(id);
168   }
169 
170   @Override
171   public boolean exists(ByteBuffer table, TGet get) throws TIOError, TException {
172     HTableInterface htable = getTable(table);
173     try {
174       return htable.exists(getFromThrift(get));
175     } catch (IOException e) {
176       throw getTIOError(e);
177     } finally {
178       closeTable(htable);
179     }
180   }
181 
182   @Override
183   public TResult get(ByteBuffer table, TGet get) throws TIOError, TException {
184     HTableInterface htable = getTable(table);
185     try {
186       return resultFromHBase(htable.get(getFromThrift(get)));
187     } catch (IOException e) {
188       throw getTIOError(e);
189     } finally {
190       closeTable(htable);
191     }
192   }
193 
194   @Override
195   public List<TResult> getMultiple(ByteBuffer table, List<TGet> gets) throws TIOError, TException {
196     HTableInterface htable = getTable(table);
197     try {
198       return resultsFromHBase(htable.get(getsFromThrift(gets)));
199     } catch (IOException e) {
200       throw getTIOError(e);
201     } finally {
202       closeTable(htable);
203     }
204   }
205 
206   @Override
207   public void put(ByteBuffer table, TPut put) throws TIOError, TException {
208     HTableInterface htable = getTable(table);
209     try {
210       htable.put(putFromThrift(put));
211     } catch (IOException e) {
212       throw getTIOError(e);
213     } finally {
214       closeTable(htable);
215     }
216   }
217 
218   @Override
219   public boolean checkAndPut(ByteBuffer table, ByteBuffer row, ByteBuffer family,
220       ByteBuffer qualifier, ByteBuffer value, TPut put) throws TIOError, TException {
221     HTableInterface htable = getTable(table);
222     try {
223       return htable.checkAndPut(byteBufferToByteArray(row), byteBufferToByteArray(family),
224         byteBufferToByteArray(qualifier), (value == null) ? null : byteBufferToByteArray(value),
225         putFromThrift(put));
226     } catch (IOException e) {
227       throw getTIOError(e);
228     } finally {
229       closeTable(htable);
230     }
231   }
232 
233   @Override
234   public void putMultiple(ByteBuffer table, List<TPut> puts) throws TIOError, TException {
235     HTableInterface htable = getTable(table);
236     try {
237       htable.put(putsFromThrift(puts));
238     } catch (IOException e) {
239       throw getTIOError(e);
240     } finally {
241       closeTable(htable);
242     }
243   }
244 
245   @Override
246   public void deleteSingle(ByteBuffer table, TDelete deleteSingle) throws TIOError, TException {
247     HTableInterface htable = getTable(table);
248     try {
249       htable.delete(deleteFromThrift(deleteSingle));
250     } catch (IOException e) {
251       throw getTIOError(e);
252     } finally {
253       closeTable(htable);
254     }
255   }
256 
257   @Override
258   public List<TDelete> deleteMultiple(ByteBuffer table, List<TDelete> deletes) throws TIOError,
259       TException {
260     HTableInterface htable = getTable(table);
261     List<Delete> tempDeletes = deletesFromThrift(deletes);
262     try {
263       htable.delete(tempDeletes);
264     } catch (IOException e) {
265       throw getTIOError(e);
266     } finally {
267       closeTable(htable);
268     }
269     return deletesFromHBase(tempDeletes);
270   }
271 
272   @Override
273   public boolean checkAndDelete(ByteBuffer table, ByteBuffer row, ByteBuffer family,
274       ByteBuffer qualifier, ByteBuffer value, TDelete deleteSingle) throws TIOError, TException {
275     HTableInterface htable = getTable(table);
276 
277     try {
278       if (value == null) {
279         return htable.checkAndDelete(byteBufferToByteArray(row), byteBufferToByteArray(family),
280           byteBufferToByteArray(qualifier), null, deleteFromThrift(deleteSingle));
281       } else {
282         return htable.checkAndDelete(byteBufferToByteArray(row), byteBufferToByteArray(family),
283           byteBufferToByteArray(qualifier), byteBufferToByteArray(value),
284           deleteFromThrift(deleteSingle));
285       }
286     } catch (IOException e) {
287       throw getTIOError(e);
288     } finally {
289       closeTable(htable);
290     }
291   }
292 
293   @Override
294   public TResult increment(ByteBuffer table, TIncrement increment) throws TIOError, TException {
295     HTableInterface htable = getTable(table);
296     try {
297       return resultFromHBase(htable.increment(incrementFromThrift(increment)));
298     } catch (IOException e) {
299       throw getTIOError(e);
300     } finally {
301       closeTable(htable);
302     }
303   }
304 
305   @Override
306   public int openScanner(ByteBuffer table, TScan scan) throws TIOError, TException {
307     HTableInterface htable = getTable(table);
308     ResultScanner resultScanner = null;
309     try {
310       resultScanner = htable.getScanner(scanFromThrift(scan));
311     } catch (IOException e) {
312       throw getTIOError(e);
313     } finally {
314       closeTable(htable);
315     }
316     return addScanner(resultScanner);
317   }
318 
319   @Override
320   public List<TResult> getScannerRows(int scannerId, int numRows) throws TIOError,
321       TIllegalArgument, TException {
322     ResultScanner scanner = getScanner(scannerId);
323     if (scanner == null) {
324       TIllegalArgument ex = new TIllegalArgument();
325       ex.setMessage("Invalid scanner Id");
326       throw ex;
327     }
328 
329     try {
330       return resultsFromHBase(scanner.next(numRows));
331     } catch (IOException e) {
332       throw getTIOError(e);
333     }
334   }
335 
336   @Override
337   public void closeScanner(int scannerId) throws TIOError, TIllegalArgument, TException {
338     LOG.debug("scannerClose: id=" + scannerId);
339     ResultScanner scanner = getScanner(scannerId);
340     if (scanner == null) {
341       String message = "scanner ID is invalid";
342       LOG.warn(message);
343       TIllegalArgument ex = new TIllegalArgument();
344       ex.setMessage("Invalid scanner Id");
345       throw ex;
346     }
347     scanner.close();
348     removeScanner(scannerId);
349   }
350 
351   @Override
352   public void mutateRow(ByteBuffer table, TRowMutations rowMutations) throws TIOError, TException {
353     HTableInterface htable = getTable(table);
354     try {
355       htable.mutateRow(rowMutationsFromThrift(rowMutations));
356     } catch (IOException e) {
357       throw getTIOError(e);
358     } finally {
359       closeTable(htable);
360     }
361   }
362 }