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