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