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.*;
22  import static org.apache.thrift.TBaseHelper.byteBufferToByteArray;
23  
24  import java.io.IOException;
25  import java.lang.reflect.InvocationHandler;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.lang.reflect.Proxy;
29  import java.nio.ByteBuffer;
30  import java.util.Collections;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.concurrent.Callable;
34  import java.util.concurrent.ConcurrentHashMap;
35  import java.util.concurrent.ExecutionException;
36  import java.util.concurrent.TimeUnit;
37  import java.util.concurrent.atomic.AtomicInteger;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.apache.hadoop.hbase.classification.InterfaceAudience;
42  import org.apache.hadoop.conf.Configuration;
43  import org.apache.hadoop.hbase.client.HTableFactory;
44  import org.apache.hadoop.hbase.client.HTableInterface;
45  import org.apache.hadoop.hbase.client.HTablePool;
46  import org.apache.hadoop.hbase.client.ResultScanner;
47  import org.apache.hadoop.hbase.client.Table;
48  import org.apache.hadoop.hbase.security.UserProvider;
49  import org.apache.hadoop.hbase.thrift.ThriftMetrics;
50  import org.apache.hadoop.hbase.thrift2.generated.*;
51  import org.apache.hadoop.hbase.util.Bytes;
52  import org.apache.hadoop.hbase.util.ConnectionCache;
53  import org.apache.thrift.TException;
54  
55  import com.google.common.cache.Cache;
56  import com.google.common.cache.CacheBuilder;
57  
58  /**
59   * This class is a glue object that connects Thrift RPC calls to the HBase client API primarily
60   * defined in the HTableInterface.
61   */
62  @InterfaceAudience.Private
63  @SuppressWarnings("deprecation")
64  public class ThriftHBaseServiceHandler implements THBaseService.Iface {
65  
66    // TODO: Size of pool configuraple
67    private final Cache<String, HTablePool> htablePools;
68    private final Callable<? extends HTablePool> htablePoolCreater;
69    private static final Log LOG = LogFactory.getLog(ThriftHBaseServiceHandler.class);
70  
71    // nextScannerId and scannerMap are used to manage scanner state
72    // TODO: Cleanup thread for Scanners, Scanner id wrap
73    private final AtomicInteger nextScannerId = new AtomicInteger(0);
74    private final Map<Integer, ResultScanner> scannerMap =
75        new ConcurrentHashMap<Integer, ResultScanner>();
76  
77    private final ConnectionCache connectionCache;
78    private final HTableFactory tableFactory;
79    private final int maxPoolSize;
80  
81    static final String CLEANUP_INTERVAL = "hbase.thrift.connection.cleanup-interval";
82    static final String MAX_IDLETIME = "hbase.thrift.connection.max-idletime";
83  
84    public static THBaseService.Iface newInstance(
85        THBaseService.Iface handler, ThriftMetrics metrics) {
86      return (THBaseService.Iface) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
87        new Class[] { THBaseService.Iface.class }, new THBaseServiceMetricsProxy(handler, metrics));
88    }
89  
90    private static class THBaseServiceMetricsProxy implements InvocationHandler {
91      private final THBaseService.Iface handler;
92      private final ThriftMetrics metrics;
93  
94      private THBaseServiceMetricsProxy(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) 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("unexpected invocation exception: " + e.getMessage());
111       }
112       return result;
113     }
114   }
115 
116   private static long now() {
117     return System.nanoTime();
118   }
119 
120   ThriftHBaseServiceHandler(final Configuration conf,
121       final UserProvider userProvider) throws IOException {
122     int cleanInterval = conf.getInt(CLEANUP_INTERVAL, 10 * 1000);
123     int maxIdleTime = conf.getInt(MAX_IDLETIME, 10 * 60 * 1000);
124     connectionCache = new ConnectionCache(
125       conf, userProvider, cleanInterval, maxIdleTime);
126     tableFactory = new HTableFactory() {
127       @Override
128       public HTableInterface createHTableInterface(Configuration config,
129           byte[] tableName) {
130         try {
131           return connectionCache.getTable(Bytes.toString(tableName));
132         } catch (IOException ioe) {
133           throw new RuntimeException(ioe);
134         }
135       }
136     };
137     htablePools = CacheBuilder.newBuilder().expireAfterAccess(
138       maxIdleTime, TimeUnit.MILLISECONDS).softValues().concurrencyLevel(4).build();
139     maxPoolSize = conf.getInt("hbase.thrift.htablepool.size.max", 1000);
140     htablePoolCreater = new Callable<HTablePool>() {
141       public HTablePool call() {
142         return new HTablePool(conf, maxPoolSize, tableFactory);
143       }
144     };
145   }
146 
147   private Table getTable(ByteBuffer tableName) {
148     String currentUser = connectionCache.getEffectiveUser();
149     try {
150       HTablePool htablePool = htablePools.get(currentUser, htablePoolCreater);
151       return htablePool.getTable(byteBufferToByteArray(tableName));
152     } catch (ExecutionException ee) {
153       throw new RuntimeException(ee);
154     }
155   }
156 
157   private void closeTable(Table table) throws TIOError {
158     try {
159       table.close();
160     } catch (IOException e) {
161       throw getTIOError(e);
162     }
163   }
164 
165   private TIOError getTIOError(IOException e) {
166     TIOError err = new TIOError();
167     err.setMessage(e.getMessage());
168     return err;
169   }
170 
171   /**
172    * Assigns a unique ID to the scanner and adds the mapping to an internal HashMap.
173    * @param scanner to add
174    * @return Id for this Scanner
175    */
176   private int addScanner(ResultScanner scanner) {
177     int id = nextScannerId.getAndIncrement();
178     scannerMap.put(id, scanner);
179     return id;
180   }
181 
182   /**
183    * Returns the Scanner associated with the specified Id.
184    * @param id of the Scanner to get
185    * @return a Scanner, or null if the Id is invalid
186    */
187   private ResultScanner getScanner(int id) {
188     return scannerMap.get(id);
189   }
190 
191   void setEffectiveUser(String effectiveUser) {
192     connectionCache.setEffectiveUser(effectiveUser);
193   }
194 
195   /**
196    * Removes the scanner associated with the specified ID from the internal HashMap.
197    * @param id of the Scanner to remove
198    * @return the removed Scanner, or <code>null</code> if the Id is invalid
199    */
200   protected ResultScanner removeScanner(int id) {
201     return scannerMap.remove(id);
202   }
203 
204   @Override
205   public boolean exists(ByteBuffer table, TGet get) throws TIOError, TException {
206     Table htable = getTable(table);
207     try {
208       return htable.exists(getFromThrift(get));
209     } catch (IOException e) {
210       throw getTIOError(e);
211     } finally {
212       closeTable(htable);
213     }
214   }
215 
216   @Override
217   public TResult get(ByteBuffer table, TGet get) throws TIOError, TException {
218     Table htable = getTable(table);
219     try {
220       return resultFromHBase(htable.get(getFromThrift(get)));
221     } catch (IOException e) {
222       throw getTIOError(e);
223     } finally {
224       closeTable(htable);
225     }
226   }
227 
228   @Override
229   public List<TResult> getMultiple(ByteBuffer table, List<TGet> gets) throws TIOError, TException {
230     Table htable = getTable(table);
231     try {
232       return resultsFromHBase(htable.get(getsFromThrift(gets)));
233     } catch (IOException e) {
234       throw getTIOError(e);
235     } finally {
236       closeTable(htable);
237     }
238   }
239 
240   @Override
241   public void put(ByteBuffer table, TPut put) throws TIOError, TException {
242     Table htable = getTable(table);
243     try {
244       htable.put(putFromThrift(put));
245     } catch (IOException e) {
246       throw getTIOError(e);
247     } finally {
248       closeTable(htable);
249     }
250   }
251 
252   @Override
253   public boolean checkAndPut(ByteBuffer table, ByteBuffer row, ByteBuffer family,
254       ByteBuffer qualifier, ByteBuffer value, TPut put) throws TIOError, TException {
255     Table htable = getTable(table);
256     try {
257       return htable.checkAndPut(byteBufferToByteArray(row), byteBufferToByteArray(family),
258         byteBufferToByteArray(qualifier), (value == null) ? null : byteBufferToByteArray(value),
259         putFromThrift(put));
260     } catch (IOException e) {
261       throw getTIOError(e);
262     } finally {
263       closeTable(htable);
264     }
265   }
266 
267   @Override
268   public void putMultiple(ByteBuffer table, List<TPut> puts) throws TIOError, TException {
269     Table htable = getTable(table);
270     try {
271       htable.put(putsFromThrift(puts));
272     } catch (IOException e) {
273       throw getTIOError(e);
274     } finally {
275       closeTable(htable);
276     }
277   }
278 
279   @Override
280   public void deleteSingle(ByteBuffer table, TDelete deleteSingle) throws TIOError, TException {
281     Table htable = getTable(table);
282     try {
283       htable.delete(deleteFromThrift(deleteSingle));
284     } catch (IOException e) {
285       throw getTIOError(e);
286     } finally {
287       closeTable(htable);
288     }
289   }
290 
291   @Override
292   public List<TDelete> deleteMultiple(ByteBuffer table, List<TDelete> deletes) throws TIOError,
293       TException {
294     Table htable = getTable(table);
295     try {
296       htable.delete(deletesFromThrift(deletes));
297     } catch (IOException e) {
298       throw getTIOError(e);
299     } finally {
300       closeTable(htable);
301     }
302     return Collections.emptyList();
303   }
304 
305   @Override
306   public boolean checkAndDelete(ByteBuffer table, ByteBuffer row, ByteBuffer family,
307       ByteBuffer qualifier, ByteBuffer value, TDelete deleteSingle) throws TIOError, TException {
308     Table htable = getTable(table);
309 
310     try {
311       if (value == null) {
312         return htable.checkAndDelete(byteBufferToByteArray(row), byteBufferToByteArray(family),
313           byteBufferToByteArray(qualifier), null, deleteFromThrift(deleteSingle));
314       } else {
315         return htable.checkAndDelete(byteBufferToByteArray(row), byteBufferToByteArray(family),
316           byteBufferToByteArray(qualifier), byteBufferToByteArray(value),
317           deleteFromThrift(deleteSingle));
318       }
319     } catch (IOException e) {
320       throw getTIOError(e);
321     } finally {
322       closeTable(htable);
323     }
324   }
325 
326   @Override
327   public TResult increment(ByteBuffer table, TIncrement increment) throws TIOError, TException {
328     Table htable = getTable(table);
329     try {
330       return resultFromHBase(htable.increment(incrementFromThrift(increment)));
331     } catch (IOException e) {
332       throw getTIOError(e);
333     } finally {
334       closeTable(htable);
335     }
336   }
337 
338   @Override
339   public TResult append(ByteBuffer table, TAppend append) throws TIOError, TException {
340     Table htable = getTable(table);
341     try {
342       return resultFromHBase(htable.append(appendFromThrift(append)));
343     } catch (IOException e) {
344       throw getTIOError(e);
345     } finally {
346       closeTable(htable);
347     }
348   }
349 
350   @Override
351   public int openScanner(ByteBuffer table, TScan scan) throws TIOError, TException {
352     Table htable = getTable(table);
353     ResultScanner resultScanner = null;
354     try {
355       resultScanner = htable.getScanner(scanFromThrift(scan));
356     } catch (IOException e) {
357       throw getTIOError(e);
358     } finally {
359       closeTable(htable);
360     }
361     return addScanner(resultScanner);
362   }
363 
364   @Override
365   public List<TResult> getScannerRows(int scannerId, int numRows) throws TIOError,
366       TIllegalArgument, TException {
367     ResultScanner scanner = getScanner(scannerId);
368     if (scanner == null) {
369       TIllegalArgument ex = new TIllegalArgument();
370       ex.setMessage("Invalid scanner Id");
371       throw ex;
372     }
373 
374     try {
375       return resultsFromHBase(scanner.next(numRows));
376     } catch (IOException e) {
377       throw getTIOError(e);
378     }
379   }
380 
381   @Override
382   public List<TResult> getScannerResults(ByteBuffer table, TScan scan, int numRows)
383       throws TIOError, TException {
384     Table htable = getTable(table);
385     List<TResult> results = null;
386     ResultScanner scanner = null;
387     try {
388       scanner = htable.getScanner(scanFromThrift(scan));
389       results = resultsFromHBase(scanner.next(numRows));
390     } catch (IOException e) {
391       throw getTIOError(e);
392     } finally {
393       if (scanner != null) {
394         scanner.close();
395       }
396       closeTable(htable);
397     }
398     return results;
399   }
400 
401   @Override
402   public void closeScanner(int scannerId) throws TIOError, TIllegalArgument, TException {
403     LOG.debug("scannerClose: id=" + scannerId);
404     ResultScanner scanner = getScanner(scannerId);
405     if (scanner == null) {
406       String message = "scanner ID is invalid";
407       LOG.warn(message);
408       TIllegalArgument ex = new TIllegalArgument();
409       ex.setMessage("Invalid scanner Id");
410       throw ex;
411     }
412     scanner.close();
413     removeScanner(scannerId);
414   }
415 
416   @Override
417   public void mutateRow(ByteBuffer table, TRowMutations rowMutations) throws TIOError, TException {
418     Table htable = getTable(table);
419     try {
420       htable.mutateRow(rowMutationsFromThrift(rowMutations));
421     } catch (IOException e) {
422       throw getTIOError(e);
423     } finally {
424       closeTable(htable);
425     }
426   }
427 
428 }