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