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