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