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