View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.thrift;
20  
21  import static org.apache.hadoop.hbase.util.Bytes.getBytes;
22  
23  import java.io.IOException;
24  import java.net.InetAddress;
25  import java.net.InetSocketAddress;
26  import java.net.UnknownHostException;
27  import java.nio.ByteBuffer;
28  import java.util.ArrayList;
29  import java.util.Arrays;
30  import java.util.Collections;
31  import java.util.HashMap;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.TreeMap;
35  import java.util.concurrent.BlockingQueue;
36  import java.util.concurrent.ExecutorService;
37  import java.util.concurrent.LinkedBlockingQueue;
38  import java.util.concurrent.ThreadPoolExecutor;
39  import java.util.concurrent.TimeUnit;
40  
41  import org.apache.commons.cli.CommandLine;
42  import org.apache.commons.cli.Option;
43  import org.apache.commons.cli.OptionGroup;
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.HBaseConfiguration;
49  import org.apache.hadoop.hbase.HColumnDescriptor;
50  import org.apache.hadoop.hbase.HConstants;
51  import org.apache.hadoop.hbase.HRegionInfo;
52  import org.apache.hadoop.hbase.HTableDescriptor;
53  import org.apache.hadoop.hbase.KeyValue;
54  import org.apache.hadoop.hbase.ServerName;
55  import org.apache.hadoop.hbase.TableName;
56  import org.apache.hadoop.hbase.TableNotFoundException;
57  import org.apache.hadoop.hbase.client.Delete;
58  import org.apache.hadoop.hbase.client.Get;
59  import org.apache.hadoop.hbase.client.HBaseAdmin;
60  import org.apache.hadoop.hbase.client.HTable;
61  import org.apache.hadoop.hbase.client.Increment;
62  import org.apache.hadoop.hbase.client.OperationWithAttributes;
63  import org.apache.hadoop.hbase.client.Put;
64  import org.apache.hadoop.hbase.client.Result;
65  import org.apache.hadoop.hbase.client.ResultScanner;
66  import org.apache.hadoop.hbase.client.Scan;
67  import org.apache.hadoop.hbase.client.Durability;
68  import org.apache.hadoop.hbase.filter.Filter;
69  import org.apache.hadoop.hbase.filter.ParseFilter;
70  import org.apache.hadoop.hbase.filter.PrefixFilter;
71  import org.apache.hadoop.hbase.filter.WhileMatchFilter;
72  import org.apache.hadoop.hbase.thrift.CallQueue.Call;
73  import org.apache.hadoop.hbase.thrift.generated.AlreadyExists;
74  import org.apache.hadoop.hbase.thrift.generated.BatchMutation;
75  import org.apache.hadoop.hbase.thrift.generated.ColumnDescriptor;
76  import org.apache.hadoop.hbase.thrift.generated.Hbase;
77  import org.apache.hadoop.hbase.thrift.generated.IOError;
78  import org.apache.hadoop.hbase.thrift.generated.IllegalArgument;
79  import org.apache.hadoop.hbase.thrift.generated.Mutation;
80  import org.apache.hadoop.hbase.thrift.generated.TCell;
81  import org.apache.hadoop.hbase.thrift.generated.TIncrement;
82  import org.apache.hadoop.hbase.thrift.generated.TRegionInfo;
83  import org.apache.hadoop.hbase.thrift.generated.TRowResult;
84  import org.apache.hadoop.hbase.thrift.generated.TScan;
85  import org.apache.hadoop.hbase.util.Bytes;
86  import org.apache.thrift.TException;
87  import org.apache.thrift.protocol.TBinaryProtocol;
88  import org.apache.thrift.protocol.TCompactProtocol;
89  import org.apache.thrift.protocol.TProtocolFactory;
90  import org.apache.thrift.server.THsHaServer;
91  import org.apache.thrift.server.TNonblockingServer;
92  import org.apache.thrift.server.TServer;
93  import org.apache.thrift.server.TThreadedSelectorServer;
94  import org.apache.thrift.transport.TFramedTransport;
95  import org.apache.thrift.transport.TNonblockingServerSocket;
96  import org.apache.thrift.transport.TNonblockingServerTransport;
97  import org.apache.thrift.transport.TServerSocket;
98  import org.apache.thrift.transport.TServerTransport;
99  import org.apache.thrift.transport.TTransportFactory;
100 
101 import com.google.common.base.Joiner;
102 import com.google.common.util.concurrent.ThreadFactoryBuilder;
103 
104 /**
105  * ThriftServerRunner - this class starts up a Thrift server which implements
106  * the Hbase API specified in the Hbase.thrift IDL file.
107  */
108 @InterfaceAudience.Private
109 public class ThriftServerRunner implements Runnable {
110 
111   private static final Log LOG = LogFactory.getLog(ThriftServerRunner.class);
112 
113   static final String SERVER_TYPE_CONF_KEY =
114       "hbase.regionserver.thrift.server.type";
115 
116   static final String BIND_CONF_KEY = "hbase.regionserver.thrift.ipaddress";
117   static final String COMPACT_CONF_KEY = "hbase.regionserver.thrift.compact";
118   static final String FRAMED_CONF_KEY = "hbase.regionserver.thrift.framed";
119   static final String PORT_CONF_KEY = "hbase.regionserver.thrift.port";
120   static final String COALESCE_INC_KEY = "hbase.regionserver.thrift.coalesceIncrement";
121 
122   private static final String DEFAULT_BIND_ADDR = "0.0.0.0";
123   public static final int DEFAULT_LISTEN_PORT = 9090;
124   private final int listenPort;
125 
126   private Configuration conf;
127   volatile TServer tserver;
128   private final Hbase.Iface handler;
129   private final ThriftMetrics metrics;
130 
131   /** An enum of server implementation selections */
132   enum ImplType {
133     HS_HA("hsha", true, THsHaServer.class, true),
134     NONBLOCKING("nonblocking", true, TNonblockingServer.class, true),
135     THREAD_POOL("threadpool", false, TBoundedThreadPoolServer.class, true),
136     THREADED_SELECTOR(
137         "threadedselector", true, TThreadedSelectorServer.class, true);
138 
139     public static final ImplType DEFAULT = THREAD_POOL;
140 
141     final String option;
142     final boolean isAlwaysFramed;
143     final Class<? extends TServer> serverClass;
144     final boolean canSpecifyBindIP;
145 
146     ImplType(String option, boolean isAlwaysFramed,
147         Class<? extends TServer> serverClass, boolean canSpecifyBindIP) {
148       this.option = option;
149       this.isAlwaysFramed = isAlwaysFramed;
150       this.serverClass = serverClass;
151       this.canSpecifyBindIP = canSpecifyBindIP;
152     }
153 
154     /**
155      * @return <code>-option</code> so we can get the list of options from
156      *         {@link #values()}
157      */
158     @Override
159     public String toString() {
160       return "-" + option;
161     }
162 
163     String getDescription() {
164       StringBuilder sb = new StringBuilder("Use the " +
165           serverClass.getSimpleName());
166       if (isAlwaysFramed) {
167         sb.append(" This implies the framed transport.");
168       }
169       if (this == DEFAULT) {
170         sb.append("This is the default.");
171       }
172       return sb.toString();
173     }
174 
175     static OptionGroup createOptionGroup() {
176       OptionGroup group = new OptionGroup();
177       for (ImplType t : values()) {
178         group.addOption(new Option(t.option, t.getDescription()));
179       }
180       return group;
181     }
182 
183     static ImplType getServerImpl(Configuration conf) {
184       String confType = conf.get(SERVER_TYPE_CONF_KEY, THREAD_POOL.option);
185       for (ImplType t : values()) {
186         if (confType.equals(t.option)) {
187           return t;
188         }
189       }
190       throw new AssertionError("Unknown server ImplType.option:" + confType);
191     }
192 
193     static void setServerImpl(CommandLine cmd, Configuration conf) {
194       ImplType chosenType = null;
195       int numChosen = 0;
196       for (ImplType t : values()) {
197         if (cmd.hasOption(t.option)) {
198           chosenType = t;
199           ++numChosen;
200         }
201       }
202       if (numChosen < 1) {
203         LOG.info("Using default thrift server type");
204         chosenType = DEFAULT;
205       } else if (numChosen > 1) {
206         throw new AssertionError("Exactly one option out of " +
207           Arrays.toString(values()) + " has to be specified");
208       }
209       LOG.info("Using thrift server type " + chosenType.option);
210       conf.set(SERVER_TYPE_CONF_KEY, chosenType.option);
211     }
212 
213     public String simpleClassName() {
214       return serverClass.getSimpleName();
215     }
216 
217     public static List<String> serversThatCannotSpecifyBindIP() {
218       List<String> l = new ArrayList<String>();
219       for (ImplType t : values()) {
220         if (!t.canSpecifyBindIP) {
221           l.add(t.simpleClassName());
222         }
223       }
224       return l;
225     }
226 
227   }
228 
229   public ThriftServerRunner(Configuration conf) throws IOException {
230     this(conf, new ThriftServerRunner.HBaseHandler(conf));
231   }
232 
233   public ThriftServerRunner(Configuration conf, HBaseHandler handler) {
234     this.conf = HBaseConfiguration.create(conf);
235     this.listenPort = conf.getInt(PORT_CONF_KEY, DEFAULT_LISTEN_PORT);
236     this.metrics = new ThriftMetrics(conf, ThriftMetrics.ThriftServerType.ONE);
237     handler.initMetrics(metrics);
238     this.handler = HbaseHandlerMetricsProxy.newInstance(handler, metrics, conf);
239   }
240 
241   /*
242    * Runs the Thrift server
243    */
244   @Override
245   public void run() {
246     try {
247       setupServer();
248       tserver.serve();
249     } catch (Exception e) {
250       LOG.fatal("Cannot run ThriftServer", e);
251       // Crash the process if the ThriftServer is not running
252       System.exit(-1);
253     }
254   }
255 
256   public void shutdown() {
257     if (tserver != null) {
258       tserver.stop();
259       tserver = null;
260     }
261   }
262 
263   /**
264    * Setting up the thrift TServer
265    */
266   private void setupServer() throws Exception {
267     // Construct correct ProtocolFactory
268     TProtocolFactory protocolFactory;
269     if (conf.getBoolean(COMPACT_CONF_KEY, false)) {
270       LOG.debug("Using compact protocol");
271       protocolFactory = new TCompactProtocol.Factory();
272     } else {
273       LOG.debug("Using binary protocol");
274       protocolFactory = new TBinaryProtocol.Factory();
275     }
276 
277     Hbase.Processor<Hbase.Iface> processor =
278         new Hbase.Processor<Hbase.Iface>(handler);
279     ImplType implType = ImplType.getServerImpl(conf);
280 
281     // Construct correct TransportFactory
282     TTransportFactory transportFactory;
283     if (conf.getBoolean(FRAMED_CONF_KEY, false) || implType.isAlwaysFramed) {
284       transportFactory = new TFramedTransport.Factory();
285       LOG.debug("Using framed transport");
286     } else {
287       transportFactory = new TTransportFactory();
288     }
289 
290     if (conf.get(BIND_CONF_KEY) != null && !implType.canSpecifyBindIP) {
291       LOG.error("Server types " + Joiner.on(", ").join(
292           ImplType.serversThatCannotSpecifyBindIP()) + " don't support IP " +
293           "address binding at the moment. See " +
294           "https://issues.apache.org/jira/browse/HBASE-2155 for details.");
295       throw new RuntimeException(
296           "-" + BIND_CONF_KEY + " not supported with " + implType);
297     }
298 
299     if (implType == ImplType.HS_HA || implType == ImplType.NONBLOCKING ||
300         implType == ImplType.THREADED_SELECTOR) {
301 
302       InetAddress listenAddress = getBindAddress(conf); 
303       TNonblockingServerTransport serverTransport = new TNonblockingServerSocket(
304           new InetSocketAddress(listenAddress, listenPort));
305 
306       if (implType == ImplType.NONBLOCKING) {
307         TNonblockingServer.Args serverArgs =
308             new TNonblockingServer.Args(serverTransport);
309         serverArgs.processor(processor)
310                   .transportFactory(transportFactory)
311                   .protocolFactory(protocolFactory);
312         tserver = new TNonblockingServer(serverArgs);
313       } else if (implType == ImplType.HS_HA) {
314         THsHaServer.Args serverArgs = new THsHaServer.Args(serverTransport);
315         CallQueue callQueue =
316             new CallQueue(new LinkedBlockingQueue<Call>(), metrics);
317         ExecutorService executorService = createExecutor(
318             callQueue, serverArgs.getWorkerThreads());
319         serverArgs.executorService(executorService)
320                   .processor(processor)
321                   .transportFactory(transportFactory)
322                   .protocolFactory(protocolFactory);
323         tserver = new THsHaServer(serverArgs);
324       } else { // THREADED_SELECTOR
325         TThreadedSelectorServer.Args serverArgs =
326             new HThreadedSelectorServerArgs(serverTransport, conf);
327         CallQueue callQueue =
328             new CallQueue(new LinkedBlockingQueue<Call>(), metrics);
329         ExecutorService executorService = createExecutor(
330             callQueue, serverArgs.getWorkerThreads());
331         serverArgs.executorService(executorService)
332                   .processor(processor)
333                   .transportFactory(transportFactory)
334                   .protocolFactory(protocolFactory);
335         tserver = new TThreadedSelectorServer(serverArgs);
336       }
337       LOG.info("starting HBase " + implType.simpleClassName() +
338           " server on " + Integer.toString(listenPort));
339     } else if (implType == ImplType.THREAD_POOL) {
340       // Thread pool server. Get the IP address to bind to.
341       InetAddress listenAddress = getBindAddress(conf);
342 
343       TServerTransport serverTransport = new TServerSocket(
344           new InetSocketAddress(listenAddress, listenPort));
345 
346       TBoundedThreadPoolServer.Args serverArgs =
347           new TBoundedThreadPoolServer.Args(serverTransport, conf);
348       serverArgs.processor(processor)
349                 .transportFactory(transportFactory)
350                 .protocolFactory(protocolFactory);
351       LOG.info("starting " + ImplType.THREAD_POOL.simpleClassName() + " on "
352           + listenAddress + ":" + Integer.toString(listenPort)
353           + "; " + serverArgs);
354       TBoundedThreadPoolServer tserver =
355           new TBoundedThreadPoolServer(serverArgs, metrics);
356       this.tserver = tserver;
357     } else {
358       throw new AssertionError("Unsupported Thrift server implementation: " +
359           implType.simpleClassName());
360     }
361 
362     // A sanity check that we instantiated the right type of server.
363     if (tserver.getClass() != implType.serverClass) {
364       throw new AssertionError("Expected to create Thrift server class " +
365           implType.serverClass.getName() + " but got " +
366           tserver.getClass().getName());
367     }
368 
369    
370 
371     registerFilters(conf);
372   }
373 
374   ExecutorService createExecutor(BlockingQueue<Runnable> callQueue,
375                                  int workerThreads) {
376     ThreadFactoryBuilder tfb = new ThreadFactoryBuilder();
377     tfb.setDaemon(true);
378     tfb.setNameFormat("thrift-worker-%d");
379     return new ThreadPoolExecutor(workerThreads, workerThreads,
380             Long.MAX_VALUE, TimeUnit.SECONDS, callQueue, tfb.build());
381   }
382 
383   private InetAddress getBindAddress(Configuration conf)
384       throws UnknownHostException {
385     String bindAddressStr = conf.get(BIND_CONF_KEY, DEFAULT_BIND_ADDR);
386     return InetAddress.getByName(bindAddressStr);
387   }
388 
389   protected static class ResultScannerWrapper {
390 
391     private final ResultScanner scanner;
392     private final boolean sortColumns;
393     public ResultScannerWrapper(ResultScanner resultScanner,
394                                 boolean sortResultColumns) {
395       scanner = resultScanner;
396       sortColumns = sortResultColumns;
397    }
398 
399     public ResultScanner getScanner() {
400       return scanner;
401     }
402 
403     public boolean isColumnSorted() {
404       return sortColumns;
405     }
406   }
407 
408   /**
409    * The HBaseHandler is a glue object that connects Thrift RPC calls to the
410    * HBase client API primarily defined in the HBaseAdmin and HTable objects.
411    */
412   public static class HBaseHandler implements Hbase.Iface {
413     protected Configuration conf;
414     protected volatile HBaseAdmin admin = null;
415     protected final Log LOG = LogFactory.getLog(this.getClass().getName());
416 
417     // nextScannerId and scannerMap are used to manage scanner state
418     protected int nextScannerId = 0;
419     protected HashMap<Integer, ResultScannerWrapper> scannerMap = null;
420     private ThriftMetrics metrics = null;
421 
422     private static ThreadLocal<Map<String, HTable>> threadLocalTables =
423         new ThreadLocal<Map<String, HTable>>() {
424       @Override
425       protected Map<String, HTable> initialValue() {
426         return new TreeMap<String, HTable>();
427       }
428     };
429 
430     IncrementCoalescer coalescer = null;
431 
432     /**
433      * Returns a list of all the column families for a given htable.
434      *
435      * @param table
436      * @return
437      * @throws IOException
438      */
439     byte[][] getAllColumns(HTable table) throws IOException {
440       HColumnDescriptor[] cds = table.getTableDescriptor().getColumnFamilies();
441       byte[][] columns = new byte[cds.length][];
442       for (int i = 0; i < cds.length; i++) {
443         columns[i] = Bytes.add(cds[i].getName(),
444             KeyValue.COLUMN_FAMILY_DELIM_ARRAY);
445       }
446       return columns;
447     }
448 
449     /**
450      * Creates and returns an HTable instance from a given table name.
451      *
452      * @param tableName
453      *          name of table
454      * @return HTable object
455      * @throws IOException
456      * @throws IOError
457      */
458     public HTable getTable(final byte[] tableName) throws
459         IOException {
460       String table = new String(tableName);
461       Map<String, HTable> tables = threadLocalTables.get();
462       if (!tables.containsKey(table)) {
463         tables.put(table, new HTable(conf, tableName));
464       }
465       return tables.get(table);
466     }
467 
468     public HTable getTable(final ByteBuffer tableName) throws IOException {
469       return getTable(getBytes(tableName));
470     }
471 
472     /**
473      * Assigns a unique ID to the scanner and adds the mapping to an internal
474      * hash-map.
475      *
476      * @param scanner
477      * @return integer scanner id
478      */
479     protected synchronized int addScanner(ResultScanner scanner,boolean sortColumns) {
480       int id = nextScannerId++;
481       ResultScannerWrapper resultScannerWrapper = new ResultScannerWrapper(scanner, sortColumns);
482       scannerMap.put(id, resultScannerWrapper);
483       return id;
484     }
485 
486     /**
487      * Returns the scanner associated with the specified ID.
488      *
489      * @param id
490      * @return a Scanner, or null if ID was invalid.
491      */
492     protected synchronized ResultScannerWrapper getScanner(int id) {
493       return scannerMap.get(id);
494     }
495 
496     /**
497      * Removes the scanner associated with the specified ID from the internal
498      * id->scanner hash-map.
499      *
500      * @param id
501      * @return a Scanner, or null if ID was invalid.
502      */
503     protected synchronized ResultScannerWrapper removeScanner(int id) {
504       return scannerMap.remove(id);
505     }
506 
507     /**
508      * Constructs an HBaseHandler object.
509      * @throws IOException
510      */
511     protected HBaseHandler()
512     throws IOException {
513       this(HBaseConfiguration.create());
514     }
515 
516     protected HBaseHandler(final Configuration c) throws IOException {
517       this.conf = c;
518       scannerMap = new HashMap<Integer, ResultScannerWrapper>();
519       this.coalescer = new IncrementCoalescer(this);
520     }
521 
522     /**
523      * Obtain HBaseAdmin. Creates the instance if it is not already created.
524      */
525     private HBaseAdmin getHBaseAdmin() throws IOException {
526       if (admin == null) {
527         synchronized (this) {
528           if (admin == null) {
529             admin = new HBaseAdmin(conf);
530           }
531         }
532       }
533       return admin;
534     }
535 
536     @Override
537     public void enableTable(ByteBuffer tableName) throws IOError {
538       try{
539         getHBaseAdmin().enableTable(getBytes(tableName));
540       } catch (IOException e) {
541         LOG.warn(e.getMessage(), e);
542         throw new IOError(e.getMessage());
543       }
544     }
545 
546     @Override
547     public void disableTable(ByteBuffer tableName) throws IOError{
548       try{
549         getHBaseAdmin().disableTable(getBytes(tableName));
550       } catch (IOException e) {
551         LOG.warn(e.getMessage(), e);
552         throw new IOError(e.getMessage());
553       }
554     }
555 
556     @Override
557     public boolean isTableEnabled(ByteBuffer tableName) throws IOError {
558       try {
559         return HTable.isTableEnabled(this.conf, getBytes(tableName));
560       } catch (IOException e) {
561         LOG.warn(e.getMessage(), e);
562         throw new IOError(e.getMessage());
563       }
564     }
565 
566     @Override
567     public void compact(ByteBuffer tableNameOrRegionName) throws IOError {
568       try{
569         getHBaseAdmin().compact(getBytes(tableNameOrRegionName));
570       } catch (InterruptedException e) {
571         throw new IOError(e.getMessage());
572       } catch (IOException e) {
573         LOG.warn(e.getMessage(), e);
574         throw new IOError(e.getMessage());
575       }
576     }
577 
578     @Override
579     public void majorCompact(ByteBuffer tableNameOrRegionName) throws IOError {
580       try{
581         getHBaseAdmin().majorCompact(getBytes(tableNameOrRegionName));
582       } catch (InterruptedException e) {
583         LOG.warn(e.getMessage(), e);
584         throw new IOError(e.getMessage());
585       } catch (IOException e) {
586         LOG.warn(e.getMessage(), e);
587         throw new IOError(e.getMessage());
588       }
589     }
590 
591     @Override
592     public List<ByteBuffer> getTableNames() throws IOError {
593       try {
594         HTableDescriptor[] tables = this.getHBaseAdmin().listTables();
595         ArrayList<ByteBuffer> list = new ArrayList<ByteBuffer>(tables.length);
596         for (int i = 0; i < tables.length; i++) {
597           list.add(ByteBuffer.wrap(tables[i].getTableName().getName()));
598         }
599         return list;
600       } catch (IOException e) {
601         LOG.warn(e.getMessage(), e);
602         throw new IOError(e.getMessage());
603       }
604     }
605 
606     /**
607      * @return the list of regions in the given table, or an empty list if the table does not exist
608      */
609     @Override
610     public List<TRegionInfo> getTableRegions(ByteBuffer tableName)
611     throws IOError {
612       try {
613         HTable table;
614         try {
615           table = getTable(tableName);
616         } catch (TableNotFoundException ex) {
617           return new ArrayList<TRegionInfo>();
618         }
619         Map<HRegionInfo, ServerName> regionLocations =
620             table.getRegionLocations();
621         List<TRegionInfo> results = new ArrayList<TRegionInfo>();
622         for (Map.Entry<HRegionInfo, ServerName> entry :
623             regionLocations.entrySet()) {
624           HRegionInfo info = entry.getKey();
625           ServerName serverName = entry.getValue();
626           TRegionInfo region = new TRegionInfo();
627           region.serverName = ByteBuffer.wrap(
628               Bytes.toBytes(serverName.getHostname()));
629           region.port = serverName.getPort();
630           region.startKey = ByteBuffer.wrap(info.getStartKey());
631           region.endKey = ByteBuffer.wrap(info.getEndKey());
632           region.id = info.getRegionId();
633           region.name = ByteBuffer.wrap(info.getRegionName());
634           region.version = info.getVersion();
635           results.add(region);
636         }
637         return results;
638       } catch (TableNotFoundException e) {
639         // Return empty list for non-existing table
640         return Collections.emptyList();
641       } catch (IOException e){
642         LOG.warn(e.getMessage(), e);
643         throw new IOError(e.getMessage());
644       }
645     }
646 
647     @Deprecated
648     @Override
649     public List<TCell> get(
650         ByteBuffer tableName, ByteBuffer row, ByteBuffer column,
651         Map<ByteBuffer, ByteBuffer> attributes)
652         throws IOError {
653       byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
654       if(famAndQf.length == 1) {
655         return get(tableName, row, famAndQf[0], new byte[0], attributes);
656       }
657       return get(tableName, row, famAndQf[0], famAndQf[1], attributes);
658     }
659 
660     protected List<TCell> get(ByteBuffer tableName,
661                               ByteBuffer row,
662                               byte[] family,
663                               byte[] qualifier,
664                               Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
665       try {
666         HTable table = getTable(tableName);
667         Get get = new Get(getBytes(row));
668         addAttributes(get, attributes);
669         if (qualifier == null || qualifier.length == 0) {
670           get.addFamily(family);
671         } else {
672           get.addColumn(family, qualifier);
673         }
674         Result result = table.get(get);
675         return ThriftUtilities.cellFromHBase(result.raw());
676       } catch (IOException e) {
677         LOG.warn(e.getMessage(), e);
678         throw new IOError(e.getMessage());
679       }
680     }
681 
682     @Deprecated
683     @Override
684     public List<TCell> getVer(ByteBuffer tableName, ByteBuffer row,
685         ByteBuffer column, int numVersions,
686         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
687       byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
688       if(famAndQf.length == 1) {
689         return getVer(tableName, row, famAndQf[0],
690             new byte[0], numVersions, attributes);
691       }
692       return getVer(tableName, row,
693           famAndQf[0], famAndQf[1], numVersions, attributes);
694     }
695 
696     public List<TCell> getVer(ByteBuffer tableName, ByteBuffer row,
697                               byte[] family,
698         byte[] qualifier, int numVersions,
699         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
700       try {
701         HTable table = getTable(tableName);
702         Get get = new Get(getBytes(row));
703         addAttributes(get, attributes);
704         get.addColumn(family, qualifier);
705         get.setMaxVersions(numVersions);
706         Result result = table.get(get);
707         return ThriftUtilities.cellFromHBase(result.raw());
708       } catch (IOException e) {
709         LOG.warn(e.getMessage(), e);
710         throw new IOError(e.getMessage());
711       }
712     }
713 
714     @Deprecated
715     @Override
716     public List<TCell> getVerTs(ByteBuffer tableName,
717                                    ByteBuffer row,
718         ByteBuffer column,
719         long timestamp,
720         int numVersions,
721         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
722       byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
723       if(famAndQf.length == 1) {
724         return getVerTs(tableName, row, famAndQf[0], new byte[0], timestamp,
725             numVersions, attributes);
726       }
727       return getVerTs(tableName, row, famAndQf[0], famAndQf[1], timestamp,
728           numVersions, attributes);
729     }
730 
731     protected List<TCell> getVerTs(ByteBuffer tableName,
732                                    ByteBuffer row, byte [] family,
733         byte [] qualifier, long timestamp, int numVersions,
734         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
735       try {
736         HTable table = getTable(tableName);
737         Get get = new Get(getBytes(row));
738         addAttributes(get, attributes);
739         get.addColumn(family, qualifier);
740         get.setTimeRange(Long.MIN_VALUE, timestamp);
741         get.setMaxVersions(numVersions);
742         Result result = table.get(get);
743         return ThriftUtilities.cellFromHBase(result.raw());
744       } catch (IOException e) {
745         LOG.warn(e.getMessage(), e);
746         throw new IOError(e.getMessage());
747       }
748     }
749 
750     @Override
751     public List<TRowResult> getRow(ByteBuffer tableName, ByteBuffer row,
752         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
753       return getRowWithColumnsTs(tableName, row, null,
754                                  HConstants.LATEST_TIMESTAMP,
755                                  attributes);
756     }
757 
758     @Override
759     public List<TRowResult> getRowWithColumns(ByteBuffer tableName,
760                                               ByteBuffer row,
761         List<ByteBuffer> columns,
762         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
763       return getRowWithColumnsTs(tableName, row, columns,
764                                  HConstants.LATEST_TIMESTAMP,
765                                  attributes);
766     }
767 
768     @Override
769     public List<TRowResult> getRowTs(ByteBuffer tableName, ByteBuffer row,
770         long timestamp, Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
771       return getRowWithColumnsTs(tableName, row, null,
772                                  timestamp, attributes);
773     }
774 
775     @Override
776     public List<TRowResult> getRowWithColumnsTs(
777         ByteBuffer tableName, ByteBuffer row, List<ByteBuffer> columns,
778         long timestamp, Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
779       try {
780         HTable table = getTable(tableName);
781         if (columns == null) {
782           Get get = new Get(getBytes(row));
783           addAttributes(get, attributes);
784           get.setTimeRange(Long.MIN_VALUE, timestamp);
785           Result result = table.get(get);
786           return ThriftUtilities.rowResultFromHBase(result);
787         }
788         Get get = new Get(getBytes(row));
789         addAttributes(get, attributes);
790         for(ByteBuffer column : columns) {
791           byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
792           if (famAndQf.length == 1) {
793               get.addFamily(famAndQf[0]);
794           } else {
795               get.addColumn(famAndQf[0], famAndQf[1]);
796           }
797         }
798         get.setTimeRange(Long.MIN_VALUE, timestamp);
799         Result result = table.get(get);
800         return ThriftUtilities.rowResultFromHBase(result);
801       } catch (IOException e) {
802         LOG.warn(e.getMessage(), e);
803         throw new IOError(e.getMessage());
804       }
805     }
806 
807     @Override
808     public List<TRowResult> getRows(ByteBuffer tableName,
809                                     List<ByteBuffer> rows,
810         Map<ByteBuffer, ByteBuffer> attributes)
811         throws IOError {
812       return getRowsWithColumnsTs(tableName, rows, null,
813                                   HConstants.LATEST_TIMESTAMP,
814                                   attributes);
815     }
816 
817     @Override
818     public List<TRowResult> getRowsWithColumns(ByteBuffer tableName,
819                                                List<ByteBuffer> rows,
820         List<ByteBuffer> columns,
821         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
822       return getRowsWithColumnsTs(tableName, rows, columns,
823                                   HConstants.LATEST_TIMESTAMP,
824                                   attributes);
825     }
826 
827     @Override
828     public List<TRowResult> getRowsTs(ByteBuffer tableName,
829                                       List<ByteBuffer> rows,
830         long timestamp,
831         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
832       return getRowsWithColumnsTs(tableName, rows, null,
833                                   timestamp, attributes);
834     }
835 
836     @Override
837     public List<TRowResult> getRowsWithColumnsTs(ByteBuffer tableName,
838                                                  List<ByteBuffer> rows,
839         List<ByteBuffer> columns, long timestamp,
840         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
841       try {
842         List<Get> gets = new ArrayList<Get>(rows.size());
843         HTable table = getTable(tableName);
844         if (metrics != null) {
845           metrics.incNumRowKeysInBatchGet(rows.size());
846         }
847         for (ByteBuffer row : rows) {
848           Get get = new Get(getBytes(row));
849           addAttributes(get, attributes);
850           if (columns != null) {
851 
852             for(ByteBuffer column : columns) {
853               byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
854               if (famAndQf.length == 1) {
855                 get.addFamily(famAndQf[0]);
856               } else {
857                 get.addColumn(famAndQf[0], famAndQf[1]);
858               }
859             }
860           }
861           get.setTimeRange(Long.MIN_VALUE, timestamp);
862           gets.add(get);
863         }
864         Result[] result = table.get(gets);
865         return ThriftUtilities.rowResultFromHBase(result);
866       } catch (IOException e) {
867         LOG.warn(e.getMessage(), e);
868         throw new IOError(e.getMessage());
869       }
870     }
871 
872     @Override
873     public void deleteAll(
874         ByteBuffer tableName, ByteBuffer row, ByteBuffer column,
875         Map<ByteBuffer, ByteBuffer> attributes)
876         throws IOError {
877       deleteAllTs(tableName, row, column, HConstants.LATEST_TIMESTAMP,
878                   attributes);
879     }
880 
881     @Override
882     public void deleteAllTs(ByteBuffer tableName,
883                             ByteBuffer row,
884                             ByteBuffer column,
885         long timestamp, Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
886       try {
887         HTable table = getTable(tableName);
888         Delete delete  = new Delete(getBytes(row));
889         addAttributes(delete, attributes);
890         byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
891         if (famAndQf.length == 1) {
892           delete.deleteFamily(famAndQf[0], timestamp);
893         } else {
894           delete.deleteColumns(famAndQf[0], famAndQf[1], timestamp);
895         }
896         table.delete(delete);
897 
898       } catch (IOException e) {
899         LOG.warn(e.getMessage(), e);
900         throw new IOError(e.getMessage());
901       }
902     }
903 
904     @Override
905     public void deleteAllRow(
906         ByteBuffer tableName, ByteBuffer row,
907         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
908       deleteAllRowTs(tableName, row, HConstants.LATEST_TIMESTAMP, attributes);
909     }
910 
911     @Override
912     public void deleteAllRowTs(
913         ByteBuffer tableName, ByteBuffer row, long timestamp,
914         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
915       try {
916         HTable table = getTable(tableName);
917         Delete delete  = new Delete(getBytes(row), timestamp);
918         addAttributes(delete, attributes);
919         table.delete(delete);
920       } catch (IOException e) {
921         LOG.warn(e.getMessage(), e);
922         throw new IOError(e.getMessage());
923       }
924     }
925 
926     @Override
927     public void createTable(ByteBuffer in_tableName,
928         List<ColumnDescriptor> columnFamilies) throws IOError,
929         IllegalArgument, AlreadyExists {
930       byte [] tableName = getBytes(in_tableName);
931       try {
932         if (getHBaseAdmin().tableExists(tableName)) {
933           throw new AlreadyExists("table name already in use");
934         }
935         HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
936         for (ColumnDescriptor col : columnFamilies) {
937           HColumnDescriptor colDesc = ThriftUtilities.colDescFromThrift(col);
938           desc.addFamily(colDesc);
939         }
940         getHBaseAdmin().createTable(desc);
941       } catch (IOException e) {
942         LOG.warn(e.getMessage(), e);
943         throw new IOError(e.getMessage());
944       } catch (IllegalArgumentException e) {
945         LOG.warn(e.getMessage(), e);
946         throw new IllegalArgument(e.getMessage());
947       }
948     }
949 
950     @Override
951     public void deleteTable(ByteBuffer in_tableName) throws IOError {
952       byte [] tableName = getBytes(in_tableName);
953       if (LOG.isDebugEnabled()) {
954         LOG.debug("deleteTable: table=" + Bytes.toString(tableName));
955       }
956       try {
957         if (!getHBaseAdmin().tableExists(tableName)) {
958           throw new IOException("table does not exist");
959         }
960         getHBaseAdmin().deleteTable(tableName);
961       } catch (IOException e) {
962         LOG.warn(e.getMessage(), e);
963         throw new IOError(e.getMessage());
964       }
965     }
966 
967     @Override
968     public void mutateRow(ByteBuffer tableName, ByteBuffer row,
969         List<Mutation> mutations, Map<ByteBuffer, ByteBuffer> attributes)
970         throws IOError, IllegalArgument {
971       mutateRowTs(tableName, row, mutations, HConstants.LATEST_TIMESTAMP,
972                   attributes);
973     }
974 
975     @Override
976     public void mutateRowTs(ByteBuffer tableName, ByteBuffer row,
977         List<Mutation> mutations, long timestamp,
978         Map<ByteBuffer, ByteBuffer> attributes)
979         throws IOError, IllegalArgument {
980       HTable table = null;
981       try {
982         table = getTable(tableName);
983         Put put = new Put(getBytes(row), timestamp);
984         addAttributes(put, attributes);
985 
986         Delete delete = new Delete(getBytes(row));
987         addAttributes(delete, attributes);
988         if (metrics != null) {
989           metrics.incNumRowKeysInBatchMutate(mutations.size());
990         }
991 
992         // I apologize for all this mess :)
993         for (Mutation m : mutations) {
994           byte[][] famAndQf = KeyValue.parseColumn(getBytes(m.column));
995           if (m.isDelete) {
996             if (famAndQf.length == 1) {
997               delete.deleteFamily(famAndQf[0], timestamp);
998             } else {
999               delete.deleteColumns(famAndQf[0], famAndQf[1], timestamp);
1000             }
1001             delete.setDurability(m.writeToWAL ? Durability.SYNC_WAL
1002                 : Durability.SKIP_WAL);
1003           } else {
1004             if(famAndQf.length == 1) {
1005               put.add(famAndQf[0], HConstants.EMPTY_BYTE_ARRAY,
1006                   m.value != null ? getBytes(m.value)
1007                       : HConstants.EMPTY_BYTE_ARRAY);
1008             } else {
1009               put.add(famAndQf[0], famAndQf[1],
1010                   m.value != null ? getBytes(m.value)
1011                       : HConstants.EMPTY_BYTE_ARRAY);
1012             }
1013             put.setDurability(m.writeToWAL ? Durability.SYNC_WAL : Durability.SKIP_WAL);
1014           }
1015         }
1016         if (!delete.isEmpty())
1017           table.delete(delete);
1018         if (!put.isEmpty())
1019           table.put(put);
1020       } catch (IOException e) {
1021         LOG.warn(e.getMessage(), e);
1022         throw new IOError(e.getMessage());
1023       } catch (IllegalArgumentException e) {
1024         LOG.warn(e.getMessage(), e);
1025         throw new IllegalArgument(e.getMessage());
1026       }
1027     }
1028 
1029     @Override
1030     public void mutateRows(ByteBuffer tableName, List<BatchMutation> rowBatches,
1031         Map<ByteBuffer, ByteBuffer> attributes)
1032         throws IOError, IllegalArgument, TException {
1033       mutateRowsTs(tableName, rowBatches, HConstants.LATEST_TIMESTAMP, attributes);
1034     }
1035 
1036     @Override
1037     public void mutateRowsTs(
1038         ByteBuffer tableName, List<BatchMutation> rowBatches, long timestamp,
1039         Map<ByteBuffer, ByteBuffer> attributes)
1040         throws IOError, IllegalArgument, TException {
1041       List<Put> puts = new ArrayList<Put>();
1042       List<Delete> deletes = new ArrayList<Delete>();
1043 
1044       for (BatchMutation batch : rowBatches) {
1045         byte[] row = getBytes(batch.row);
1046         List<Mutation> mutations = batch.mutations;
1047         Delete delete = new Delete(row);
1048         addAttributes(delete, attributes);
1049         Put put = new Put(row, timestamp);
1050         addAttributes(put, attributes);
1051         for (Mutation m : mutations) {
1052           byte[][] famAndQf = KeyValue.parseColumn(getBytes(m.column));
1053           if (m.isDelete) {
1054             // no qualifier, family only.
1055             if (famAndQf.length == 1) {
1056               delete.deleteFamily(famAndQf[0], timestamp);
1057             } else {
1058               delete.deleteColumns(famAndQf[0], famAndQf[1], timestamp);
1059             }
1060             delete.setDurability(m.writeToWAL ? Durability.SYNC_WAL
1061                 : Durability.SKIP_WAL);
1062           } else {
1063             if(famAndQf.length == 1) {
1064               put.add(famAndQf[0], HConstants.EMPTY_BYTE_ARRAY,
1065                   m.value != null ? getBytes(m.value)
1066                       : HConstants.EMPTY_BYTE_ARRAY);
1067             } else {
1068               put.add(famAndQf[0], famAndQf[1],
1069                   m.value != null ? getBytes(m.value)
1070                       : HConstants.EMPTY_BYTE_ARRAY);
1071             }
1072             put.setDurability(m.writeToWAL ? Durability.SYNC_WAL : Durability.SKIP_WAL);
1073           }
1074         }
1075         if (!delete.isEmpty())
1076           deletes.add(delete);
1077         if (!put.isEmpty())
1078           puts.add(put);
1079       }
1080 
1081       HTable table = null;
1082       try {
1083         table = getTable(tableName);
1084         if (!puts.isEmpty())
1085           table.put(puts);
1086         if (!deletes.isEmpty())
1087           table.delete(deletes);
1088         
1089       } catch (IOException e) {
1090         LOG.warn(e.getMessage(), e);
1091         throw new IOError(e.getMessage());
1092       } catch (IllegalArgumentException e) {
1093         LOG.warn(e.getMessage(), e);
1094         throw new IllegalArgument(e.getMessage());
1095       }
1096     }
1097 
1098     @Deprecated
1099     @Override
1100     public long atomicIncrement(
1101         ByteBuffer tableName, ByteBuffer row, ByteBuffer column, long amount)
1102             throws IOError, IllegalArgument, TException {
1103       byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
1104       if(famAndQf.length == 1) {
1105         return atomicIncrement(tableName, row, famAndQf[0], new byte[0],
1106             amount);
1107       }
1108       return atomicIncrement(tableName, row, famAndQf[0], famAndQf[1], amount);
1109     }
1110 
1111     protected long atomicIncrement(ByteBuffer tableName, ByteBuffer row,
1112         byte [] family, byte [] qualifier, long amount)
1113         throws IOError, IllegalArgument, TException {
1114       HTable table;
1115       try {
1116         table = getTable(tableName);
1117         return table.incrementColumnValue(
1118             getBytes(row), family, qualifier, amount);
1119       } catch (IOException e) {
1120         LOG.warn(e.getMessage(), e);
1121         throw new IOError(e.getMessage());
1122       }
1123     }
1124 
1125     public void scannerClose(int id) throws IOError, IllegalArgument {
1126       LOG.debug("scannerClose: id=" + id);
1127       ResultScannerWrapper resultScannerWrapper = getScanner(id);
1128       if (resultScannerWrapper == null) {
1129         String message = "scanner ID is invalid";
1130         LOG.warn(message);
1131         throw new IllegalArgument("scanner ID is invalid");
1132       }
1133       resultScannerWrapper.getScanner().close();
1134       removeScanner(id);
1135     }
1136 
1137     @Override
1138     public List<TRowResult> scannerGetList(int id,int nbRows)
1139         throws IllegalArgument, IOError {
1140       LOG.debug("scannerGetList: id=" + id);
1141       ResultScannerWrapper resultScannerWrapper = getScanner(id);
1142       if (null == resultScannerWrapper) {
1143         String message = "scanner ID is invalid";
1144         LOG.warn(message);
1145         throw new IllegalArgument("scanner ID is invalid");
1146       }
1147 
1148       Result [] results = null;
1149       try {
1150         results = resultScannerWrapper.getScanner().next(nbRows);
1151         if (null == results) {
1152           return new ArrayList<TRowResult>();
1153         }
1154       } catch (IOException e) {
1155         LOG.warn(e.getMessage(), e);
1156         throw new IOError(e.getMessage());
1157       }
1158       return ThriftUtilities.rowResultFromHBase(results, resultScannerWrapper.isColumnSorted());
1159     }
1160 
1161     @Override
1162     public List<TRowResult> scannerGet(int id) throws IllegalArgument, IOError {
1163       return scannerGetList(id,1);
1164     }
1165 
1166     public int scannerOpenWithScan(ByteBuffer tableName, TScan tScan,
1167         Map<ByteBuffer, ByteBuffer> attributes)
1168         throws IOError {
1169       try {
1170         HTable table = getTable(tableName);
1171         Scan scan = new Scan();
1172         addAttributes(scan, attributes);
1173         if (tScan.isSetStartRow()) {
1174           scan.setStartRow(tScan.getStartRow());
1175         }
1176         if (tScan.isSetStopRow()) {
1177           scan.setStopRow(tScan.getStopRow());
1178         }
1179         if (tScan.isSetTimestamp()) {
1180           scan.setTimeRange(Long.MIN_VALUE, tScan.getTimestamp());
1181         }
1182         if (tScan.isSetCaching()) {
1183           scan.setCaching(tScan.getCaching());
1184         }
1185         if (tScan.isSetBatchSize()) {
1186           scan.setBatch(tScan.getBatchSize());
1187         }
1188         if (tScan.isSetColumns() && tScan.getColumns().size() != 0) {
1189           for(ByteBuffer column : tScan.getColumns()) {
1190             byte [][] famQf = KeyValue.parseColumn(getBytes(column));
1191             if(famQf.length == 1) {
1192               scan.addFamily(famQf[0]);
1193             } else {
1194               scan.addColumn(famQf[0], famQf[1]);
1195             }
1196           }
1197         }
1198         if (tScan.isSetFilterString()) {
1199           ParseFilter parseFilter = new ParseFilter();
1200           scan.setFilter(
1201               parseFilter.parseFilterString(tScan.getFilterString()));
1202         }
1203         return addScanner(table.getScanner(scan), tScan.sortColumns);
1204       } catch (IOException e) {
1205         LOG.warn(e.getMessage(), e);
1206         throw new IOError(e.getMessage());
1207       }
1208     }
1209 
1210     @Override
1211     public int scannerOpen(ByteBuffer tableName, ByteBuffer startRow,
1212         List<ByteBuffer> columns,
1213         Map<ByteBuffer, ByteBuffer> attributes) throws IOError {
1214       try {
1215         HTable table = getTable(tableName);
1216         Scan scan = new Scan(getBytes(startRow));
1217         addAttributes(scan, attributes);
1218         if(columns != null && columns.size() != 0) {
1219           for(ByteBuffer column : columns) {
1220             byte [][] famQf = KeyValue.parseColumn(getBytes(column));
1221             if(famQf.length == 1) {
1222               scan.addFamily(famQf[0]);
1223             } else {
1224               scan.addColumn(famQf[0], famQf[1]);
1225             }
1226           }
1227         }
1228         return addScanner(table.getScanner(scan), false);
1229       } catch (IOException e) {
1230         LOG.warn(e.getMessage(), e);
1231         throw new IOError(e.getMessage());
1232       }
1233     }
1234 
1235     @Override
1236     public int scannerOpenWithStop(ByteBuffer tableName, ByteBuffer startRow,
1237         ByteBuffer stopRow, List<ByteBuffer> columns,
1238         Map<ByteBuffer, ByteBuffer> attributes)
1239         throws IOError, TException {
1240       try {
1241         HTable table = getTable(tableName);
1242         Scan scan = new Scan(getBytes(startRow), getBytes(stopRow));
1243         addAttributes(scan, attributes);
1244         if(columns != null && columns.size() != 0) {
1245           for(ByteBuffer column : columns) {
1246             byte [][] famQf = KeyValue.parseColumn(getBytes(column));
1247             if(famQf.length == 1) {
1248               scan.addFamily(famQf[0]);
1249             } else {
1250               scan.addColumn(famQf[0], famQf[1]);
1251             }
1252           }
1253         }
1254         return addScanner(table.getScanner(scan), false);
1255       } catch (IOException e) {
1256         LOG.warn(e.getMessage(), e);
1257         throw new IOError(e.getMessage());
1258       }
1259     }
1260 
1261     @Override
1262     public int scannerOpenWithPrefix(ByteBuffer tableName,
1263                                      ByteBuffer startAndPrefix,
1264                                      List<ByteBuffer> columns,
1265         Map<ByteBuffer, ByteBuffer> attributes)
1266         throws IOError, TException {
1267       try {
1268         HTable table = getTable(tableName);
1269         Scan scan = new Scan(getBytes(startAndPrefix));
1270         addAttributes(scan, attributes);
1271         Filter f = new WhileMatchFilter(
1272             new PrefixFilter(getBytes(startAndPrefix)));
1273         scan.setFilter(f);
1274         if (columns != null && columns.size() != 0) {
1275           for(ByteBuffer column : columns) {
1276             byte [][] famQf = KeyValue.parseColumn(getBytes(column));
1277             if(famQf.length == 1) {
1278               scan.addFamily(famQf[0]);
1279             } else {
1280               scan.addColumn(famQf[0], famQf[1]);
1281             }
1282           }
1283         }
1284         return addScanner(table.getScanner(scan), false);
1285       } catch (IOException e) {
1286         LOG.warn(e.getMessage(), e);
1287         throw new IOError(e.getMessage());
1288       }
1289     }
1290 
1291     @Override
1292     public int scannerOpenTs(ByteBuffer tableName, ByteBuffer startRow,
1293         List<ByteBuffer> columns, long timestamp,
1294         Map<ByteBuffer, ByteBuffer> attributes) throws IOError, TException {
1295       try {
1296         HTable table = getTable(tableName);
1297         Scan scan = new Scan(getBytes(startRow));
1298         addAttributes(scan, attributes);
1299         scan.setTimeRange(Long.MIN_VALUE, timestamp);
1300         if (columns != null && columns.size() != 0) {
1301           for (ByteBuffer column : columns) {
1302             byte [][] famQf = KeyValue.parseColumn(getBytes(column));
1303             if(famQf.length == 1) {
1304               scan.addFamily(famQf[0]);
1305             } else {
1306               scan.addColumn(famQf[0], famQf[1]);
1307             }
1308           }
1309         }
1310         return addScanner(table.getScanner(scan), false);
1311       } catch (IOException e) {
1312         LOG.warn(e.getMessage(), e);
1313         throw new IOError(e.getMessage());
1314       }
1315     }
1316 
1317     @Override
1318     public int scannerOpenWithStopTs(ByteBuffer tableName, ByteBuffer startRow,
1319         ByteBuffer stopRow, List<ByteBuffer> columns, long timestamp,
1320         Map<ByteBuffer, ByteBuffer> attributes)
1321         throws IOError, TException {
1322       try {
1323         HTable table = getTable(tableName);
1324         Scan scan = new Scan(getBytes(startRow), getBytes(stopRow));
1325         addAttributes(scan, attributes);
1326         scan.setTimeRange(Long.MIN_VALUE, timestamp);
1327         if (columns != null && columns.size() != 0) {
1328           for (ByteBuffer column : columns) {
1329             byte [][] famQf = KeyValue.parseColumn(getBytes(column));
1330             if(famQf.length == 1) {
1331               scan.addFamily(famQf[0]);
1332             } else {
1333               scan.addColumn(famQf[0], famQf[1]);
1334             }
1335           }
1336         }
1337         scan.setTimeRange(Long.MIN_VALUE, timestamp);
1338         return addScanner(table.getScanner(scan), false);
1339       } catch (IOException e) {
1340         LOG.warn(e.getMessage(), e);
1341         throw new IOError(e.getMessage());
1342       }
1343     }
1344 
1345     @Override
1346     public Map<ByteBuffer, ColumnDescriptor> getColumnDescriptors(
1347         ByteBuffer tableName) throws IOError, TException {
1348       try {
1349         TreeMap<ByteBuffer, ColumnDescriptor> columns =
1350           new TreeMap<ByteBuffer, ColumnDescriptor>();
1351 
1352         HTable table = getTable(tableName);
1353         HTableDescriptor desc = table.getTableDescriptor();
1354 
1355         for (HColumnDescriptor e : desc.getFamilies()) {
1356           ColumnDescriptor col = ThriftUtilities.colDescFromHbase(e);
1357           columns.put(col.name, col);
1358         }
1359         return columns;
1360       } catch (IOException e) {
1361         LOG.warn(e.getMessage(), e);
1362         throw new IOError(e.getMessage());
1363       }
1364     }
1365 
1366     @Override
1367     public List<TCell> getRowOrBefore(ByteBuffer tableName, ByteBuffer row,
1368         ByteBuffer family) throws IOError {
1369       try {
1370         HTable table = getTable(getBytes(tableName));
1371         Result result = table.getRowOrBefore(getBytes(row), getBytes(family));
1372         return ThriftUtilities.cellFromHBase(result.raw());
1373       } catch (IOException e) {
1374         LOG.warn(e.getMessage(), e);
1375         throw new IOError(e.getMessage());
1376       }
1377     }
1378 
1379     @Override
1380     public TRegionInfo getRegionInfo(ByteBuffer searchRow) throws IOError {
1381       try {
1382         HTable table = getTable(TableName.META_TABLE_NAME.getName());
1383         byte[] row = getBytes(searchRow);
1384         Result startRowResult = table.getRowOrBefore(
1385           row, HConstants.CATALOG_FAMILY);
1386 
1387         if (startRowResult == null) {
1388           throw new IOException("Cannot find row in "+ TableName.META_TABLE_NAME+", row="
1389                                 + Bytes.toStringBinary(row));
1390         }
1391 
1392         // find region start and end keys
1393         HRegionInfo regionInfo = HRegionInfo.getHRegionInfo(startRowResult);
1394         if (regionInfo == null) {
1395           throw new IOException("HRegionInfo REGIONINFO was null or " +
1396                                 " empty in Meta for row="
1397                                 + Bytes.toStringBinary(row));
1398         }
1399         TRegionInfo region = new TRegionInfo();
1400         region.setStartKey(regionInfo.getStartKey());
1401         region.setEndKey(regionInfo.getEndKey());
1402         region.id = regionInfo.getRegionId();
1403         region.setName(regionInfo.getRegionName());
1404         region.version = regionInfo.getVersion();
1405 
1406         // find region assignment to server
1407         ServerName serverName = HRegionInfo.getServerName(startRowResult);
1408         if (serverName != null) {
1409           region.setServerName(Bytes.toBytes(serverName.getHostname()));
1410           region.port = serverName.getPort();
1411         }
1412         return region;
1413       } catch (IOException e) {
1414         LOG.warn(e.getMessage(), e);
1415         throw new IOError(e.getMessage());
1416       }
1417     }
1418 
1419     private void initMetrics(ThriftMetrics metrics) {
1420       this.metrics = metrics;
1421     }
1422 
1423     @Override
1424     public void increment(TIncrement tincrement) throws IOError, TException {
1425 
1426       if (tincrement.getRow().length == 0 || tincrement.getTable().length == 0) {
1427         throw new TException("Must supply a table and a row key; can't increment");
1428       }
1429 
1430       if (conf.getBoolean(COALESCE_INC_KEY, false)) {
1431         this.coalescer.queueIncrement(tincrement);
1432         return;
1433       }
1434 
1435       try {
1436         HTable table = getTable(tincrement.getTable());
1437         Increment inc = ThriftUtilities.incrementFromThrift(tincrement);
1438         table.increment(inc);
1439       } catch (IOException e) {
1440         LOG.warn(e.getMessage(), e);
1441         throw new IOError(e.getMessage());
1442       }
1443     }
1444 
1445     @Override
1446     public void incrementRows(List<TIncrement> tincrements) throws IOError, TException {
1447       if (conf.getBoolean(COALESCE_INC_KEY, false)) {
1448         this.coalescer.queueIncrements(tincrements);
1449         return;
1450       }
1451       for (TIncrement tinc : tincrements) {
1452         increment(tinc);
1453       }
1454     }
1455   }
1456 
1457 
1458 
1459   /**
1460    * Adds all the attributes into the Operation object
1461    */
1462   private static void addAttributes(OperationWithAttributes op,
1463     Map<ByteBuffer, ByteBuffer> attributes) {
1464     if (attributes == null || attributes.size() == 0) {
1465       return;
1466     }
1467     for (Map.Entry<ByteBuffer, ByteBuffer> entry : attributes.entrySet()) {
1468       String name = Bytes.toStringBinary(getBytes(entry.getKey()));
1469       byte[] value =  getBytes(entry.getValue());
1470       op.setAttribute(name, value);
1471     }
1472   }
1473 
1474   public static void registerFilters(Configuration conf) {
1475     String[] filters = conf.getStrings("hbase.thrift.filters");
1476     if(filters != null) {
1477       for(String filterClass: filters) {
1478         String[] filterPart = filterClass.split(":");
1479         if(filterPart.length != 2) {
1480           LOG.warn("Invalid filter specification " + filterClass + " - skipping");
1481         } else {
1482           ParseFilter.registerFilter(filterPart[0], filterPart[1]);
1483         }
1484       }
1485     }
1486   }
1487 }