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.thrift2;
20  
21  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.deleteFromThrift;
22  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.deletesFromThrift;
23  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.getFromThrift;
24  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.getsFromThrift;
25  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.incrementFromThrift;
26  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.putFromThrift;
27  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.putsFromThrift;
28  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.resultFromHBase;
29  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.resultsFromHBase;
30  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.scanFromThrift;
31  
32  import java.io.IOException;
33  import java.lang.reflect.InvocationHandler;
34  import java.lang.reflect.InvocationTargetException;
35  import java.lang.reflect.Method;
36  import java.lang.reflect.Proxy;
37  import java.nio.ByteBuffer;
38  import java.util.Collections;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.concurrent.ConcurrentHashMap;
42  import java.util.concurrent.atomic.AtomicInteger;
43  
44  import org.apache.commons.logging.Log;
45  import org.apache.commons.logging.LogFactory;
46  import org.apache.hadoop.classification.InterfaceAudience;
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  @InterfaceAudience.Private
69  public class ThriftHBaseServiceHandler implements THBaseService.Iface {
70  
71    // TODO: Size of pool configuraple
72    private final HTablePool htablePool;
73    private static final Log LOG = LogFactory.getLog(ThriftHBaseServiceHandler.class);
74  
75    // nextScannerId and scannerMap are used to manage scanner state
76    // TODO: Cleanup thread for Scanners, Scanner id wrap
77    private final AtomicInteger nextScannerId = new AtomicInteger(0);
78    private final Map<Integer, ResultScanner> scannerMap = new ConcurrentHashMap<Integer, ResultScanner>();
79  
80    public static THBaseService.Iface newInstance(
81        Configuration conf, ThriftMetrics metrics) {
82      THBaseService.Iface handler = new ThriftHBaseServiceHandler(conf);
83      return (THBaseService.Iface) Proxy.newProxyInstance(
84          handler.getClass().getClassLoader(),
85          new Class[]{THBaseService.Iface.class},
86          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(
94          THBaseService.Iface handler, ThriftMetrics metrics) {
95        this.handler = handler;
96        this.metrics = metrics;
97      }
98  
99      @Override
100     public Object invoke(Object proxy, Method m, Object[] args)
101         throws Throwable {
102       Object result;
103       try {
104         long start = now();
105         result = m.invoke(handler, args);
106         int processTime = (int)(now() - start);
107         metrics.incMethodTime(m.getName(), processTime);
108       } catch (InvocationTargetException e) {
109         throw e.getTargetException();
110       } catch (Exception e) {
111         throw new RuntimeException(
112             "unexpected invocation exception: " + e.getMessage());
113       }
114       return result;
115     }
116   }
117     
118   private static long now() {
119     return System.nanoTime();
120   }
121 
122   ThriftHBaseServiceHandler(Configuration conf) {
123     htablePool = new HTablePool(conf, Integer.MAX_VALUE);
124   }
125 
126   private HTableInterface getTable(byte[] tableName) {
127     return htablePool.getTable(tableName);
128   }
129 
130   private void closeTable(HTableInterface table) throws TIOError {
131     try {
132       table.close();
133     } catch (IOException e) {
134       throw getTIOError(e);
135     }
136   }
137 
138   private TIOError getTIOError(IOException e) {
139     TIOError err = new TIOError();
140     err.setMessage(e.getMessage());
141     return err;
142   }
143 
144   /**
145    * Assigns a unique ID to the scanner and adds the mapping to an internal HashMap.
146    * 
147    * @param scanner to add
148    * @return Id for this Scanner
149    */
150   private int addScanner(ResultScanner scanner) {
151     int id = nextScannerId.getAndIncrement();
152     scannerMap.put(id, scanner);
153     return id;
154   }
155 
156   /**
157    * Returns the Scanner associated with the specified Id.
158    * 
159    * @param id of the Scanner to get
160    * @return a Scanner, or null if the Id is invalid
161    */
162   private ResultScanner getScanner(int id) {
163     return scannerMap.get(id);
164   }
165 
166   /**
167    * Removes the scanner associated with the specified ID from the internal HashMap.
168    * 
169    * @param id of the Scanner to remove
170    * @return the removed Scanner, or <code>null</code> if the Id is invalid
171    */
172   protected ResultScanner removeScanner(int id) {
173     return scannerMap.remove(id);
174   }
175 
176   @Override
177   public boolean exists(ByteBuffer table, TGet get) throws TIOError, TException {
178     HTableInterface htable = getTable(table.array());
179     try {
180       return htable.exists(getFromThrift(get));
181     } catch (IOException e) {
182       throw getTIOError(e);
183     } finally {
184       closeTable(htable);
185     }
186   }
187 
188   @Override
189   public TResult get(ByteBuffer table, TGet get) throws TIOError, TException {
190     HTableInterface htable = getTable(table.array());
191     try {
192       return resultFromHBase(htable.get(getFromThrift(get)));
193     } catch (IOException e) {
194       throw getTIOError(e);
195     } finally {
196       closeTable(htable);
197     }
198   }
199 
200   @Override
201   public List<TResult> getMultiple(ByteBuffer table, List<TGet> gets) throws TIOError, TException {
202     HTableInterface htable = getTable(table.array());
203     try {
204       return resultsFromHBase(htable.get(getsFromThrift(gets)));
205     } catch (IOException e) {
206       throw getTIOError(e);
207     } finally {
208       closeTable(htable);
209     }
210   }
211 
212   @Override
213   public void put(ByteBuffer table, TPut put) throws TIOError, TException {
214     HTableInterface htable = getTable(table.array());
215     try {
216       htable.put(putFromThrift(put));
217     } catch (IOException e) {
218       throw getTIOError(e);
219     } finally {
220       closeTable(htable);
221     }
222   }
223 
224   @Override
225   public boolean checkAndPut(ByteBuffer table, ByteBuffer row, ByteBuffer family, ByteBuffer qualifier, ByteBuffer value, TPut put)
226     throws TIOError, TException {
227     HTableInterface htable = getTable(table.array());
228     try {
229       return htable.checkAndPut(row.array(), family.array(), qualifier.array(), (value == null) ? null : value.array(), putFromThrift(put));
230     } catch (IOException e) {
231       throw getTIOError(e);
232     } finally {
233       closeTable(htable);
234     }
235   }
236 
237   @Override
238   public void putMultiple(ByteBuffer table, List<TPut> puts) throws TIOError, TException {
239     HTableInterface htable = getTable(table.array());
240     try {
241       htable.put(putsFromThrift(puts));
242     } catch (IOException e) {
243       throw getTIOError(e);
244     } finally {
245       closeTable(htable);
246     }
247   }
248 
249   @Override
250   public void deleteSingle(ByteBuffer table, TDelete deleteSingle) throws TIOError, TException {
251     HTableInterface htable = getTable(table.array());
252     try {
253       htable.delete(deleteFromThrift(deleteSingle));
254     } catch (IOException e) {
255       throw getTIOError(e);
256     } finally {
257       closeTable(htable);
258     }
259   }
260 
261   @Override
262   public List<TDelete> deleteMultiple(ByteBuffer table, List<TDelete> deletes) throws TIOError, TException {
263     HTableInterface htable = getTable(table.array());
264     try {
265       htable.delete(deletesFromThrift(deletes));
266     } catch (IOException e) {
267       throw getTIOError(e);
268     } finally {
269       closeTable(htable);
270     }
271     return Collections.emptyList();
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 }