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