View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.thrift;
20  
21  import static org.junit.Assert.assertArrayEquals;
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertFalse;
24  import static org.junit.Assert.assertTrue;
25  
26  import java.io.IOException;
27  import java.nio.ByteBuffer;
28  import java.util.ArrayList;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.hadoop.conf.Configuration;
35  import org.apache.hadoop.hbase.CompatibilityFactory;
36  import org.apache.hadoop.hbase.HBaseTestingUtility;
37  import org.apache.hadoop.hbase.HConstants;
38  import org.apache.hadoop.hbase.HRegionInfo;
39  import org.apache.hadoop.hbase.testclassification.LargeTests;
40  import org.apache.hadoop.hbase.TableName;
41  import org.apache.hadoop.hbase.filter.ParseFilter;
42  import org.apache.hadoop.hbase.security.UserProvider;
43  import org.apache.hadoop.hbase.test.MetricsAssertHelper;
44  import org.apache.hadoop.hbase.thrift.ThriftServerRunner.HBaseHandler;
45  import org.apache.hadoop.hbase.thrift.generated.BatchMutation;
46  import org.apache.hadoop.hbase.thrift.generated.ColumnDescriptor;
47  import org.apache.hadoop.hbase.thrift.generated.Hbase;
48  import org.apache.hadoop.hbase.thrift.generated.IOError;
49  import org.apache.hadoop.hbase.thrift.generated.Mutation;
50  import org.apache.hadoop.hbase.thrift.generated.TAppend;
51  import org.apache.hadoop.hbase.thrift.generated.TCell;
52  import org.apache.hadoop.hbase.thrift.generated.TIncrement;
53  import org.apache.hadoop.hbase.thrift.generated.TRegionInfo;
54  import org.apache.hadoop.hbase.thrift.generated.TRowResult;
55  import org.apache.hadoop.hbase.thrift.generated.TScan;
56  import org.apache.hadoop.hbase.util.Bytes;
57  import org.apache.hadoop.hbase.util.Threads;
58  import org.junit.AfterClass;
59  import org.junit.BeforeClass;
60  import org.junit.Test;
61  import org.junit.experimental.categories.Category;
62  
63  /**
64   * Unit testing for ThriftServerRunner.HBaseHandler, a part of the
65   * org.apache.hadoop.hbase.thrift package.
66   */
67  @Category(LargeTests.class)
68  public class TestThriftServer {
69    private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
70    private static final Log LOG = LogFactory.getLog(TestThriftServer.class);
71    private static final MetricsAssertHelper metricsHelper = CompatibilityFactory
72        .getInstance(MetricsAssertHelper.class);
73    protected static final int MAXVERSIONS = 3;
74  
75    private static ByteBuffer asByteBuffer(String i) {
76      return ByteBuffer.wrap(Bytes.toBytes(i));
77    }
78    private static ByteBuffer asByteBuffer(long l) {
79      return ByteBuffer.wrap(Bytes.toBytes(l));
80    }
81  
82    // Static names for tables, columns, rows, and values
83    private static ByteBuffer tableAname = asByteBuffer("tableA");
84    private static ByteBuffer tableBname = asByteBuffer("tableB");
85    private static ByteBuffer columnAname = asByteBuffer("columnA:");
86    private static ByteBuffer columnAAname = asByteBuffer("columnA:A");
87    private static ByteBuffer columnBname = asByteBuffer("columnB:");
88    private static ByteBuffer rowAname = asByteBuffer("rowA");
89    private static ByteBuffer rowBname = asByteBuffer("rowB");
90    private static ByteBuffer valueAname = asByteBuffer("valueA");
91    private static ByteBuffer valueBname = asByteBuffer("valueB");
92    private static ByteBuffer valueCname = asByteBuffer("valueC");
93    private static ByteBuffer valueDname = asByteBuffer("valueD");
94    private static ByteBuffer valueEname = asByteBuffer(100l);
95  
96    @BeforeClass
97    public static void beforeClass() throws Exception {
98      UTIL.getConfiguration().setBoolean(ThriftServerRunner.COALESCE_INC_KEY, true);
99      UTIL.getConfiguration().setBoolean("hbase.table.sanity.checks", false);
100     UTIL.startMiniCluster();
101   }
102 
103   @AfterClass
104   public static void afterClass() throws Exception {
105     UTIL.shutdownMiniCluster();
106   }
107 
108   /**
109    * Runs all of the tests under a single JUnit test method.  We
110    * consolidate all testing to one method because HBaseClusterTestCase
111    * is prone to OutOfMemoryExceptions when there are three or more
112    * JUnit test methods.
113    *
114    * @throws Exception
115    */
116   @Test
117   public void testAll() throws Exception {
118     // Run all tests
119     doTestTableCreateDrop();
120     doTestThriftMetrics();
121     doTestTableMutations();
122     doTestTableTimestampsAndColumns();
123     doTestTableScanners();
124     doTestGetTableRegions();
125     doTestFilterRegistration();
126     doTestGetRegionInfo();
127     doTestIncrements();
128     doTestAppend();
129     doTestCheckAndPut();
130   }
131 
132   /**
133    * Tests for creating, enabling, disabling, and deleting tables.  Also
134    * tests that creating a table with an invalid column name yields an
135    * IllegalArgument exception.
136    *
137    * @throws Exception
138    */
139   public void doTestTableCreateDrop() throws Exception {
140     ThriftServerRunner.HBaseHandler handler =
141       new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(),
142         UserProvider.instantiate(UTIL.getConfiguration()));
143     doTestTableCreateDrop(handler);
144   }
145 
146   public static void doTestTableCreateDrop(Hbase.Iface handler) throws Exception {
147     createTestTables(handler);
148     dropTestTables(handler);
149   }
150 
151   public static final class MySlowHBaseHandler extends ThriftServerRunner.HBaseHandler
152       implements Hbase.Iface {
153 
154     protected MySlowHBaseHandler(Configuration c)
155         throws IOException {
156       super(c, UserProvider.instantiate(c));
157     }
158 
159     @Override
160     public List<ByteBuffer> getTableNames() throws IOError {
161       Threads.sleepWithoutInterrupt(3000);
162       return super.getTableNames();
163     }
164   }
165 
166   /**
167    * TODO: These counts are supposed to be zero but sometimes they are not, they are equal to the
168    * passed in maybe.  Investigate why.  My guess is they are set by the test that runs just
169    * previous to this one.  Sometimes they are cleared.  Sometimes not.
170    * @param name
171    * @param maybe
172    * @param metrics
173    * @return
174    */
175   private int getCurrentCount(final String name, final int maybe, final ThriftMetrics metrics) {
176     int currentCount = 0;
177     try {
178       metricsHelper.assertCounter(name, maybe, metrics.getSource());
179       LOG.info("Shouldn't this be null? name=" + name + ", equals=" + maybe);
180       currentCount = maybe;
181     } catch (AssertionError e) {
182       // Ignore
183     }
184     return currentCount;
185   }
186 
187   /**
188    * Tests if the metrics for thrift handler work correctly
189    */
190   public void doTestThriftMetrics() throws Exception {
191     LOG.info("START doTestThriftMetrics");
192     Configuration conf = UTIL.getConfiguration();
193     ThriftMetrics metrics = getMetrics(conf);
194     Hbase.Iface handler = getHandlerForMetricsTest(metrics, conf);
195     int currentCountCreateTable = getCurrentCount("createTable_num_ops", 2, metrics);
196     int currentCountDeleteTable = getCurrentCount("deleteTable_num_ops", 2, metrics);
197     int currentCountDisableTable = getCurrentCount("disableTable_num_ops", 2, metrics);
198     createTestTables(handler);
199     dropTestTables(handler);;
200     metricsHelper.assertCounter("createTable_num_ops", currentCountCreateTable + 2,
201       metrics.getSource());
202     metricsHelper.assertCounter("deleteTable_num_ops", currentCountDeleteTable + 2,
203       metrics.getSource());
204     metricsHelper.assertCounter("disableTable_num_ops", currentCountDisableTable + 2,
205       metrics.getSource());
206     handler.getTableNames(); // This will have an artificial delay.
207 
208     // 3 to 6 seconds (to account for potential slowness), measured in nanoseconds
209    try {
210      metricsHelper.assertGaugeGt("getTableNames_avg_time", 3L * 1000 * 1000 * 1000, metrics.getSource());
211      metricsHelper.assertGaugeLt("getTableNames_avg_time",6L * 1000 * 1000 * 1000, metrics.getSource());
212    } catch (AssertionError e) {
213      LOG.info("Fix me!  Why does this happen?  A concurrent cluster running?", e);
214    }
215   }
216 
217   private static Hbase.Iface getHandlerForMetricsTest(ThriftMetrics metrics, Configuration conf)
218       throws Exception {
219     Hbase.Iface handler = new MySlowHBaseHandler(conf);
220     return HbaseHandlerMetricsProxy.newInstance(handler, metrics, conf);
221   }
222 
223   private static ThriftMetrics getMetrics(Configuration conf) throws Exception {
224     return new ThriftMetrics( conf, ThriftMetrics.ThriftServerType.ONE);
225   }
226 
227 
228   public static void createTestTables(Hbase.Iface handler) throws Exception {
229     // Create/enable/disable/delete tables, ensure methods act correctly
230     assertEquals(handler.getTableNames().size(), 0);
231     handler.createTable(tableAname, getColumnDescriptors());
232     assertEquals(handler.getTableNames().size(), 1);
233     assertEquals(handler.getColumnDescriptors(tableAname).size(), 2);
234     assertTrue(handler.isTableEnabled(tableAname));
235     handler.createTable(tableBname, new ArrayList<ColumnDescriptor>());
236     assertEquals(handler.getTableNames().size(), 2);
237   }
238 
239   public static void checkTableList(Hbase.Iface handler) throws Exception {
240     assertTrue(handler.getTableNames().contains(tableAname));
241   }
242 
243   public static void dropTestTables(Hbase.Iface handler) throws Exception {
244     handler.disableTable(tableBname);
245     assertFalse(handler.isTableEnabled(tableBname));
246     handler.deleteTable(tableBname);
247     assertEquals(handler.getTableNames().size(), 1);
248     handler.disableTable(tableAname);
249     assertFalse(handler.isTableEnabled(tableAname));
250     /* TODO Reenable.
251     assertFalse(handler.isTableEnabled(tableAname));
252     handler.enableTable(tableAname);
253     assertTrue(handler.isTableEnabled(tableAname));
254     handler.disableTable(tableAname);*/
255     handler.deleteTable(tableAname);
256     assertEquals(handler.getTableNames().size(), 0);
257   }
258 
259   public void doTestIncrements() throws Exception {
260     ThriftServerRunner.HBaseHandler handler =
261       new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(),
262         UserProvider.instantiate(UTIL.getConfiguration()));
263     createTestTables(handler);
264     doTestIncrements(handler);
265     dropTestTables(handler);
266   }
267 
268   public static void doTestIncrements(HBaseHandler handler) throws Exception {
269     List<Mutation> mutations = new ArrayList<Mutation>(1);
270     mutations.add(new Mutation(false, columnAAname, valueEname, true));
271     mutations.add(new Mutation(false, columnAname, valueEname, true));
272     handler.mutateRow(tableAname, rowAname, mutations, null);
273     handler.mutateRow(tableAname, rowBname, mutations, null);
274 
275     List<TIncrement> increments = new ArrayList<TIncrement>();
276     increments.add(new TIncrement(tableAname, rowBname, columnAAname, 7));
277     increments.add(new TIncrement(tableAname, rowBname, columnAAname, 7));
278     increments.add(new TIncrement(tableAname, rowBname, columnAAname, 7));
279 
280     int numIncrements = 60000;
281     for (int i = 0; i < numIncrements; i++) {
282       handler.increment(new TIncrement(tableAname, rowAname, columnAname, 2));
283       handler.incrementRows(increments);
284     }
285 
286     Thread.sleep(1000);
287     long lv = handler.get(tableAname, rowAname, columnAname, null).get(0).value.getLong();
288     // Wait on all increments being flushed
289     while (handler.coalescer.getQueueSize() != 0) Threads.sleep(10);
290     assertEquals((100 + (2 * numIncrements)), lv );
291 
292 
293     lv = handler.get(tableAname, rowBname, columnAAname, null).get(0).value.getLong();
294     assertEquals((100 + (3 * 7 * numIncrements)), lv);
295 
296     assertTrue(handler.coalescer.getSuccessfulCoalescings() > 0);
297 
298   }
299 
300   /**
301    * Tests adding a series of Mutations and BatchMutations, including a
302    * delete mutation.  Also tests data retrieval, and getting back multiple
303    * versions.
304    *
305    * @throws Exception
306    */
307   public void doTestTableMutations() throws Exception {
308     ThriftServerRunner.HBaseHandler handler =
309       new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(),
310         UserProvider.instantiate(UTIL.getConfiguration()));
311     doTestTableMutations(handler);
312   }
313 
314   public static void doTestTableMutations(Hbase.Iface handler) throws Exception {
315     // Setup
316     handler.createTable(tableAname, getColumnDescriptors());
317 
318     // Apply a few Mutations to rowA
319     //     mutations.add(new Mutation(false, columnAname, valueAname));
320     //     mutations.add(new Mutation(false, columnBname, valueBname));
321     handler.mutateRow(tableAname, rowAname, getMutations(), null);
322 
323     // Assert that the changes were made
324     assertEquals(valueAname,
325       handler.get(tableAname, rowAname, columnAname, null).get(0).value);
326     TRowResult rowResult1 = handler.getRow(tableAname, rowAname, null).get(0);
327     assertEquals(rowAname, rowResult1.row);
328     assertEquals(valueBname,
329       rowResult1.columns.get(columnBname).value);
330 
331     // Apply a few BatchMutations for rowA and rowB
332     // rowAmutations.add(new Mutation(true, columnAname, null));
333     // rowAmutations.add(new Mutation(false, columnBname, valueCname));
334     // batchMutations.add(new BatchMutation(rowAname, rowAmutations));
335     // Mutations to rowB
336     // rowBmutations.add(new Mutation(false, columnAname, valueCname));
337     // rowBmutations.add(new Mutation(false, columnBname, valueDname));
338     // batchMutations.add(new BatchMutation(rowBname, rowBmutations));
339     handler.mutateRows(tableAname, getBatchMutations(), null);
340 
341     // Assert that changes were made to rowA
342     List<TCell> cells = handler.get(tableAname, rowAname, columnAname, null);
343     assertFalse(cells.size() > 0);
344     assertEquals(valueCname, handler.get(tableAname, rowAname, columnBname, null).get(0).value);
345     List<TCell> versions = handler.getVer(tableAname, rowAname, columnBname, MAXVERSIONS, null);
346     assertEquals(valueCname, versions.get(0).value);
347     assertEquals(valueBname, versions.get(1).value);
348 
349     // Assert that changes were made to rowB
350     TRowResult rowResult2 = handler.getRow(tableAname, rowBname, null).get(0);
351     assertEquals(rowBname, rowResult2.row);
352     assertEquals(valueCname, rowResult2.columns.get(columnAname).value);
353     assertEquals(valueDname, rowResult2.columns.get(columnBname).value);
354 
355     // Apply some deletes
356     handler.deleteAll(tableAname, rowAname, columnBname, null);
357     handler.deleteAllRow(tableAname, rowBname, null);
358 
359     // Assert that the deletes were applied
360     int size = handler.get(tableAname, rowAname, columnBname, null).size();
361     assertEquals(0, size);
362     size = handler.getRow(tableAname, rowBname, null).size();
363     assertEquals(0, size);
364 
365     // Try null mutation
366     List<Mutation> mutations = new ArrayList<Mutation>();
367     mutations.add(new Mutation(false, columnAname, null, true));
368     handler.mutateRow(tableAname, rowAname, mutations, null);
369     TRowResult rowResult3 = handler.getRow(tableAname, rowAname, null).get(0);
370     assertEquals(rowAname, rowResult3.row);
371     assertEquals(0, rowResult3.columns.get(columnAname).value.remaining());
372 
373     // Teardown
374     handler.disableTable(tableAname);
375     handler.deleteTable(tableAname);
376   }
377 
378   /**
379    * Similar to testTableMutations(), except Mutations are applied with
380    * specific timestamps and data retrieval uses these timestamps to
381    * extract specific versions of data.
382    *
383    * @throws Exception
384    */
385   public void doTestTableTimestampsAndColumns() throws Exception {
386     // Setup
387     ThriftServerRunner.HBaseHandler handler =
388       new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(),
389         UserProvider.instantiate(UTIL.getConfiguration()));
390     handler.createTable(tableAname, getColumnDescriptors());
391 
392     // Apply timestamped Mutations to rowA
393     long time1 = System.currentTimeMillis();
394     handler.mutateRowTs(tableAname, rowAname, getMutations(), time1, null);
395 
396     Thread.sleep(1000);
397 
398     // Apply timestamped BatchMutations for rowA and rowB
399     long time2 = System.currentTimeMillis();
400     handler.mutateRowsTs(tableAname, getBatchMutations(), time2, null);
401 
402     // Apply an overlapping timestamped mutation to rowB
403     handler.mutateRowTs(tableAname, rowBname, getMutations(), time2, null);
404 
405     // the getVerTs is [inf, ts) so you need to increment one.
406     time1 += 1;
407     time2 += 2;
408 
409     // Assert that the timestamp-related methods retrieve the correct data
410     assertEquals(2, handler.getVerTs(tableAname, rowAname, columnBname, time2,
411       MAXVERSIONS, null).size());
412     assertEquals(1, handler.getVerTs(tableAname, rowAname, columnBname, time1,
413       MAXVERSIONS, null).size());
414 
415     TRowResult rowResult1 = handler.getRowTs(tableAname, rowAname, time1, null).get(0);
416     TRowResult rowResult2 = handler.getRowTs(tableAname, rowAname, time2, null).get(0);
417     // columnA was completely deleted
418     //assertTrue(Bytes.equals(rowResult1.columns.get(columnAname).value, valueAname));
419     assertEquals(rowResult1.columns.get(columnBname).value, valueBname);
420     assertEquals(rowResult2.columns.get(columnBname).value, valueCname);
421 
422     // ColumnAname has been deleted, and will never be visible even with a getRowTs()
423     assertFalse(rowResult2.columns.containsKey(columnAname));
424 
425     List<ByteBuffer> columns = new ArrayList<ByteBuffer>();
426     columns.add(columnBname);
427 
428     rowResult1 = handler.getRowWithColumns(tableAname, rowAname, columns, null).get(0);
429     assertEquals(rowResult1.columns.get(columnBname).value, valueCname);
430     assertFalse(rowResult1.columns.containsKey(columnAname));
431 
432     rowResult1 = handler.getRowWithColumnsTs(tableAname, rowAname, columns, time1, null).get(0);
433     assertEquals(rowResult1.columns.get(columnBname).value, valueBname);
434     assertFalse(rowResult1.columns.containsKey(columnAname));
435 
436     // Apply some timestamped deletes
437     // this actually deletes _everything_.
438     // nukes everything in columnB: forever.
439     handler.deleteAllTs(tableAname, rowAname, columnBname, time1, null);
440     handler.deleteAllRowTs(tableAname, rowBname, time2, null);
441 
442     // Assert that the timestamp-related methods retrieve the correct data
443     int size = handler.getVerTs(tableAname, rowAname, columnBname, time1, MAXVERSIONS, null).size();
444     assertEquals(0, size);
445 
446     size = handler.getVerTs(tableAname, rowAname, columnBname, time2, MAXVERSIONS, null).size();
447     assertEquals(1, size);
448 
449     // should be available....
450     assertEquals(handler.get(tableAname, rowAname, columnBname, null).get(0).value, valueCname);
451 
452     assertEquals(0, handler.getRow(tableAname, rowBname, null).size());
453 
454     // Teardown
455     handler.disableTable(tableAname);
456     handler.deleteTable(tableAname);
457   }
458 
459   /**
460    * Tests the four different scanner-opening methods (with and without
461    * a stoprow, with and without a timestamp).
462    *
463    * @throws Exception
464    */
465   public void doTestTableScanners() throws Exception {
466     // Setup
467     ThriftServerRunner.HBaseHandler handler =
468       new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(),
469         UserProvider.instantiate(UTIL.getConfiguration()));
470     handler.createTable(tableAname, getColumnDescriptors());
471 
472     // Apply timestamped Mutations to rowA
473     long time1 = System.currentTimeMillis();
474     handler.mutateRowTs(tableAname, rowAname, getMutations(), time1, null);
475 
476     // Sleep to assure that 'time1' and 'time2' will be different even with a
477     // coarse grained system timer.
478     Thread.sleep(1000);
479 
480     // Apply timestamped BatchMutations for rowA and rowB
481     long time2 = System.currentTimeMillis();
482     handler.mutateRowsTs(tableAname, getBatchMutations(), time2, null);
483 
484     time1 += 1;
485 
486     // Test a scanner on all rows and all columns, no timestamp
487     int scanner1 = handler.scannerOpen(tableAname, rowAname, getColumnList(true, true), null);
488     TRowResult rowResult1a = handler.scannerGet(scanner1).get(0);
489     assertEquals(rowResult1a.row, rowAname);
490     // This used to be '1'.  I don't know why when we are asking for two columns
491     // and when the mutations above would seem to add two columns to the row.
492     // -- St.Ack 05/12/2009
493     assertEquals(rowResult1a.columns.size(), 1);
494     assertEquals(rowResult1a.columns.get(columnBname).value, valueCname);
495 
496     TRowResult rowResult1b = handler.scannerGet(scanner1).get(0);
497     assertEquals(rowResult1b.row, rowBname);
498     assertEquals(rowResult1b.columns.size(), 2);
499     assertEquals(rowResult1b.columns.get(columnAname).value, valueCname);
500     assertEquals(rowResult1b.columns.get(columnBname).value, valueDname);
501     closeScanner(scanner1, handler);
502 
503     // Test a scanner on all rows and all columns, with timestamp
504     int scanner2 = handler.scannerOpenTs(tableAname, rowAname, getColumnList(true, true), time1, null);
505     TRowResult rowResult2a = handler.scannerGet(scanner2).get(0);
506     assertEquals(rowResult2a.columns.size(), 1);
507     // column A deleted, does not exist.
508     //assertTrue(Bytes.equals(rowResult2a.columns.get(columnAname).value, valueAname));
509     assertEquals(rowResult2a.columns.get(columnBname).value, valueBname);
510     closeScanner(scanner2, handler);
511 
512     // Test a scanner on the first row and first column only, no timestamp
513     int scanner3 = handler.scannerOpenWithStop(tableAname, rowAname, rowBname,
514         getColumnList(true, false), null);
515     closeScanner(scanner3, handler);
516 
517     // Test a scanner on the first row and second column only, with timestamp
518     int scanner4 = handler.scannerOpenWithStopTs(tableAname, rowAname, rowBname,
519         getColumnList(false, true), time1, null);
520     TRowResult rowResult4a = handler.scannerGet(scanner4).get(0);
521     assertEquals(rowResult4a.columns.size(), 1);
522     assertEquals(rowResult4a.columns.get(columnBname).value, valueBname);
523 
524     // Test scanner using a TScan object once with sortColumns False and once with sortColumns true
525     TScan scanNoSortColumns = new TScan();
526     scanNoSortColumns.setStartRow(rowAname);
527     scanNoSortColumns.setStopRow(rowBname);
528 
529     int scanner5 = handler.scannerOpenWithScan(tableAname , scanNoSortColumns, null);
530     TRowResult rowResult5 = handler.scannerGet(scanner5).get(0);
531     assertEquals(rowResult5.columns.size(), 1);
532     assertEquals(rowResult5.columns.get(columnBname).value, valueCname);
533 
534     TScan scanSortColumns = new TScan();
535     scanSortColumns.setStartRow(rowAname);
536     scanSortColumns.setStopRow(rowBname);
537     scanSortColumns = scanSortColumns.setSortColumns(true);
538 
539     int scanner6 = handler.scannerOpenWithScan(tableAname ,scanSortColumns, null);
540     TRowResult rowResult6 = handler.scannerGet(scanner6).get(0);
541     assertEquals(rowResult6.sortedColumns.size(), 1);
542     assertEquals(rowResult6.sortedColumns.get(0).getCell().value, valueCname);
543 
544     List<Mutation> rowBmutations = new ArrayList<Mutation>();
545     for (int i = 0; i < 20; i++) {
546       rowBmutations.add(new Mutation(false, asByteBuffer("columnA:" + i), valueCname, true));
547     }
548     ByteBuffer rowC = asByteBuffer("rowC");
549     handler.mutateRow(tableAname, rowC, rowBmutations, null);
550 
551     TScan scanSortMultiColumns = new TScan();
552     scanSortMultiColumns.setStartRow(rowC);
553     scanSortMultiColumns = scanSortMultiColumns.setSortColumns(true);
554     int scanner7 = handler.scannerOpenWithScan(tableAname, scanSortMultiColumns, null);
555     TRowResult rowResult7 = handler.scannerGet(scanner7).get(0);
556 
557     ByteBuffer smallerColumn = asByteBuffer("columnA:");
558     for (int i = 0; i < 20; i++) {
559       ByteBuffer currentColumn = rowResult7.sortedColumns.get(i).columnName;
560       assertTrue(Bytes.compareTo(smallerColumn.array(), currentColumn.array()) < 0);
561       smallerColumn = currentColumn;
562     }
563 
564     TScan reversedScan = new TScan();
565     reversedScan.setReversed(true);
566     reversedScan.setStartRow(rowBname);
567     reversedScan.setStopRow(rowAname);
568 
569     int scanner8 = handler.scannerOpenWithScan(tableAname , reversedScan, null);
570     List<TRowResult> results = handler.scannerGet(scanner8);
571     handler.scannerClose(scanner8);
572     assertEquals(results.size(), 1);
573     assertEquals(ByteBuffer.wrap(results.get(0).getRow()), rowBname);
574 
575     // Teardown
576     handler.disableTable(tableAname);
577     handler.deleteTable(tableAname);
578   }
579 
580   /**
581    * For HBASE-2556
582    * Tests for GetTableRegions
583    *
584    * @throws Exception
585    */
586   public void doTestGetTableRegions() throws Exception {
587     ThriftServerRunner.HBaseHandler handler =
588       new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(),
589         UserProvider.instantiate(UTIL.getConfiguration()));
590     doTestGetTableRegions(handler);
591   }
592 
593   public static void doTestGetTableRegions(Hbase.Iface handler)
594       throws Exception {
595     assertEquals(handler.getTableNames().size(), 0);
596     handler.createTable(tableAname, getColumnDescriptors());
597     assertEquals(handler.getTableNames().size(), 1);
598     List<TRegionInfo> regions = handler.getTableRegions(tableAname);
599     int regionCount = regions.size();
600     assertEquals("empty table should have only 1 region, " +
601             "but found " + regionCount, regionCount, 1);
602     LOG.info("Region found:" + regions.get(0));
603     handler.disableTable(tableAname);
604     handler.deleteTable(tableAname);
605     regionCount = handler.getTableRegions(tableAname).size();
606     assertEquals("non-existing table should have 0 region, " +
607             "but found " + regionCount, regionCount, 0);
608   }
609 
610   public void doTestFilterRegistration() throws Exception {
611     Configuration conf = UTIL.getConfiguration();
612 
613     conf.set("hbase.thrift.filters", "MyFilter:filterclass");
614 
615     ThriftServerRunner.registerFilters(conf);
616 
617     Map<String, String> registeredFilters = ParseFilter.getAllFilters();
618 
619     assertEquals("filterclass", registeredFilters.get("MyFilter"));
620   }
621 
622   public void doTestGetRegionInfo() throws Exception {
623     ThriftServerRunner.HBaseHandler handler =
624       new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(),
625         UserProvider.instantiate(UTIL.getConfiguration()));
626     doTestGetRegionInfo(handler);
627   }
628 
629   public static void doTestGetRegionInfo(Hbase.Iface handler) throws Exception {
630     // Create tableA and add two columns to rowA
631     handler.createTable(tableAname, getColumnDescriptors());
632     try {
633       handler.mutateRow(tableAname, rowAname, getMutations(), null);
634       byte[] searchRow = HRegionInfo.createRegionName(
635           TableName.valueOf(tableAname.array()), rowAname.array(),
636           HConstants.NINES, false);
637       TRegionInfo regionInfo = handler.getRegionInfo(ByteBuffer.wrap(searchRow));
638       assertTrue(Bytes.toStringBinary(regionInfo.getName()).startsWith(
639             Bytes.toStringBinary(tableAname)));
640     } finally {
641       handler.disableTable(tableAname);
642       handler.deleteTable(tableAname);
643     }
644   }
645 
646   /**
647    * Appends the value to a cell and checks that the cell value is updated properly.
648    *
649    * @throws Exception
650    */
651   public static void doTestAppend() throws Exception {
652     ThriftServerRunner.HBaseHandler handler =
653       new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(),
654         UserProvider.instantiate(UTIL.getConfiguration()));
655     handler.createTable(tableAname, getColumnDescriptors());
656     try {
657       List<Mutation> mutations = new ArrayList<Mutation>(1);
658       mutations.add(new Mutation(false, columnAname, valueAname, true));
659       handler.mutateRow(tableAname, rowAname, mutations, null);
660 
661       List<ByteBuffer> columnList = new ArrayList<ByteBuffer>();
662       columnList.add(columnAname);
663       List<ByteBuffer> valueList = new ArrayList<ByteBuffer>();
664       valueList.add(valueBname);
665 
666       TAppend append = new TAppend(tableAname, rowAname, columnList, valueList);
667       handler.append(append);
668 
669       TRowResult rowResult = handler.getRow(tableAname, rowAname, null).get(0);
670       assertEquals(rowAname, rowResult.row);
671       assertArrayEquals(Bytes.add(valueAname.array(), valueBname.array()),
672         rowResult.columns.get(columnAname).value.array());
673     } finally {
674       handler.disableTable(tableAname);
675       handler.deleteTable(tableAname);
676     }
677   }
678 
679   /**
680    * Check that checkAndPut fails if the cell does not exist, then put in the cell, then check that
681    * the checkAndPut succeeds.
682    *
683    * @throws Exception
684    */
685   public static void doTestCheckAndPut() throws Exception {
686     ThriftServerRunner.HBaseHandler handler =
687       new ThriftServerRunner.HBaseHandler(UTIL.getConfiguration(),
688         UserProvider.instantiate(UTIL.getConfiguration()));
689     handler.createTable(tableAname, getColumnDescriptors());
690     try {
691       List<Mutation> mutations = new ArrayList<Mutation>(1);
692       mutations.add(new Mutation(false, columnAname, valueAname, true));
693       Mutation putB = (new Mutation(false, columnBname, valueBname, true));
694 
695       assertFalse(handler.checkAndPut(tableAname, rowAname, columnAname, valueAname, putB, null));
696 
697       handler.mutateRow(tableAname, rowAname, mutations, null);
698 
699       assertTrue(handler.checkAndPut(tableAname, rowAname, columnAname, valueAname, putB, null));
700 
701       TRowResult rowResult = handler.getRow(tableAname, rowAname, null).get(0);
702       assertEquals(rowAname, rowResult.row);
703       assertEquals(valueBname, rowResult.columns.get(columnBname).value);
704     } finally {
705       handler.disableTable(tableAname);
706       handler.deleteTable(tableAname);
707     }
708   }
709 
710   /**
711    *
712    * @return a List of ColumnDescriptors for use in creating a table.  Has one
713    * default ColumnDescriptor and one ColumnDescriptor with fewer versions
714    */
715   private static List<ColumnDescriptor> getColumnDescriptors() {
716     ArrayList<ColumnDescriptor> cDescriptors = new ArrayList<ColumnDescriptor>();
717 
718     // A default ColumnDescriptor
719     ColumnDescriptor cDescA = new ColumnDescriptor();
720     cDescA.name = columnAname;
721     cDescriptors.add(cDescA);
722 
723     // A slightly customized ColumnDescriptor (only 2 versions)
724     ColumnDescriptor cDescB = new ColumnDescriptor(columnBname, 2, "NONE",
725         false, "NONE", 0, 0, false, -1);
726     cDescriptors.add(cDescB);
727 
728     return cDescriptors;
729   }
730 
731   /**
732    *
733    * @param includeA whether or not to include columnA
734    * @param includeB whether or not to include columnB
735    * @return a List of column names for use in retrieving a scanner
736    */
737   private List<ByteBuffer> getColumnList(boolean includeA, boolean includeB) {
738     List<ByteBuffer> columnList = new ArrayList<ByteBuffer>();
739     if (includeA) columnList.add(columnAname);
740     if (includeB) columnList.add(columnBname);
741     return columnList;
742   }
743 
744   /**
745    *
746    * @return a List of Mutations for a row, with columnA having valueA
747    * and columnB having valueB
748    */
749   private static List<Mutation> getMutations() {
750     List<Mutation> mutations = new ArrayList<Mutation>();
751     mutations.add(new Mutation(false, columnAname, valueAname, true));
752     mutations.add(new Mutation(false, columnBname, valueBname, true));
753     return mutations;
754   }
755 
756   /**
757    *
758    * @return a List of BatchMutations with the following effects:
759    * (rowA, columnA): delete
760    * (rowA, columnB): place valueC
761    * (rowB, columnA): place valueC
762    * (rowB, columnB): place valueD
763    */
764   private static List<BatchMutation> getBatchMutations() {
765     List<BatchMutation> batchMutations = new ArrayList<BatchMutation>();
766 
767     // Mutations to rowA.  You can't mix delete and put anymore.
768     List<Mutation> rowAmutations = new ArrayList<Mutation>();
769     rowAmutations.add(new Mutation(true, columnAname, null, true));
770     batchMutations.add(new BatchMutation(rowAname, rowAmutations));
771 
772     rowAmutations = new ArrayList<Mutation>();
773     rowAmutations.add(new Mutation(false, columnBname, valueCname, true));
774     batchMutations.add(new BatchMutation(rowAname, rowAmutations));
775 
776     // Mutations to rowB
777     List<Mutation> rowBmutations = new ArrayList<Mutation>();
778     rowBmutations.add(new Mutation(false, columnAname, valueCname, true));
779     rowBmutations.add(new Mutation(false, columnBname, valueDname, true));
780     batchMutations.add(new BatchMutation(rowBname, rowBmutations));
781 
782     return batchMutations;
783   }
784 
785   /**
786    * Asserts that the passed scanner is exhausted, and then closes
787    * the scanner.
788    *
789    * @param scannerId the scanner to close
790    * @param handler the HBaseHandler interfacing to HBase
791    * @throws Exception
792    */
793   private void closeScanner(
794       int scannerId, ThriftServerRunner.HBaseHandler handler) throws Exception {
795     handler.scannerGet(scannerId);
796     handler.scannerClose(scannerId);
797   }
798 }