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 java.io.IOException;
22  import java.net.InetAddress;
23  import java.net.InetSocketAddress;
24  import java.net.UnknownHostException;
25  import java.nio.ByteBuffer;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.TreeMap;
32  
33  import org.apache.commons.cli.CommandLine;
34  import org.apache.commons.cli.CommandLineParser;
35  import org.apache.commons.cli.HelpFormatter;
36  import org.apache.commons.cli.Option;
37  import org.apache.commons.cli.OptionGroup;
38  import org.apache.commons.cli.Options;
39  import org.apache.commons.cli.PosixParser;
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  import org.apache.hadoop.conf.Configuration;
43  import org.apache.hadoop.hbase.HBaseConfiguration;
44  import org.apache.hadoop.hbase.HColumnDescriptor;
45  import org.apache.hadoop.hbase.HConstants;
46  import org.apache.hadoop.hbase.HRegionInfo;
47  import org.apache.hadoop.hbase.HTableDescriptor;
48  import org.apache.hadoop.hbase.KeyValue;
49  import org.apache.hadoop.hbase.client.Delete;
50  import org.apache.hadoop.hbase.client.Get;
51  import org.apache.hadoop.hbase.client.HBaseAdmin;
52  import org.apache.hadoop.hbase.client.HTable;
53  import org.apache.hadoop.hbase.client.Put;
54  import org.apache.hadoop.hbase.client.Result;
55  import org.apache.hadoop.hbase.client.ResultScanner;
56  import org.apache.hadoop.hbase.client.Scan;
57  import org.apache.hadoop.hbase.filter.Filter;
58  import org.apache.hadoop.hbase.filter.PrefixFilter;
59  import org.apache.hadoop.hbase.filter.WhileMatchFilter;
60  import org.apache.hadoop.hbase.filter.ParseFilter;
61  import org.apache.hadoop.hbase.thrift.generated.AlreadyExists;
62  import org.apache.hadoop.hbase.thrift.generated.BatchMutation;
63  import org.apache.hadoop.hbase.thrift.generated.ColumnDescriptor;
64  import org.apache.hadoop.hbase.thrift.generated.Hbase;
65  import org.apache.hadoop.hbase.thrift.generated.IOError;
66  import org.apache.hadoop.hbase.thrift.generated.IllegalArgument;
67  import org.apache.hadoop.hbase.thrift.generated.Mutation;
68  import org.apache.hadoop.hbase.thrift.generated.TCell;
69  import org.apache.hadoop.hbase.thrift.generated.TRegionInfo;
70  import org.apache.hadoop.hbase.thrift.generated.TRowResult;
71  import org.apache.hadoop.hbase.thrift.generated.TScan;
72  import org.apache.hadoop.hbase.util.Bytes;
73  import org.apache.hadoop.hbase.util.VersionInfo;
74  import org.apache.thrift.TException;
75  import org.apache.thrift.protocol.TBinaryProtocol;
76  import org.apache.thrift.protocol.TCompactProtocol;
77  import org.apache.thrift.protocol.TProtocolFactory;
78  import org.apache.thrift.server.THsHaServer;
79  import org.apache.thrift.server.TNonblockingServer;
80  import org.apache.thrift.server.TServer;
81  import org.apache.thrift.server.TThreadPoolServer;
82  import org.apache.thrift.transport.TFramedTransport;
83  import org.apache.thrift.transport.TNonblockingServerSocket;
84  import org.apache.thrift.transport.TNonblockingServerTransport;
85  import org.apache.thrift.transport.TServerSocket;
86  import org.apache.thrift.transport.TServerTransport;
87  import org.apache.thrift.transport.TTransportFactory;
88  
89  import static org.apache.hadoop.hbase.util.Bytes.getBytes;
90  
91  /**
92   * ThriftServer - this class starts up a Thrift server which implements the
93   * Hbase API specified in the Hbase.thrift IDL file.
94   */
95  public class ThriftServer {
96  
97    /**
98     * The HBaseHandler is a glue object that connects Thrift RPC calls to the
99     * HBase client API primarily defined in the HBaseAdmin and HTable objects.
100    */
101   public static class HBaseHandler implements Hbase.Iface {
102     protected Configuration conf;
103     protected HBaseAdmin admin = null;
104     protected final Log LOG = LogFactory.getLog(this.getClass().getName());
105 
106     // nextScannerId and scannerMap are used to manage scanner state
107     protected int nextScannerId = 0;
108     protected HashMap<Integer, ResultScanner> scannerMap = null;
109 
110     private static ThreadLocal<Map<String, HTable>> threadLocalTables = new ThreadLocal<Map<String, HTable>>() {
111       @Override
112       protected Map<String, HTable> initialValue() {
113         return new TreeMap<String, HTable>();
114       }
115     };
116 
117     /**
118      * Returns a list of all the column families for a given htable.
119      *
120      * @param table
121      * @return
122      * @throws IOException
123      */
124     byte[][] getAllColumns(HTable table) throws IOException {
125       HColumnDescriptor[] cds = table.getTableDescriptor().getColumnFamilies();
126       byte[][] columns = new byte[cds.length][];
127       for (int i = 0; i < cds.length; i++) {
128         columns[i] = Bytes.add(cds[i].getName(),
129             KeyValue.COLUMN_FAMILY_DELIM_ARRAY);
130       }
131       return columns;
132     }
133 
134     /**
135      * Creates and returns an HTable instance from a given table name.
136      *
137      * @param tableName
138      *          name of table
139      * @return HTable object
140      * @throws IOException
141      * @throws IOError
142      */
143     protected HTable getTable(final byte[] tableName) throws
144         IOException {
145       String table = new String(tableName);
146       Map<String, HTable> tables = threadLocalTables.get();
147       if (!tables.containsKey(table)) {
148         tables.put(table, new HTable(conf, tableName));
149       }
150       return tables.get(table);
151     }
152 
153     protected HTable getTable(final ByteBuffer tableName) throws IOException {
154       return getTable(getBytes(tableName));
155     }
156 
157     /**
158      * Assigns a unique ID to the scanner and adds the mapping to an internal
159      * hash-map.
160      *
161      * @param scanner
162      * @return integer scanner id
163      */
164     protected synchronized int addScanner(ResultScanner scanner) {
165       int id = nextScannerId++;
166       scannerMap.put(id, scanner);
167       return id;
168     }
169 
170     /**
171      * Returns the scanner associated with the specified ID.
172      *
173      * @param id
174      * @return a Scanner, or null if ID was invalid.
175      */
176     protected synchronized ResultScanner getScanner(int id) {
177       return scannerMap.get(id);
178     }
179 
180     /**
181      * Removes the scanner associated with the specified ID from the internal
182      * id->scanner hash-map.
183      *
184      * @param id
185      * @return a Scanner, or null if ID was invalid.
186      */
187     protected synchronized ResultScanner removeScanner(int id) {
188       return scannerMap.remove(id);
189     }
190 
191     /**
192      * Constructs an HBaseHandler object.
193      * @throws IOException
194      */
195     HBaseHandler()
196     throws IOException {
197       this(HBaseConfiguration.create());
198     }
199 
200     HBaseHandler(final Configuration c)
201     throws IOException {
202       this.conf = c;
203       admin = new HBaseAdmin(conf);
204       scannerMap = new HashMap<Integer, ResultScanner>();
205     }
206 
207     @Override
208     public void enableTable(ByteBuffer tableName) throws IOError {
209       try{
210         admin.enableTable(getBytes(tableName));
211       } catch (IOException e) {
212         throw new IOError(e.getMessage());
213       }
214     }
215 
216     @Override
217     public void disableTable(ByteBuffer tableName) throws IOError{
218       try{
219         admin.disableTable(getBytes(tableName));
220       } catch (IOException e) {
221         throw new IOError(e.getMessage());
222       }
223     }
224 
225     @Override
226     public boolean isTableEnabled(ByteBuffer tableName) throws IOError {
227       try {
228         return HTable.isTableEnabled(this.conf, getBytes(tableName));
229       } catch (IOException e) {
230         throw new IOError(e.getMessage());
231       }
232     }
233 
234     @Override
235     public void compact(ByteBuffer tableNameOrRegionName) throws IOError {
236       try{
237         admin.compact(getBytes(tableNameOrRegionName));
238       } catch (InterruptedException e) {
239         throw new IOError(e.getMessage());
240       } catch (IOException e) {
241         throw new IOError(e.getMessage());
242       }
243     }
244 
245     @Override
246     public void majorCompact(ByteBuffer tableNameOrRegionName) throws IOError {
247       try{
248         admin.majorCompact(getBytes(tableNameOrRegionName));
249       } catch (InterruptedException e) {
250         throw new IOError(e.getMessage());
251       } catch (IOException e) {
252         throw new IOError(e.getMessage());
253       }
254     }
255 
256     @Override
257     public List<ByteBuffer> getTableNames() throws IOError {
258       try {
259         HTableDescriptor[] tables = this.admin.listTables();
260         ArrayList<ByteBuffer> list = new ArrayList<ByteBuffer>(tables.length);
261         for (int i = 0; i < tables.length; i++) {
262           list.add(ByteBuffer.wrap(tables[i].getName()));
263         }
264         return list;
265       } catch (IOException e) {
266         throw new IOError(e.getMessage());
267       }
268     }
269 
270     @Override
271     public List<TRegionInfo> getTableRegions(ByteBuffer tableName)
272     throws IOError {
273       try{
274         List<HRegionInfo> hris = this.admin.getTableRegions(tableName.array());
275         List<TRegionInfo> regions = new ArrayList<TRegionInfo>();
276 
277         if (hris != null) {
278           for (HRegionInfo regionInfo : hris){
279             TRegionInfo region = new TRegionInfo();
280             region.startKey = ByteBuffer.wrap(regionInfo.getStartKey());
281             region.endKey = ByteBuffer.wrap(regionInfo.getEndKey());
282             region.id = regionInfo.getRegionId();
283             region.name = ByteBuffer.wrap(regionInfo.getRegionName());
284             region.version = regionInfo.getVersion();
285             regions.add(region);
286           }
287         }
288         return regions;
289       } catch (IOException e){
290         throw new IOError(e.getMessage());
291       }
292     }
293 
294     @Deprecated
295     @Override
296     public List<TCell> get(ByteBuffer tableName, ByteBuffer row, ByteBuffer column)
297         throws IOError {
298       byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
299       if(famAndQf.length == 1) {
300         return get(tableName, row, famAndQf[0], new byte[0]);
301       }
302       return get(tableName, row, famAndQf[0], famAndQf[1]);
303     }
304 
305     protected List<TCell> get(ByteBuffer tableName,
306                               ByteBuffer row,
307                               byte[] family,
308                               byte[] qualifier) throws IOError {
309       try {
310         HTable table = getTable(tableName);
311         Get get = new Get(getBytes(row));
312         if (qualifier == null || qualifier.length == 0) {
313           get.addFamily(family);
314         } else {
315           get.addColumn(family, qualifier);
316         }
317         Result result = table.get(get);
318         return ThriftUtilities.cellFromHBase(result.raw());
319       } catch (IOException e) {
320         throw new IOError(e.getMessage());
321       }
322     }
323 
324     @Deprecated
325     @Override
326     public List<TCell> getVer(ByteBuffer tableName, ByteBuffer row,
327         ByteBuffer column, int numVersions) throws IOError {
328       byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
329       if(famAndQf.length == 1) {
330         return getVer(tableName, row, famAndQf[0],
331             new byte[0], numVersions);
332       }
333       return getVer(tableName, row,
334           famAndQf[0], famAndQf[1], numVersions);
335     }
336 
337     public List<TCell> getVer(ByteBuffer tableName, ByteBuffer row,
338                               byte[] family,
339         byte[] qualifier, int numVersions) throws IOError {
340       try {
341         HTable table = getTable(tableName);
342         Get get = new Get(getBytes(row));
343         get.addColumn(family, qualifier);
344         get.setMaxVersions(numVersions);
345         Result result = table.get(get);
346         return ThriftUtilities.cellFromHBase(result.raw());
347       } catch (IOException e) {
348         throw new IOError(e.getMessage());
349       }
350     }
351 
352     @Deprecated
353     @Override
354     public List<TCell> getVerTs(ByteBuffer tableName,
355                                    ByteBuffer row,
356         ByteBuffer column,
357         long timestamp,
358         int numVersions) throws IOError {
359       byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
360       if(famAndQf.length == 1) {
361         return getVerTs(tableName, row, famAndQf[0], new byte[0], timestamp,
362             numVersions);
363       }
364       return getVerTs(tableName, row, famAndQf[0], famAndQf[1], timestamp,
365           numVersions);
366     }
367 
368     protected List<TCell> getVerTs(ByteBuffer tableName,
369                                    ByteBuffer row, byte [] family,
370         byte [] qualifier, long timestamp, int numVersions) throws IOError {
371       try {
372         HTable table = getTable(tableName);
373         Get get = new Get(getBytes(row));
374         get.addColumn(family, qualifier);
375         get.setTimeRange(Long.MIN_VALUE, timestamp);
376         get.setMaxVersions(numVersions);
377         Result result = table.get(get);
378         return ThriftUtilities.cellFromHBase(result.raw());
379       } catch (IOException e) {
380         throw new IOError(e.getMessage());
381       }
382     }
383 
384     @Override
385     public List<TRowResult> getRow(ByteBuffer tableName, ByteBuffer row)
386         throws IOError {
387       return getRowWithColumnsTs(tableName, row, null,
388                                  HConstants.LATEST_TIMESTAMP);
389     }
390 
391     @Override
392     public List<TRowResult> getRowWithColumns(ByteBuffer tableName,
393                                               ByteBuffer row,
394         List<ByteBuffer> columns) throws IOError {
395       return getRowWithColumnsTs(tableName, row, columns,
396                                  HConstants.LATEST_TIMESTAMP);
397     }
398 
399     @Override
400     public List<TRowResult> getRowTs(ByteBuffer tableName, ByteBuffer row,
401         long timestamp) throws IOError {
402       return getRowWithColumnsTs(tableName, row, null,
403                                  timestamp);
404     }
405 
406     @Override
407     public List<TRowResult> getRowWithColumnsTs(ByteBuffer tableName, ByteBuffer row,
408         List<ByteBuffer> columns, long timestamp) throws IOError {
409       try {
410         HTable table = getTable(tableName);
411         if (columns == null) {
412           Get get = new Get(getBytes(row));
413           get.setTimeRange(Long.MIN_VALUE, timestamp);
414           Result result = table.get(get);
415           return ThriftUtilities.rowResultFromHBase(result);
416         }
417         Get get = new Get(getBytes(row));
418         for(ByteBuffer column : columns) {
419           byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
420           if (famAndQf.length == 1) {
421               get.addFamily(famAndQf[0]);
422           } else {
423               get.addColumn(famAndQf[0], famAndQf[1]);
424           }
425         }
426         get.setTimeRange(Long.MIN_VALUE, timestamp);
427         Result result = table.get(get);
428         return ThriftUtilities.rowResultFromHBase(result);
429       } catch (IOException e) {
430         throw new IOError(e.getMessage());
431       }
432     }
433 
434     @Override
435     public List<TRowResult> getRows(ByteBuffer tableName,
436                                     List<ByteBuffer> rows)
437         throws IOError {
438       return getRowsWithColumnsTs(tableName, rows, null,
439                                   HConstants.LATEST_TIMESTAMP);
440     }
441 
442     @Override
443     public List<TRowResult> getRowsWithColumns(ByteBuffer tableName,
444                                                List<ByteBuffer> rows,
445         List<ByteBuffer> columns) throws IOError {
446       return getRowsWithColumnsTs(tableName, rows, columns,
447                                   HConstants.LATEST_TIMESTAMP);
448     }
449 
450     @Override
451     public List<TRowResult> getRowsTs(ByteBuffer tableName,
452                                       List<ByteBuffer> rows,
453         long timestamp) throws IOError {
454       return getRowsWithColumnsTs(tableName, rows, null,
455                                   timestamp);
456     }
457 
458     @Override
459     public List<TRowResult> getRowsWithColumnsTs(ByteBuffer tableName,
460                                                  List<ByteBuffer> rows,
461         List<ByteBuffer> columns, long timestamp) throws IOError {
462       try {
463         List<Get> gets = new ArrayList<Get>(rows.size());
464         HTable table = getTable(tableName);
465         for (ByteBuffer row : rows) {
466           Get get = new Get(getBytes(row));
467           if (columns != null) {
468 
469             for(ByteBuffer column : columns) {
470               byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
471               if (famAndQf.length == 1) {
472                 get.addFamily(famAndQf[0]);
473               } else {
474                 get.addColumn(famAndQf[0], famAndQf[1]);
475               }
476             }
477             get.setTimeRange(Long.MIN_VALUE, timestamp);
478           }
479           gets.add(get);
480         }
481         Result[] result = table.get(gets);
482         return ThriftUtilities.rowResultFromHBase(result);
483       } catch (IOException e) {
484         throw new IOError(e.getMessage());
485       }
486     }
487 
488     @Override
489     public void deleteAll(ByteBuffer tableName, ByteBuffer row, ByteBuffer column)
490         throws IOError {
491       deleteAllTs(tableName, row, column, HConstants.LATEST_TIMESTAMP);
492     }
493 
494     @Override
495     public void deleteAllTs(ByteBuffer tableName,
496                             ByteBuffer row,
497                             ByteBuffer column,
498         long timestamp) throws IOError {
499       try {
500         HTable table = getTable(tableName);
501         Delete delete  = new Delete(getBytes(row));
502         byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
503         if (famAndQf.length == 1) {
504           delete.deleteFamily(famAndQf[0], timestamp);
505         } else {
506           delete.deleteColumns(famAndQf[0], famAndQf[1], timestamp);
507         }
508         table.delete(delete);
509 
510       } catch (IOException e) {
511         throw new IOError(e.getMessage());
512       }
513     }
514 
515     @Override
516     public void deleteAllRow(ByteBuffer tableName, ByteBuffer row) throws IOError {
517       deleteAllRowTs(tableName, row, HConstants.LATEST_TIMESTAMP);
518     }
519 
520     @Override
521     public void deleteAllRowTs(ByteBuffer tableName, ByteBuffer row, long timestamp)
522         throws IOError {
523       try {
524         HTable table = getTable(tableName);
525         Delete delete  = new Delete(getBytes(row), timestamp, null);
526         table.delete(delete);
527       } catch (IOException e) {
528         throw new IOError(e.getMessage());
529       }
530     }
531 
532     @Override
533     public void createTable(ByteBuffer in_tableName,
534         List<ColumnDescriptor> columnFamilies) throws IOError,
535         IllegalArgument, AlreadyExists {
536       byte [] tableName = getBytes(in_tableName);
537       try {
538         if (admin.tableExists(tableName)) {
539           throw new AlreadyExists("table name already in use");
540         }
541         HTableDescriptor desc = new HTableDescriptor(tableName);
542         for (ColumnDescriptor col : columnFamilies) {
543           HColumnDescriptor colDesc = ThriftUtilities.colDescFromThrift(col);
544           desc.addFamily(colDesc);
545         }
546         admin.createTable(desc);
547       } catch (IOException e) {
548         throw new IOError(e.getMessage());
549       } catch (IllegalArgumentException e) {
550         throw new IllegalArgument(e.getMessage());
551       }
552     }
553 
554     @Override
555     public void deleteTable(ByteBuffer in_tableName) throws IOError {
556       byte [] tableName = getBytes(in_tableName);
557       if (LOG.isDebugEnabled()) {
558         LOG.debug("deleteTable: table=" + Bytes.toString(tableName));
559       }
560       try {
561         if (!admin.tableExists(tableName)) {
562           throw new IOError("table does not exist");
563         }
564         admin.deleteTable(tableName);
565       } catch (IOException e) {
566         throw new IOError(e.getMessage());
567       }
568     }
569 
570     @Override
571     public void mutateRow(ByteBuffer tableName, ByteBuffer row,
572         List<Mutation> mutations) throws IOError, IllegalArgument {
573       mutateRowTs(tableName, row, mutations, HConstants.LATEST_TIMESTAMP);
574     }
575 
576     @Override
577     public void mutateRowTs(ByteBuffer tableName, ByteBuffer row,
578         List<Mutation> mutations, long timestamp) throws IOError, IllegalArgument {
579       HTable table = null;
580       try {
581         table = getTable(tableName);
582         Put put = new Put(getBytes(row), timestamp, null);
583 
584         Delete delete = new Delete(getBytes(row));
585 
586         // I apologize for all this mess :)
587         for (Mutation m : mutations) {
588           byte[][] famAndQf = KeyValue.parseColumn(getBytes(m.column));
589           if (m.isDelete) {
590             if (famAndQf.length == 1) {
591               delete.deleteFamily(famAndQf[0], timestamp);
592             } else {
593               delete.deleteColumns(famAndQf[0], famAndQf[1], timestamp);
594             }
595           } else {
596             if(famAndQf.length == 1) {
597               put.add(famAndQf[0], HConstants.EMPTY_BYTE_ARRAY,
598                   m.value != null ? m.value.array()
599                       : HConstants.EMPTY_BYTE_ARRAY);
600             } else {
601               put.add(famAndQf[0], famAndQf[1],
602                   m.value != null ? m.value.array()
603                       : HConstants.EMPTY_BYTE_ARRAY);
604             }
605           }
606         }
607         if (!delete.isEmpty())
608           table.delete(delete);
609         if (!put.isEmpty())
610           table.put(put);
611       } catch (IOException e) {
612         throw new IOError(e.getMessage());
613       } catch (IllegalArgumentException e) {
614         throw new IllegalArgument(e.getMessage());
615       }
616     }
617 
618     @Override
619     public void mutateRows(ByteBuffer tableName, List<BatchMutation> rowBatches)
620         throws IOError, IllegalArgument, TException {
621       mutateRowsTs(tableName, rowBatches, HConstants.LATEST_TIMESTAMP);
622     }
623 
624     @Override
625     public void mutateRowsTs(ByteBuffer tableName, List<BatchMutation> rowBatches, long timestamp)
626         throws IOError, IllegalArgument, TException {
627       List<Put> puts = new ArrayList<Put>();
628       List<Delete> deletes = new ArrayList<Delete>();
629 
630       for (BatchMutation batch : rowBatches) {
631         byte[] row = getBytes(batch.row);
632         List<Mutation> mutations = batch.mutations;
633         Delete delete = new Delete(row);
634         Put put = new Put(row, timestamp, null);
635         for (Mutation m : mutations) {
636           byte[][] famAndQf = KeyValue.parseColumn(getBytes(m.column));
637           if (m.isDelete) {
638             // no qualifier, family only.
639             if (famAndQf.length == 1) {
640               delete.deleteFamily(famAndQf[0], timestamp);
641             } else {
642               delete.deleteColumns(famAndQf[0], famAndQf[1], timestamp);
643             }
644           } else {
645             if(famAndQf.length == 1) {
646               put.add(famAndQf[0], HConstants.EMPTY_BYTE_ARRAY,
647                   m.value != null ? m.value.array()
648                       : HConstants.EMPTY_BYTE_ARRAY);
649             } else {
650               put.add(famAndQf[0], famAndQf[1],
651                   m.value != null ? m.value.array()
652                       : HConstants.EMPTY_BYTE_ARRAY);
653             }
654           }
655         }
656         if (!delete.isEmpty())
657           deletes.add(delete);
658         if (!put.isEmpty())
659           puts.add(put);
660       }
661 
662       HTable table = null;
663       try {
664         table = getTable(tableName);
665         if (!puts.isEmpty())
666           table.put(puts);
667         for (Delete del : deletes) {
668           table.delete(del);
669         }
670       } catch (IOException e) {
671         throw new IOError(e.getMessage());
672       } catch (IllegalArgumentException e) {
673         throw new IllegalArgument(e.getMessage());
674       }
675     }
676 
677     @Deprecated
678     @Override
679     public long atomicIncrement(ByteBuffer tableName, ByteBuffer row, ByteBuffer column,
680         long amount) throws IOError, IllegalArgument, TException {
681       byte [][] famAndQf = KeyValue.parseColumn(getBytes(column));
682       if(famAndQf.length == 1) {
683         return atomicIncrement(tableName, row, famAndQf[0], new byte[0],
684             amount);
685       }
686       return atomicIncrement(tableName, row, famAndQf[0], famAndQf[1], amount);
687     }
688 
689     protected long atomicIncrement(ByteBuffer tableName, ByteBuffer row, byte [] family,
690         byte [] qualifier, long amount)
691     throws IOError, IllegalArgument, TException {
692       HTable table;
693       try {
694         table = getTable(tableName);
695         return table.incrementColumnValue(getBytes(row), family, qualifier, amount);
696       } catch (IOException e) {
697         throw new IOError(e.getMessage());
698       }
699     }
700 
701     public void scannerClose(int id) throws IOError, IllegalArgument {
702       LOG.debug("scannerClose: id=" + id);
703       ResultScanner scanner = getScanner(id);
704       if (scanner == null) {
705         throw new IllegalArgument("scanner ID is invalid");
706       }
707       scanner.close();
708       removeScanner(id);
709     }
710 
711     @Override
712     public List<TRowResult> scannerGetList(int id,int nbRows) throws IllegalArgument, IOError {
713         LOG.debug("scannerGetList: id=" + id);
714         ResultScanner scanner = getScanner(id);
715         if (null == scanner) {
716             throw new IllegalArgument("scanner ID is invalid");
717         }
718 
719         Result [] results = null;
720         try {
721             results = scanner.next(nbRows);
722             if (null == results) {
723                 return new ArrayList<TRowResult>();
724             }
725         } catch (IOException e) {
726             throw new IOError(e.getMessage());
727         }
728         return ThriftUtilities.rowResultFromHBase(results);
729     }
730     @Override
731     public List<TRowResult> scannerGet(int id) throws IllegalArgument, IOError {
732         return scannerGetList(id,1);
733     }
734 
735     public int scannerOpenWithScan(ByteBuffer tableName, TScan tScan) throws IOError {
736         try {
737           HTable table = getTable(tableName);
738           Scan scan = new Scan();
739           if (tScan.isSetStartRow()) {
740               scan.setStartRow(tScan.getStartRow());
741           }
742           if (tScan.isSetStopRow()) {
743               scan.setStopRow(tScan.getStopRow());
744           }
745           if (tScan.isSetTimestamp()) {
746               scan.setTimeRange(Long.MIN_VALUE, tScan.getTimestamp());              
747           }
748           if (tScan.isSetCaching()) {
749               scan.setCaching(tScan.getCaching());
750           }
751           if(tScan.isSetColumns() && tScan.getColumns().size() != 0) {
752             for(ByteBuffer column : tScan.getColumns()) {
753               byte [][] famQf = KeyValue.parseColumn(getBytes(column));
754               if(famQf.length == 1) {
755                 scan.addFamily(famQf[0]);
756               } else {
757                 scan.addColumn(famQf[0], famQf[1]);
758               }
759             }
760           }
761           if (tScan.isSetFilterString()) {
762             ParseFilter parseFilter = new ParseFilter();
763             scan.setFilter(parseFilter.parseFilterString(tScan.getFilterString()));
764           }
765           return addScanner(table.getScanner(scan));
766         } catch (IOException e) {
767           throw new IOError(e.getMessage());
768         }
769     }
770 
771     @Override
772     public int scannerOpen(ByteBuffer tableName, ByteBuffer startRow,
773             List<ByteBuffer> columns) throws IOError {
774         try {
775           HTable table = getTable(tableName);
776           Scan scan = new Scan(getBytes(startRow));
777           if(columns != null && columns.size() != 0) {
778             for(ByteBuffer column : columns) {
779               byte [][] famQf = KeyValue.parseColumn(getBytes(column));
780               if(famQf.length == 1) {
781                 scan.addFamily(famQf[0]);
782               } else {
783                 scan.addColumn(famQf[0], famQf[1]);
784               }
785             }
786           }
787           return addScanner(table.getScanner(scan));
788         } catch (IOException e) {
789           throw new IOError(e.getMessage());
790         }
791     }
792 
793     @Override
794     public int scannerOpenWithStop(ByteBuffer tableName, ByteBuffer startRow,
795         ByteBuffer stopRow, List<ByteBuffer> columns) throws IOError, TException {
796       try {
797         HTable table = getTable(tableName);
798         Scan scan = new Scan(getBytes(startRow), getBytes(stopRow));
799         if(columns != null && columns.size() != 0) {
800           for(ByteBuffer column : columns) {
801             byte [][] famQf = KeyValue.parseColumn(getBytes(column));
802             if(famQf.length == 1) {
803               scan.addFamily(famQf[0]);
804             } else {
805               scan.addColumn(famQf[0], famQf[1]);
806             }
807           }
808         }
809         return addScanner(table.getScanner(scan));
810       } catch (IOException e) {
811         throw new IOError(e.getMessage());
812       }
813     }
814 
815     @Override
816     public int scannerOpenWithPrefix(ByteBuffer tableName,
817                                      ByteBuffer startAndPrefix,
818                                      List<ByteBuffer> columns)
819         throws IOError, TException {
820       try {
821         HTable table = getTable(tableName);
822         Scan scan = new Scan(getBytes(startAndPrefix));
823         Filter f = new WhileMatchFilter(
824             new PrefixFilter(getBytes(startAndPrefix)));
825         scan.setFilter(f);
826         if(columns != null && columns.size() != 0) {
827           for(ByteBuffer column : columns) {
828             byte [][] famQf = KeyValue.parseColumn(getBytes(column));
829             if(famQf.length == 1) {
830               scan.addFamily(famQf[0]);
831             } else {
832               scan.addColumn(famQf[0], famQf[1]);
833             }
834           }
835         }
836         return addScanner(table.getScanner(scan));
837       } catch (IOException e) {
838         throw new IOError(e.getMessage());
839       }
840     }
841 
842     @Override
843     public int scannerOpenTs(ByteBuffer tableName, ByteBuffer startRow,
844         List<ByteBuffer> columns, long timestamp) throws IOError, TException {
845       try {
846         HTable table = getTable(tableName);
847         Scan scan = new Scan(getBytes(startRow));
848         scan.setTimeRange(Long.MIN_VALUE, timestamp);
849         if(columns != null && columns.size() != 0) {
850           for(ByteBuffer column : columns) {
851             byte [][] famQf = KeyValue.parseColumn(getBytes(column));
852             if(famQf.length == 1) {
853               scan.addFamily(famQf[0]);
854             } else {
855               scan.addColumn(famQf[0], famQf[1]);
856             }
857           }
858         }
859         return addScanner(table.getScanner(scan));
860       } catch (IOException e) {
861         throw new IOError(e.getMessage());
862       }
863     }
864 
865     @Override
866     public int scannerOpenWithStopTs(ByteBuffer tableName, ByteBuffer startRow,
867         ByteBuffer stopRow, List<ByteBuffer> columns, long timestamp)
868         throws IOError, TException {
869       try {
870         HTable table = getTable(tableName);
871         Scan scan = new Scan(getBytes(startRow), getBytes(stopRow));
872         scan.setTimeRange(Long.MIN_VALUE, timestamp);
873         if(columns != null && columns.size() != 0) {
874           for(ByteBuffer column : columns) {
875             byte [][] famQf = KeyValue.parseColumn(getBytes(column));
876             if(famQf.length == 1) {
877               scan.addFamily(famQf[0]);
878             } else {
879               scan.addColumn(famQf[0], famQf[1]);
880             }
881           }
882         }
883         scan.setTimeRange(Long.MIN_VALUE, timestamp);
884         return addScanner(table.getScanner(scan));
885       } catch (IOException e) {
886         throw new IOError(e.getMessage());
887       }
888     }
889 
890     @Override
891     public Map<ByteBuffer, ColumnDescriptor> getColumnDescriptors(
892         ByteBuffer tableName) throws IOError, TException {
893       try {
894         TreeMap<ByteBuffer, ColumnDescriptor> columns =
895           new TreeMap<ByteBuffer, ColumnDescriptor>();
896 
897         HTable table = getTable(tableName);
898         HTableDescriptor desc = table.getTableDescriptor();
899 
900         for (HColumnDescriptor e : desc.getFamilies()) {
901           ColumnDescriptor col = ThriftUtilities.colDescFromHbase(e);
902           columns.put(col.name, col);
903         }
904         return columns;
905       } catch (IOException e) {
906         throw new IOError(e.getMessage());
907       }
908     }
909   }
910 
911   //
912   // Main program and support routines
913   //
914 
915   private static void printUsageAndExit(Options options, int exitCode) {
916     HelpFormatter formatter = new HelpFormatter();
917     formatter.printHelp("Thrift", null, options,
918             "To start the Thrift server run 'bin/hbase-daemon.sh start thrift'\n" +
919             "To shutdown the thrift server run 'bin/hbase-daemon.sh stop thrift' or" +
920             " send a kill signal to the thrift server pid",
921             true);
922       System.exit(exitCode);
923   }
924 
925   private static final String DEFAULT_LISTEN_PORT = "9090";
926 
927   /*
928    * Start up the Thrift server.
929    * @param args
930    */
931   static private void doMain(final String[] args) throws Exception {
932     Log LOG = LogFactory.getLog("ThriftServer");
933 
934     Options options = new Options();
935     options.addOption("b", "bind", true, "Address to bind the Thrift server to. Not supported by the Nonblocking and HsHa server [default: 0.0.0.0]");
936     options.addOption("p", "port", true, "Port to bind to [default: 9090]");
937     options.addOption("f", "framed", false, "Use framed transport");
938     options.addOption("c", "compact", false, "Use the compact protocol");
939     options.addOption("h", "help", false, "Print help information");
940 
941     OptionGroup servers = new OptionGroup();
942     servers.addOption(new Option("nonblocking", false, "Use the TNonblockingServer. This implies the framed transport."));
943     servers.addOption(new Option("hsha", false, "Use the THsHaServer. This implies the framed transport."));
944     servers.addOption(new Option("threadpool", false, "Use the TThreadPoolServer. This is the default."));
945     options.addOptionGroup(servers);
946 
947     CommandLineParser parser = new PosixParser();
948     CommandLine cmd = parser.parse(options, args);
949 
950     /**
951      * This is so complicated to please both bin/hbase and bin/hbase-daemon.
952      * hbase-daemon provides "start" and "stop" arguments
953      * hbase should print the help if no argument is provided
954      */
955     List<String> commandLine = Arrays.asList(args);
956     boolean stop = commandLine.contains("stop");
957     boolean start = commandLine.contains("start");
958     if (cmd.hasOption("help") || !start || stop) {
959       printUsageAndExit(options, 1);
960     }
961 
962     // Get port to bind to
963     int listenPort = 0;
964     try {
965       listenPort = Integer.parseInt(cmd.getOptionValue("port", DEFAULT_LISTEN_PORT));
966     } catch (NumberFormatException e) {
967       LOG.error("Could not parse the value provided for the port option", e);
968       printUsageAndExit(options, -1);
969     }
970 
971     // Construct correct ProtocolFactory
972     TProtocolFactory protocolFactory;
973     if (cmd.hasOption("compact")) {
974       LOG.debug("Using compact protocol");
975       protocolFactory = new TCompactProtocol.Factory();
976     } else {
977       LOG.debug("Using binary protocol");
978       protocolFactory = new TBinaryProtocol.Factory();
979     }
980 
981     HBaseHandler handler = new HBaseHandler();
982     Hbase.Processor processor = new Hbase.Processor(handler);
983 
984     TServer server;
985     if (cmd.hasOption("nonblocking") || cmd.hasOption("hsha")) {
986       if (cmd.hasOption("bind")) {
987         LOG.error("The Nonblocking and HsHa servers don't support IP address binding at the moment." +
988                 " See https://issues.apache.org/jira/browse/HBASE-2155 for details.");
989         printUsageAndExit(options, -1);
990       }
991 
992       TNonblockingServerTransport serverTransport = new TNonblockingServerSocket(listenPort);
993       TFramedTransport.Factory transportFactory = new TFramedTransport.Factory();
994 
995      if (cmd.hasOption("nonblocking")) {
996         TNonblockingServer.Args serverArgs = new TNonblockingServer.Args(serverTransport);
997         serverArgs.processor(processor);
998         serverArgs.transportFactory(transportFactory);
999         serverArgs.protocolFactory(protocolFactory);
1000 
1001         LOG.info("starting HBase Nonblocking Thrift server on " + Integer.toString(listenPort));
1002         server = new TNonblockingServer(serverArgs);
1003       } else {
1004         THsHaServer.Args serverArgs = new THsHaServer.Args(serverTransport);
1005         serverArgs.processor(processor);
1006         serverArgs.transportFactory(transportFactory);
1007         serverArgs.protocolFactory(protocolFactory);
1008 
1009         LOG.info("starting HBase HsHA Thrift server on " + Integer.toString(listenPort));
1010         server = new THsHaServer(serverArgs);
1011       }
1012     } else {
1013       // Get IP address to bind to
1014       InetAddress listenAddress = null;
1015       if (cmd.hasOption("bind")) {
1016         try {
1017           listenAddress = InetAddress.getByName(cmd.getOptionValue("bind"));
1018         } catch (UnknownHostException e) {
1019           LOG.error("Could not bind to provided ip address", e);
1020           printUsageAndExit(options, -1);
1021         }
1022       } else {
1023         listenAddress = InetAddress.getByName("0.0.0.0");
1024       }
1025       TServerTransport serverTransport = new TServerSocket(new InetSocketAddress(listenAddress, listenPort));
1026 
1027       // Construct correct TransportFactory
1028       TTransportFactory transportFactory;
1029       if (cmd.hasOption("framed")) {
1030         transportFactory = new TFramedTransport.Factory();
1031         LOG.debug("Using framed transport");
1032       } else {
1033         transportFactory = new TTransportFactory();
1034       }
1035 
1036       TThreadPoolServer.Args serverArgs = new TThreadPoolServer.Args(serverTransport);
1037       serverArgs.processor(processor);
1038       serverArgs.protocolFactory(protocolFactory);
1039       serverArgs.transportFactory(transportFactory);
1040       LOG.info("starting HBase ThreadPool Thrift server on " + listenAddress + ":" + Integer.toString(listenPort));
1041       server = new TThreadPoolServer(serverArgs);
1042     }
1043 
1044     server.serve();
1045   }
1046 
1047   /**
1048    * @param args
1049    * @throws Exception
1050    */
1051   public static void main(String [] args) throws Exception {
1052 	VersionInfo.logVersion();
1053     doMain(args);
1054   }
1055 }