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.thrift2;
20  
21  import com.google.common.collect.Lists;
22  import org.apache.commons.collections.CollectionUtils;
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.hadoop.conf.Configuration;
26  import org.apache.hadoop.hbase.Cell;
27  import org.apache.hadoop.hbase.CompatibilityFactory;
28  import org.apache.hadoop.hbase.CoprocessorEnvironment;
29  import org.apache.hadoop.hbase.HBaseTestingUtility;
30  import org.apache.hadoop.hbase.HColumnDescriptor;
31  import org.apache.hadoop.hbase.HTableDescriptor;
32  import org.apache.hadoop.hbase.TableName;
33  import org.apache.hadoop.hbase.client.Admin;
34  import org.apache.hadoop.hbase.client.Get;
35  import org.apache.hadoop.hbase.client.HBaseAdmin;
36  import org.apache.hadoop.hbase.client.Put;
37  import org.apache.hadoop.hbase.client.Scan;
38  import org.apache.hadoop.hbase.client.Increment;
39  import org.apache.hadoop.hbase.client.Delete;
40  import org.apache.hadoop.hbase.client.Durability;
41  import org.apache.hadoop.hbase.client.Table;
42  import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
43  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
44  import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
45  import org.apache.hadoop.hbase.filter.ParseFilter;
46  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
47  import org.apache.hadoop.hbase.security.UserProvider;
48  import org.apache.hadoop.hbase.test.MetricsAssertHelper;
49  import org.apache.hadoop.hbase.testclassification.MediumTests;
50  import org.apache.hadoop.hbase.thrift.ErrorThrowingGetObserver;
51  import org.apache.hadoop.hbase.thrift.ThriftMetrics;
52  import org.apache.hadoop.hbase.thrift2.generated.TAppend;
53  import org.apache.hadoop.hbase.thrift2.generated.TColumn;
54  import org.apache.hadoop.hbase.thrift2.generated.TColumnIncrement;
55  import org.apache.hadoop.hbase.thrift2.generated.TColumnValue;
56  import org.apache.hadoop.hbase.thrift2.generated.TCompareOp;
57  import org.apache.hadoop.hbase.thrift2.generated.TDelete;
58  import org.apache.hadoop.hbase.thrift2.generated.TDeleteType;
59  import org.apache.hadoop.hbase.thrift2.generated.TGet;
60  import org.apache.hadoop.hbase.thrift2.generated.THBaseService;
61  import org.apache.hadoop.hbase.thrift2.generated.TIOError;
62  import org.apache.hadoop.hbase.thrift2.generated.TIllegalArgument;
63  import org.apache.hadoop.hbase.thrift2.generated.TIncrement;
64  import org.apache.hadoop.hbase.thrift2.generated.TPut;
65  import org.apache.hadoop.hbase.thrift2.generated.TResult;
66  import org.apache.hadoop.hbase.thrift2.generated.TScan;
67  import org.apache.hadoop.hbase.thrift2.generated.TMutation;
68  import org.apache.hadoop.hbase.thrift2.generated.TRowMutations;
69  import org.apache.hadoop.hbase.thrift2.generated.TDurability;
70  import org.apache.hadoop.hbase.thrift2.generated.TTimeRange;
71  import org.apache.hadoop.hbase.util.Bytes;
72  import org.apache.thrift.TException;
73  import org.junit.AfterClass;
74  import org.junit.Before;
75  import org.junit.BeforeClass;
76  import org.junit.Test;
77  import org.junit.experimental.categories.Category;
78  
79  import java.io.IOException;
80  import java.io.InterruptedIOException;
81  import java.nio.ByteBuffer;
82  import java.util.ArrayList;
83  import java.util.Arrays;
84  import java.util.Collections;
85  import java.util.Comparator;
86  import java.util.List;
87  import java.util.Map;
88  import java.util.HashMap;
89  import java.util.concurrent.TimeUnit;
90  
91  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.getFromThrift;
92  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.putFromThrift;
93  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.scanFromThrift;
94  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.incrementFromThrift;
95  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.deleteFromThrift;
96  import static org.junit.Assert.*;
97  import static java.nio.ByteBuffer.wrap;
98  
99  /**
100  * Unit testing for ThriftServer.HBaseHandler, a part of the org.apache.hadoop.hbase.thrift2
101  * package.
102  */
103 @Category(MediumTests.class)
104 public class TestThriftHBaseServiceHandler {
105 
106   private static final Log LOG = LogFactory.getLog(TestThriftHBaseServiceHandler.class);
107   private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
108 
109   // Static names for tables, columns, rows, and values
110   private static byte[] tableAname = Bytes.toBytes("tableA");
111   private static byte[] familyAname = Bytes.toBytes("familyA");
112   private static byte[] familyBname = Bytes.toBytes("familyB");
113   private static byte[] qualifierAname = Bytes.toBytes("qualifierA");
114   private static byte[] qualifierBname = Bytes.toBytes("qualifierB");
115   private static byte[] valueAname = Bytes.toBytes("valueA");
116   private static byte[] valueBname = Bytes.toBytes("valueB");
117   private static HColumnDescriptor[] families = new HColumnDescriptor[] {
118       new HColumnDescriptor(familyAname).setMaxVersions(3),
119       new HColumnDescriptor(familyBname).setMaxVersions(2)
120   };
121 
122 
123   private static final MetricsAssertHelper metricsHelper =
124       CompatibilityFactory.getInstance(MetricsAssertHelper.class);
125 
126 
127   public void assertTColumnValuesEqual(List<TColumnValue> columnValuesA,
128       List<TColumnValue> columnValuesB) {
129     assertEquals(columnValuesA.size(), columnValuesB.size());
130     Comparator<TColumnValue> comparator = new Comparator<TColumnValue>() {
131       @Override
132       public int compare(TColumnValue o1, TColumnValue o2) {
133         return Bytes.compareTo(Bytes.add(o1.getFamily(), o1.getQualifier()),
134             Bytes.add(o2.getFamily(), o2.getQualifier()));
135       }
136     };
137     Collections.sort(columnValuesA, comparator);
138     Collections.sort(columnValuesB, comparator);
139 
140     for (int i = 0; i < columnValuesA.size(); i++) {
141       TColumnValue a = columnValuesA.get(i);
142       TColumnValue b = columnValuesB.get(i);
143       assertTColumnValueEqual(a, b);
144     }
145   }
146 
147   public void assertTColumnValueEqual(TColumnValue a, TColumnValue b) {
148     assertArrayEquals(a.getFamily(), b.getFamily());
149     assertArrayEquals(a.getQualifier(), b.getQualifier());
150     assertArrayEquals(a.getValue(), b.getValue());
151   }
152 
153   @BeforeClass
154   public static void beforeClass() throws Exception {
155     UTIL.getConfiguration().set("hbase.client.retries.number", "3");
156     UTIL.startMiniCluster();
157     Admin admin = new HBaseAdmin(UTIL.getConfiguration());
158     HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(tableAname));
159     for (HColumnDescriptor family : families) {
160       tableDescriptor.addFamily(family);
161     }
162     admin.createTable(tableDescriptor);
163     admin.close();
164   }
165 
166   @AfterClass
167   public static void afterClass() throws Exception {
168     UTIL.shutdownMiniCluster();
169   }
170 
171   @Before
172   public void setup() throws Exception {
173 
174   }
175 
176   private ThriftHBaseServiceHandler createHandler() throws TException {
177     try {
178       Configuration conf = UTIL.getConfiguration();
179       return new ThriftHBaseServiceHandler(conf, UserProvider.instantiate(conf));
180     } catch (IOException ie) {
181       throw new TException(ie);
182     }
183   }
184 
185   @Test
186   public void testExists() throws TIOError, TException {
187     ThriftHBaseServiceHandler handler = createHandler();
188     byte[] rowName = "testExists".getBytes();
189     ByteBuffer table = wrap(tableAname);
190 
191     TGet get = new TGet(wrap(rowName));
192     assertFalse(handler.exists(table, get));
193 
194     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
195     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
196     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
197     TPut put = new TPut(wrap(rowName), columnValues);
198     put.setColumnValues(columnValues);
199 
200     handler.put(table, put);
201 
202     assertTrue(handler.exists(table, get));
203   }
204 
205   @Test
206   public void testExistsAll() throws TIOError, TException {
207     ThriftHBaseServiceHandler handler = createHandler();
208     byte[] rowName1 = "testExistsAll1".getBytes();
209     byte[] rowName2 = "testExistsAll2".getBytes();
210     ByteBuffer table = wrap(tableAname);
211 
212     List<TGet> gets = new ArrayList<>();
213     gets.add(new TGet(wrap(rowName2)));
214     gets.add(new TGet(wrap(rowName2)));
215     List<Boolean> existsResult1 = handler.existsAll(table, gets);
216     assertFalse(existsResult1.get(0));
217     assertFalse(existsResult1.get(1));
218 
219     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
220     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
221     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
222     List<TPut> puts = new ArrayList<TPut>();
223     puts.add(new TPut(wrap(rowName1), columnValues));
224     puts.add(new TPut(wrap(rowName2), columnValues));
225 
226     handler.putMultiple(table, puts);
227     List<Boolean> existsResult2 = handler.existsAll(table,gets );
228 
229     assertTrue(existsResult2.get(0));
230     assertTrue(existsResult2.get(1));
231   }
232 
233   @Test
234   public void testPutGet() throws Exception {
235     ThriftHBaseServiceHandler handler = createHandler();
236     byte[] rowName = "testPutGet".getBytes();
237     ByteBuffer table = wrap(tableAname);
238 
239     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
240     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
241     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
242     TPut put = new TPut(wrap(rowName), columnValues);
243 
244     put.setColumnValues(columnValues);
245 
246     handler.put(table, put);
247 
248     TGet get = new TGet(wrap(rowName));
249 
250     TResult result = handler.get(table, get);
251     assertArrayEquals(rowName, result.getRow());
252     List<TColumnValue> returnedColumnValues = result.getColumnValues();
253     assertTColumnValuesEqual(columnValues, returnedColumnValues);
254   }
255 
256   @Test
257   public void testPutGetMultiple() throws Exception {
258     ThriftHBaseServiceHandler handler = createHandler();
259     ByteBuffer table = wrap(tableAname);
260     byte[] rowName1 = "testPutGetMultiple1".getBytes();
261     byte[] rowName2 = "testPutGetMultiple2".getBytes();
262 
263     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
264     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
265     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
266     List<TPut> puts = new ArrayList<TPut>();
267     puts.add(new TPut(wrap(rowName1), columnValues));
268     puts.add(new TPut(wrap(rowName2), columnValues));
269 
270     handler.putMultiple(table, puts);
271 
272     List<TGet> gets = new ArrayList<TGet>();
273     gets.add(new TGet(wrap(rowName1)));
274     gets.add(new TGet(wrap(rowName2)));
275 
276     List<TResult> results = handler.getMultiple(table, gets);
277     assertEquals(2, results.size());
278 
279     assertArrayEquals(rowName1, results.get(0).getRow());
280     assertTColumnValuesEqual(columnValues, results.get(0).getColumnValues());
281 
282     assertArrayEquals(rowName2, results.get(1).getRow());
283     assertTColumnValuesEqual(columnValues, results.get(1).getColumnValues());
284   }
285 
286   @Test
287   public void testDeleteMultiple() throws Exception {
288     ThriftHBaseServiceHandler handler = createHandler();
289     ByteBuffer table = wrap(tableAname);
290     byte[] rowName1 = "testDeleteMultiple1".getBytes();
291     byte[] rowName2 = "testDeleteMultiple2".getBytes();
292 
293     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
294     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
295     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
296     List<TPut> puts = new ArrayList<TPut>();
297     puts.add(new TPut(wrap(rowName1), columnValues));
298     puts.add(new TPut(wrap(rowName2), columnValues));
299 
300     handler.putMultiple(table, puts);
301 
302     List<TDelete> deletes = new ArrayList<TDelete>();
303     deletes.add(new TDelete(wrap(rowName1)));
304     deletes.add(new TDelete(wrap(rowName2)));
305 
306     List<TDelete> deleteResults = handler.deleteMultiple(table, deletes);
307     // 0 means they were all successfully applies
308     assertEquals(0, deleteResults.size());
309 
310     assertFalse(handler.exists(table, new TGet(wrap(rowName1))));
311     assertFalse(handler.exists(table, new TGet(wrap(rowName2))));
312   }
313 
314   @Test
315   public void testDelete() throws Exception {
316     ThriftHBaseServiceHandler handler = createHandler();
317     byte[] rowName = "testDelete".getBytes();
318     ByteBuffer table = wrap(tableAname);
319 
320     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
321     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
322       wrap(valueAname));
323     TColumnValue columnValueB = new TColumnValue(wrap(familyBname), wrap(qualifierBname),
324       wrap(valueBname));
325     columnValues.add(columnValueA);
326     columnValues.add(columnValueB);
327     TPut put = new TPut(wrap(rowName), columnValues);
328 
329     put.setColumnValues(columnValues);
330 
331     handler.put(table, put);
332 
333     TDelete delete = new TDelete(wrap(rowName));
334     List<TColumn> deleteColumns = new ArrayList<TColumn>();
335     TColumn deleteColumn = new TColumn(wrap(familyAname));
336     deleteColumn.setQualifier(qualifierAname);
337     deleteColumns.add(deleteColumn);
338     delete.setColumns(deleteColumns);
339 
340     handler.deleteSingle(table, delete);
341 
342     TGet get = new TGet(wrap(rowName));
343     TResult result = handler.get(table, get);
344     assertArrayEquals(rowName, result.getRow());
345     List<TColumnValue> returnedColumnValues = result.getColumnValues();
346     List<TColumnValue> expectedColumnValues = new ArrayList<TColumnValue>();
347     expectedColumnValues.add(columnValueB);
348     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
349   }
350 
351   @Test
352   public void testDeleteAllTimestamps() throws Exception {
353     ThriftHBaseServiceHandler handler = createHandler();
354     byte[] rowName = "testDeleteAllTimestamps".getBytes();
355     ByteBuffer table = wrap(tableAname);
356 
357     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
358     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
359       wrap(valueAname));
360     columnValueA.setTimestamp(System.currentTimeMillis() - 10);
361     columnValues.add(columnValueA);
362     TPut put = new TPut(wrap(rowName), columnValues);
363 
364     put.setColumnValues(columnValues);
365 
366     handler.put(table, put);
367     columnValueA.setTimestamp(System.currentTimeMillis());
368     handler.put(table, put);
369 
370     TGet get = new TGet(wrap(rowName));
371     get.setMaxVersions(2);
372     TResult result = handler.get(table, get);
373     assertEquals(2, result.getColumnValuesSize());
374 
375     TDelete delete = new TDelete(wrap(rowName));
376     List<TColumn> deleteColumns = new ArrayList<TColumn>();
377     TColumn deleteColumn = new TColumn(wrap(familyAname));
378     deleteColumn.setQualifier(qualifierAname);
379     deleteColumns.add(deleteColumn);
380     delete.setColumns(deleteColumns);
381     delete.setDeleteType(TDeleteType.DELETE_COLUMNS); // This is the default anyway.
382 
383     handler.deleteSingle(table, delete);
384 
385     get = new TGet(wrap(rowName));
386     result = handler.get(table, get);
387     assertNull(result.getRow());
388     assertEquals(0, result.getColumnValuesSize());
389   }
390 
391   @Test
392   public void testDeleteSingleTimestamp() throws Exception {
393     ThriftHBaseServiceHandler handler = createHandler();
394     byte[] rowName = "testDeleteSingleTimestamp".getBytes();
395     ByteBuffer table = wrap(tableAname);
396 
397     long timestamp1 = System.currentTimeMillis() - 10;
398     long timestamp2 = System.currentTimeMillis();
399 
400     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
401     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
402       wrap(valueAname));
403     columnValueA.setTimestamp(timestamp1);
404     columnValues.add(columnValueA);
405     TPut put = new TPut(wrap(rowName), columnValues);
406 
407     put.setColumnValues(columnValues);
408 
409     handler.put(table, put);
410     columnValueA.setTimestamp(timestamp2);
411     handler.put(table, put);
412 
413     TGet get = new TGet(wrap(rowName));
414     get.setMaxVersions(2);
415     TResult result = handler.get(table, get);
416     assertEquals(2, result.getColumnValuesSize());
417 
418     TDelete delete = new TDelete(wrap(rowName));
419     List<TColumn> deleteColumns = new ArrayList<TColumn>();
420     TColumn deleteColumn = new TColumn(wrap(familyAname));
421     deleteColumn.setQualifier(qualifierAname);
422     deleteColumns.add(deleteColumn);
423     delete.setColumns(deleteColumns);
424     delete.setDeleteType(TDeleteType.DELETE_COLUMN);
425 
426     handler.deleteSingle(table, delete);
427 
428     get = new TGet(wrap(rowName));
429     result = handler.get(table, get);
430     assertArrayEquals(rowName, result.getRow());
431     assertEquals(1, result.getColumnValuesSize());
432     // the older timestamp should remain.
433     assertEquals(timestamp1, result.getColumnValues().get(0).getTimestamp());
434   }
435 
436   @Test
437   public void testIncrement() throws Exception {
438     ThriftHBaseServiceHandler handler = createHandler();
439     byte[] rowName = "testIncrement".getBytes();
440     ByteBuffer table = wrap(tableAname);
441 
442     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
443     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname),
444       wrap(Bytes.toBytes(1L))));
445     TPut put = new TPut(wrap(rowName), columnValues);
446     put.setColumnValues(columnValues);
447     handler.put(table, put);
448 
449     List<TColumnIncrement> incrementColumns = new ArrayList<TColumnIncrement>();
450     incrementColumns.add(new TColumnIncrement(wrap(familyAname), wrap(qualifierAname)));
451     TIncrement increment = new TIncrement(wrap(rowName), incrementColumns);
452     handler.increment(table, increment);
453 
454     TGet get = new TGet(wrap(rowName));
455     TResult result = handler.get(table, get);
456 
457     assertArrayEquals(rowName, result.getRow());
458     assertEquals(1, result.getColumnValuesSize());
459     TColumnValue columnValue = result.getColumnValues().get(0);
460     assertArrayEquals(Bytes.toBytes(2L), columnValue.getValue());
461   }
462 
463   @Test
464   public void testAppend() throws Exception {
465     ThriftHBaseServiceHandler handler = createHandler();
466     byte[] rowName = "testAppend".getBytes();
467     ByteBuffer table = wrap(tableAname);
468     byte[] v1 = Bytes.toBytes("42");
469     byte[] v2 = Bytes.toBytes("23");
470     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
471     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(v1)));
472     TPut put = new TPut(wrap(rowName), columnValues);
473     put.setColumnValues(columnValues);
474     handler.put(table, put);
475 
476     List<TColumnValue> appendColumns = new ArrayList<TColumnValue>();
477     appendColumns.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(v2)));
478     TAppend append = new TAppend(wrap(rowName), appendColumns);
479     handler.append(table, append);
480 
481     TGet get = new TGet(wrap(rowName));
482     TResult result = handler.get(table, get);
483 
484     assertArrayEquals(rowName, result.getRow());
485     assertEquals(1, result.getColumnValuesSize());
486     TColumnValue columnValue = result.getColumnValues().get(0);
487     assertArrayEquals(Bytes.add(v1, v2), columnValue.getValue());
488   }
489 
490   /**
491    * check that checkAndPut fails if the cell does not exist, then put in the cell, then check
492    * that the checkAndPut succeeds.
493    *
494    * @throws Exception
495    */
496   @Test
497   public void testCheckAndPut() throws Exception {
498     ThriftHBaseServiceHandler handler = createHandler();
499     byte[] rowName = "testCheckAndPut".getBytes();
500     ByteBuffer table = wrap(tableAname);
501 
502     List<TColumnValue> columnValuesA = new ArrayList<TColumnValue>();
503     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
504       wrap(valueAname));
505     columnValuesA.add(columnValueA);
506     TPut putA = new TPut(wrap(rowName), columnValuesA);
507     putA.setColumnValues(columnValuesA);
508 
509     List<TColumnValue> columnValuesB = new ArrayList<TColumnValue>();
510     TColumnValue columnValueB = new TColumnValue(wrap(familyBname), wrap(qualifierBname),
511       wrap(valueBname));
512     columnValuesB.add(columnValueB);
513     TPut putB = new TPut(wrap(rowName), columnValuesB);
514     putB.setColumnValues(columnValuesB);
515 
516     assertFalse(handler.checkAndPut(table, wrap(rowName), wrap(familyAname),
517       wrap(qualifierAname), wrap(valueAname), putB));
518 
519     TGet get = new TGet(wrap(rowName));
520     TResult result = handler.get(table, get);
521     assertEquals(0, result.getColumnValuesSize());
522 
523     handler.put(table, putA);
524 
525     assertTrue(handler.checkAndPut(table, wrap(rowName), wrap(familyAname),
526       wrap(qualifierAname), wrap(valueAname), putB));
527 
528     result = handler.get(table, get);
529     assertArrayEquals(rowName, result.getRow());
530     List<TColumnValue> returnedColumnValues = result.getColumnValues();
531     List<TColumnValue> expectedColumnValues = new ArrayList<TColumnValue>();
532     expectedColumnValues.add(columnValueA);
533     expectedColumnValues.add(columnValueB);
534     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
535   }
536 
537   /**
538    * check that checkAndDelete fails if the cell does not exist, then put in the cell, then
539    * check that the checkAndDelete succeeds.
540    *
541    * @throws Exception
542    */
543   @Test
544   public void testCheckAndDelete() throws Exception {
545     ThriftHBaseServiceHandler handler = createHandler();
546     byte[] rowName = "testCheckAndDelete".getBytes();
547     ByteBuffer table = wrap(tableAname);
548 
549     List<TColumnValue> columnValuesA = new ArrayList<TColumnValue>();
550     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
551       wrap(valueAname));
552     columnValuesA.add(columnValueA);
553     TPut putA = new TPut(wrap(rowName), columnValuesA);
554     putA.setColumnValues(columnValuesA);
555 
556     List<TColumnValue> columnValuesB = new ArrayList<TColumnValue>();
557     TColumnValue columnValueB = new TColumnValue(wrap(familyBname), wrap(qualifierBname),
558       wrap(valueBname));
559     columnValuesB.add(columnValueB);
560     TPut putB = new TPut(wrap(rowName), columnValuesB);
561     putB.setColumnValues(columnValuesB);
562 
563     // put putB so that we know whether the row has been deleted or not
564     handler.put(table, putB);
565 
566     TDelete delete = new TDelete(wrap(rowName));
567 
568     assertFalse(handler.checkAndDelete(table, wrap(rowName), wrap(familyAname),
569         wrap(qualifierAname), wrap(valueAname), delete));
570 
571     TGet get = new TGet(wrap(rowName));
572     TResult result = handler.get(table, get);
573     assertArrayEquals(rowName, result.getRow());
574     assertTColumnValuesEqual(columnValuesB, result.getColumnValues());
575 
576     handler.put(table, putA);
577 
578     assertTrue(handler.checkAndDelete(table, wrap(rowName), wrap(familyAname),
579       wrap(qualifierAname), wrap(valueAname), delete));
580 
581     result = handler.get(table, get);
582     assertFalse(result.isSetRow());
583     assertEquals(0, result.getColumnValuesSize());
584   }
585 
586   @Test
587   public void testScan() throws Exception {
588     ThriftHBaseServiceHandler handler = createHandler();
589     ByteBuffer table = wrap(tableAname);
590 
591     // insert data
592     TColumnValue columnValue = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
593       wrap(valueAname));
594     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
595     columnValues.add(columnValue);
596     for (int i = 0; i < 10; i++) {
597       TPut put = new TPut(wrap(("testScan" + i).getBytes()), columnValues);
598       handler.put(table, put);
599     }
600 
601     // create scan instance
602     TScan scan = new TScan();
603     List<TColumn> columns = new ArrayList<TColumn>();
604     TColumn column = new TColumn();
605     column.setFamily(familyAname);
606     column.setQualifier(qualifierAname);
607     columns.add(column);
608     scan.setColumns(columns);
609     scan.setStartRow("testScan".getBytes());
610     scan.setStopRow("testScan\uffff".getBytes());
611 
612     // get scanner and rows
613     int scanId = handler.openScanner(table, scan);
614     List<TResult> results = handler.getScannerRows(scanId, 10);
615     assertEquals(10, results.size());
616     for (int i = 0; i < 10; i++) {
617       // check if the rows are returned and in order
618       assertArrayEquals(("testScan" + i).getBytes(), results.get(i).getRow());
619     }
620 
621     // check that we are at the end of the scan
622     results = handler.getScannerRows(scanId, 10);
623     assertEquals(0, results.size());
624 
625     // close scanner and check that it was indeed closed
626     handler.closeScanner(scanId);
627     try {
628       handler.getScannerRows(scanId, 10);
629       fail("Scanner id should be invalid");
630     } catch (TIllegalArgument e) {
631     }
632   }
633 
634   /**
635    * Tests keeping a HBase scanner alive for long periods of time. Each call to getScannerRow()
636    * should reset the ConnectionCache timeout for the scanner's connection
637    * @throws Exception
638    */
639   @Test
640   public void testLongLivedScan() throws Exception {
641     int numTrials = 6;
642     int trialPause = 1000;
643     int cleanUpInterval = 100;
644     Configuration conf = new Configuration(UTIL.getConfiguration());
645     // Set the ConnectionCache timeout to trigger halfway through the trials
646     conf.setInt(ThriftHBaseServiceHandler.MAX_IDLETIME, (numTrials / 2) * trialPause);
647     conf.setInt(ThriftHBaseServiceHandler.CLEANUP_INTERVAL, cleanUpInterval);
648     ThriftHBaseServiceHandler handler = new ThriftHBaseServiceHandler(conf,
649         UserProvider.instantiate(conf));
650 
651     ByteBuffer table = wrap(tableAname);
652     // insert data
653     TColumnValue columnValue = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
654         wrap(valueAname));
655     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
656     columnValues.add(columnValue);
657     for (int i = 0; i < numTrials; i++) {
658       TPut put = new TPut(wrap(("testScan" + i).getBytes()), columnValues);
659       handler.put(table, put);
660     }
661 
662     // create scan instance
663     TScan scan = new TScan();
664     List<TColumn> columns = new ArrayList<TColumn>();
665     TColumn column = new TColumn();
666     column.setFamily(familyAname);
667     column.setQualifier(qualifierAname);
668     columns.add(column);
669     scan.setColumns(columns);
670     scan.setStartRow("testScan".getBytes());
671     scan.setStopRow("testScan\uffff".getBytes());
672     // Prevent the scanner from caching results
673     scan.setCaching(1);
674 
675     // get scanner and rows
676     int scanId = handler.openScanner(table, scan);
677     for (int i = 0; i < numTrials; i++) {
678       // Make sure that the Scanner doesn't throw an exception after the ConnectionCache timeout
679       List<TResult> results = handler.getScannerRows(scanId, 1);
680       assertArrayEquals(("testScan" + i).getBytes(), results.get(0).getRow());
681       Thread.sleep(trialPause);
682     }
683   }
684 
685   @Test
686   public void testReverseScan() throws Exception {
687     ThriftHBaseServiceHandler handler = createHandler();
688     ByteBuffer table = wrap(tableAname);
689 
690     // insert data
691     TColumnValue columnValue = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
692       wrap(valueAname));
693     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
694     columnValues.add(columnValue);
695     for (int i = 0; i < 10; i++) {
696       TPut put = new TPut(wrap(("testReverseScan" + i).getBytes()), columnValues);
697       handler.put(table, put);
698     }
699 
700     // create reverse scan instance
701     TScan scan = new TScan();
702     scan.setReversed(true);
703     List<TColumn> columns = new ArrayList<TColumn>();
704     TColumn column = new TColumn();
705     column.setFamily(familyAname);
706     column.setQualifier(qualifierAname);
707     columns.add(column);
708     scan.setColumns(columns);
709     scan.setStartRow("testReverseScan\uffff".getBytes());
710     scan.setStopRow("testReverseScan".getBytes());
711 
712     // get scanner and rows
713     int scanId = handler.openScanner(table, scan);
714     List<TResult> results = handler.getScannerRows(scanId, 10);
715     assertEquals(10, results.size());
716     for (int i = 0; i < 10; i++) {
717       // check if the rows are returned and in order
718       assertArrayEquals(("testReverseScan" + (9 - i)).getBytes(), results.get(i).getRow());
719     }
720 
721     // check that we are at the end of the scan
722     results = handler.getScannerRows(scanId, 10);
723     assertEquals(0, results.size());
724 
725     // close scanner and check that it was indeed closed
726     handler.closeScanner(scanId);
727     try {
728       handler.getScannerRows(scanId, 10);
729       fail("Scanner id should be invalid");
730     } catch (TIllegalArgument e) {
731     }
732   }
733 
734   @Test
735   public void testScanWithFilter() throws Exception {
736     ThriftHBaseServiceHandler handler = createHandler();
737     ByteBuffer table = wrap(tableAname);
738 
739     // insert data
740     TColumnValue columnValue = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
741       wrap(valueAname));
742     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
743     columnValues.add(columnValue);
744     for (int i = 0; i < 10; i++) {
745       TPut put = new TPut(wrap(("testScanWithFilter" + i).getBytes()), columnValues);
746       handler.put(table, put);
747     }
748 
749     // create scan instance with filter
750     TScan scan = new TScan();
751     List<TColumn> columns = new ArrayList<TColumn>();
752     TColumn column = new TColumn();
753     column.setFamily(familyAname);
754     column.setQualifier(qualifierAname);
755     columns.add(column);
756     scan.setColumns(columns);
757     scan.setStartRow("testScanWithFilter".getBytes());
758     scan.setStopRow("testScanWithFilter\uffff".getBytes());
759     // only get the key part
760     scan.setFilterString(wrap(("KeyOnlyFilter()").getBytes()));
761 
762     // get scanner and rows
763     int scanId = handler.openScanner(table, scan);
764     List<TResult> results = handler.getScannerRows(scanId, 10);
765     assertEquals(10, results.size());
766     for (int i = 0; i < 10; i++) {
767       // check if the rows are returned and in order
768       assertArrayEquals(("testScanWithFilter" + i).getBytes(), results.get(i).getRow());
769       // check that the value is indeed stripped by the filter
770       assertEquals(0, results.get(i).getColumnValues().get(0).getValue().length);
771     }
772 
773     // check that we are at the end of the scan
774     results = handler.getScannerRows(scanId, 10);
775     assertEquals(0, results.size());
776 
777     // close scanner and check that it was indeed closed
778     handler.closeScanner(scanId);
779     try {
780       handler.getScannerRows(scanId, 10);
781       fail("Scanner id should be invalid");
782     } catch (TIllegalArgument e) {
783     }
784   }
785 
786   @Test
787   public void testScanWithColumnFamilyTimeRange() throws Exception {
788     ThriftHBaseServiceHandler handler = createHandler();
789     ByteBuffer table = wrap(tableAname);
790 
791     // insert data
792     TColumnValue familyAColumnValue = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
793         wrap(valueAname));
794     TColumnValue familyBColumnValue = new TColumnValue(wrap(familyBname), wrap(qualifierBname),
795         wrap(valueBname));
796     long minTimestamp = System.currentTimeMillis();
797     for (int i = 0; i < 10; i++) {
798       familyAColumnValue.setTimestamp(minTimestamp + i);
799       familyBColumnValue.setTimestamp(minTimestamp + i);
800       List<TColumnValue> columnValues = new ArrayList<>(2);
801       columnValues.add(familyAColumnValue);
802       columnValues.add(familyBColumnValue);
803       TPut put = new TPut(wrap(("testScanWithColumnFamilyTimeRange" + i).getBytes()),
804           columnValues);
805       handler.put(table, put);
806     }
807 
808     // create scan instance with column family time range
809     TScan scan = new TScan();
810     Map<ByteBuffer,TTimeRange> colFamTimeRangeMap = new HashMap<>(2);
811     colFamTimeRangeMap.put(wrap(familyAname), new TTimeRange(minTimestamp + 3, minTimestamp + 5));
812     colFamTimeRangeMap.put(wrap(familyBname), new TTimeRange(minTimestamp + 6, minTimestamp + 9));
813     scan.setColFamTimeRangeMap(colFamTimeRangeMap);
814 
815     // get scanner and rows
816     int scanId = handler.openScanner(table, scan);
817     List<TResult> results = handler.getScannerRows(scanId, 5);
818     assertEquals(5, results.size());
819     int familyACount = 0;
820     int familyBCount = 0;
821     for (TResult result : results) {
822       List<TColumnValue> columnValues = result.getColumnValues();
823       if (CollectionUtils.isNotEmpty(columnValues)) {
824         if (Bytes.equals(familyAname, columnValues.get(0).getFamily())) {
825           familyACount++;
826         } else if (Bytes.equals(familyBname, columnValues.get(0).getFamily())) {
827           familyBCount++;
828         }
829       }
830     }
831     assertEquals(2, familyACount);
832     assertEquals(3, familyBCount);
833 
834     // check that we are at the end of the scan
835     results = handler.getScannerRows(scanId, 1);
836     assertEquals(0, results.size());
837 
838     // close scanner and check that it was indeed closed
839     handler.closeScanner(scanId);
840     try {
841       handler.getScannerRows(scanId, 1);
842       fail("Scanner id should be invalid");
843     } catch (TIllegalArgument e) {
844     }
845   }
846 
847   @Test
848   public void testSmallScan() throws Exception {
849     ThriftHBaseServiceHandler handler = createHandler();
850     ByteBuffer table = wrap(tableAname);
851 
852     // insert data
853     TColumnValue columnValue = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
854             wrap(valueAname));
855     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
856     columnValues.add(columnValue);
857     for (int i = 0; i < 10; i++) {
858       TPut put = new TPut(wrap(("testSmallScan" + i).getBytes()), columnValues);
859       handler.put(table, put);
860     }
861 
862     // small scan instance
863     TScan scan = new TScan();
864     scan.setStartRow("testSmallScan".getBytes());
865     scan.setStopRow("testSmallScan\uffff".getBytes());
866     scan.setSmall(true);
867     scan.setCaching(2);
868 
869     // get scanner and rows
870     int scanId = handler.openScanner(table, scan);
871     List<TResult> results = handler.getScannerRows(scanId, 10);
872     assertEquals(10, results.size());
873     for (int i = 0; i < 10; i++) {
874       // check if the rows are returned and in order
875       assertArrayEquals(("testSmallScan" + i).getBytes(), results.get(i).getRow());
876     }
877 
878     // check that we are at the end of the scan
879     results = handler.getScannerRows(scanId, 10);
880     assertEquals(0, results.size());
881 
882     // close scanner and check that it was indeed closed
883     handler.closeScanner(scanId);
884     try {
885       handler.getScannerRows(scanId, 10);
886       fail("Scanner id should be invalid");
887     } catch (TIllegalArgument e) {
888     }
889   }
890 
891   @Test
892   public void testPutTTL() throws Exception {
893     ThriftHBaseServiceHandler handler = createHandler();
894     byte[] rowName = "testPutTTL".getBytes();
895     ByteBuffer table = wrap(tableAname);
896     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
897 
898     // Add some dummy data
899     columnValues.add(
900         new TColumnValue(
901             wrap(familyAname),
902             wrap(qualifierAname),
903             wrap(Bytes.toBytes(1L))));
904 
905 
906     TPut put = new TPut(wrap(rowName), columnValues);
907     put.setColumnValues(columnValues);
908 
909     Map<ByteBuffer, ByteBuffer> attributes = new HashMap<>();
910 
911     // Time in ms for the kv's to live.
912     long ttlTimeMs = 2000L;
913 
914     // the _ttl attribute is a number of ms ttl for key values in this put.
915     attributes.put(wrap(Bytes.toBytes("_ttl")), wrap(Bytes.toBytes(ttlTimeMs)));
916     // Attach the attributes
917     put.setAttributes(attributes);
918     // Send it.
919     handler.put(table, put);
920 
921     // Now get the data back
922     TGet getOne = new TGet(wrap(rowName));
923     TResult resultOne = handler.get(table, getOne);
924 
925     // It's there.
926     assertArrayEquals(rowName, resultOne.getRow());
927     assertEquals(1, resultOne.getColumnValuesSize());
928 
929     // Sleep 30 seconds just to make 100% sure that the key value should be expired.
930     Thread.sleep(ttlTimeMs * 15);
931 
932     TGet getTwo = new TGet(wrap(rowName));
933     TResult resultTwo = handler.get(table, getTwo);
934 
935 
936     // Nothing should be there since it's ttl'd out.
937     assertNull(resultTwo.getRow());
938     assertEquals(0, resultTwo.getColumnValuesSize());
939   }
940 
941   /**
942    * Padding numbers to make comparison of sort order easier in a for loop
943    *
944    * @param n  The number to pad.
945    * @param pad  The length to pad up to.
946    * @return The padded number as a string.
947    */
948   private String pad(int n, byte pad) {
949     String res = Integer.toString(n);
950     while (res.length() < pad) res = "0" + res;
951     return res;
952   }
953 
954   @Test
955   public void testScanWithBatchSize() throws Exception {
956     ThriftHBaseServiceHandler handler = createHandler();
957     ByteBuffer table = wrap(tableAname);
958 
959     // insert data
960     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
961     for (int i = 0; i < 100; i++) {
962       String colNum = pad(i, (byte) 3);
963       TColumnValue columnValue = new TColumnValue(wrap(familyAname),
964         wrap(("col" + colNum).getBytes()), wrap(("val" + colNum).getBytes()));
965       columnValues.add(columnValue);
966     }
967     TPut put = new TPut(wrap(("testScanWithBatchSize").getBytes()), columnValues);
968     handler.put(table, put);
969 
970     // create scan instance
971     TScan scan = new TScan();
972     List<TColumn> columns = new ArrayList<TColumn>();
973     TColumn column = new TColumn();
974     column.setFamily(familyAname);
975     columns.add(column);
976     scan.setColumns(columns);
977     scan.setStartRow("testScanWithBatchSize".getBytes());
978     scan.setStopRow("testScanWithBatchSize\uffff".getBytes());
979     // set batch size to 10 columns per call
980     scan.setBatchSize(10);
981 
982     // get scanner
983     int scanId = handler.openScanner(table, scan);
984     List<TResult> results = null;
985     for (int i = 0; i < 10; i++) {
986       // get batch for single row (10x10 is what we expect)
987       results = handler.getScannerRows(scanId, 1);
988       assertEquals(1, results.size());
989       // check length of batch
990       List<TColumnValue> cols = results.get(0).getColumnValues();
991       assertEquals(10, cols.size());
992       // check if the columns are returned and in order
993       for (int y = 0; y < 10; y++) {
994         int colNum = y + (10 * i);
995         String colNumPad = pad(colNum, (byte) 3);
996         assertArrayEquals(("col" + colNumPad).getBytes(), cols.get(y).getQualifier());
997       }
998     }
999 
1000     // check that we are at the end of the scan
1001     results = handler.getScannerRows(scanId, 1);
1002     assertEquals(0, results.size());
1003 
1004     // close scanner and check that it was indeed closed
1005     handler.closeScanner(scanId);
1006     try {
1007       handler.getScannerRows(scanId, 1);
1008       fail("Scanner id should be invalid");
1009     } catch (TIllegalArgument e) {
1010     }
1011   }
1012 
1013   @Test
1014   public void testGetScannerResults() throws Exception {
1015     ThriftHBaseServiceHandler handler = createHandler();
1016     ByteBuffer table = wrap(tableAname);
1017 
1018     // insert data
1019     TColumnValue columnValue =
1020         new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname));
1021     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
1022     columnValues.add(columnValue);
1023     for (int i = 0; i < 20; i++) {
1024       TPut put =
1025           new TPut(wrap(("testGetScannerResults" + pad(i, (byte) 2)).getBytes()), columnValues);
1026       handler.put(table, put);
1027     }
1028 
1029     // create scan instance
1030     TScan scan = new TScan();
1031     List<TColumn> columns = new ArrayList<TColumn>();
1032     TColumn column = new TColumn();
1033     column.setFamily(familyAname);
1034     column.setQualifier(qualifierAname);
1035     columns.add(column);
1036     scan.setColumns(columns);
1037     scan.setStartRow("testGetScannerResults".getBytes());
1038 
1039     // get 5 rows and check the returned results
1040     scan.setStopRow("testGetScannerResults05".getBytes());
1041     List<TResult> results = handler.getScannerResults(table, scan, 5);
1042     assertEquals(5, results.size());
1043     for (int i = 0; i < 5; i++) {
1044       // check if the rows are returned and in order
1045       assertArrayEquals(("testGetScannerResults" + pad(i, (byte) 2)).getBytes(), results.get(i)
1046           .getRow());
1047     }
1048 
1049     // get 10 rows and check the returned results
1050     scan.setStopRow("testGetScannerResults10".getBytes());
1051     results = handler.getScannerResults(table, scan, 10);
1052     assertEquals(10, results.size());
1053     for (int i = 0; i < 10; i++) {
1054       // check if the rows are returned and in order
1055       assertArrayEquals(("testGetScannerResults" + pad(i, (byte) 2)).getBytes(), results.get(i)
1056           .getRow());
1057     }
1058 
1059     // get 20 rows and check the returned results
1060     scan.setStopRow("testGetScannerResults20".getBytes());
1061     results = handler.getScannerResults(table, scan, 20);
1062     assertEquals(20, results.size());
1063     for (int i = 0; i < 20; i++) {
1064       // check if the rows are returned and in order
1065       assertArrayEquals(("testGetScannerResults" + pad(i, (byte) 2)).getBytes(), results.get(i)
1066           .getRow());
1067     }
1068 
1069     // reverse scan
1070     scan = new TScan();
1071     scan.setColumns(columns);
1072     scan.setReversed(true);
1073     scan.setStartRow("testGetScannerResults20".getBytes());
1074     scan.setStopRow("testGetScannerResults".getBytes());
1075     results = handler.getScannerResults(table, scan, 20);
1076     assertEquals(20, results.size());
1077     for (int i = 0; i < 20; i++) {
1078       // check if the rows are returned and in order
1079       assertArrayEquals(("testGetScannerResults" + pad(19 - i, (byte) 2)).getBytes(), results.get(i)
1080           .getRow());
1081     }
1082  }
1083 
1084   @Test
1085   public void testFilterRegistration() throws Exception {
1086     Configuration conf = UTIL.getConfiguration();
1087     conf.set("hbase.thrift.filters", "MyFilter:filterclass");
1088     ThriftServer.registerFilters(conf);
1089     Map<String, String> registeredFilters = ParseFilter.getAllFilters();
1090     assertEquals("filterclass", registeredFilters.get("MyFilter"));
1091   }
1092 
1093   @Test
1094   public void testMetrics() throws Exception {
1095     Configuration conf = UTIL.getConfiguration();
1096     ThriftMetrics metrics = getMetrics(conf);
1097     ThriftHBaseServiceHandler hbaseHandler = createHandler();
1098     THBaseService.Iface handler =
1099         ThriftHBaseServiceHandler.newInstance(hbaseHandler, metrics);
1100     byte[] rowName = "testMetrics".getBytes();
1101     ByteBuffer table = wrap(tableAname);
1102 
1103     TGet get = new TGet(wrap(rowName));
1104     assertFalse(handler.exists(table, get));
1105 
1106     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
1107     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
1108     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname),  wrap(valueBname)));
1109     TPut put = new TPut(wrap(rowName), columnValues);
1110     put.setColumnValues(columnValues);
1111 
1112     handler.put(table, put);
1113 
1114     assertTrue(handler.exists(table, get));
1115     metricsHelper.assertCounter("put_num_ops", 1, metrics.getSource());
1116     metricsHelper.assertCounter( "exists_num_ops", 2, metrics.getSource());
1117   }
1118 
1119   private static ThriftMetrics getMetrics(Configuration conf) throws Exception {
1120     ThriftMetrics m = new ThriftMetrics(conf, ThriftMetrics.ThriftServerType.TWO);
1121     m.getSource().init(); //Clear all the metrics
1122     return m;
1123   }
1124 
1125   @Test
1126   public void testMetricsWithException() throws Exception {
1127     byte[] rowkey = Bytes.toBytes("row1");
1128     byte[] family = Bytes.toBytes("f");
1129     byte[] col = Bytes.toBytes("c");
1130     // create a table which will throw exceptions for requests
1131     TableName tableName = TableName.valueOf("testMetricsWithException");
1132     HTableDescriptor tableDesc = new HTableDescriptor(tableName);
1133     tableDesc.addCoprocessor(ErrorThrowingGetObserver.class.getName());
1134     tableDesc.addFamily(new HColumnDescriptor(family));
1135 
1136     Table table = UTIL.createTable(tableDesc, null);
1137     table.put(new Put(rowkey).addColumn(family, col, Bytes.toBytes("val1")));
1138 
1139     ThriftHBaseServiceHandler hbaseHandler = createHandler();
1140     ThriftMetrics metrics = getMetrics(UTIL.getConfiguration());
1141     THBaseService.Iface handler =
1142         ThriftHBaseServiceHandler.newInstance(hbaseHandler, metrics);
1143     ByteBuffer tTableName = wrap(tableName.getName());
1144 
1145     // check metrics increment with a successful get
1146     long preGetCounter = metricsHelper.checkCounterExists("get_num_ops", metrics.getSource()) ?
1147         metricsHelper.getCounter("get_num_ops", metrics.getSource()) :
1148         0;
1149     TGet tGet = new TGet(wrap(rowkey));
1150     TResult tResult = handler.get(tTableName, tGet);
1151 
1152     List<TColumnValue> expectedColumnValues = Lists.newArrayList(
1153         new TColumnValue(wrap(family), wrap(col), wrap(Bytes.toBytes("val1")))
1154     );
1155     assertArrayEquals(rowkey, tResult.getRow());
1156     List<TColumnValue> returnedColumnValues = tResult.getColumnValues();
1157     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
1158 
1159     metricsHelper.assertCounter("get_num_ops", preGetCounter + 1, metrics.getSource());
1160 
1161     // check metrics increment when the get throws each exception type
1162     for (ErrorThrowingGetObserver.ErrorType type : ErrorThrowingGetObserver.ErrorType.values()) {
1163       testExceptionType(handler, metrics, tTableName, rowkey, type);
1164     }
1165   }
1166 
1167   private void testExceptionType(THBaseService.Iface handler, ThriftMetrics metrics,
1168                                  ByteBuffer tTableName, byte[] rowkey, ErrorThrowingGetObserver.ErrorType errorType) {
1169     long preGetCounter = metricsHelper.getCounter("get_num_ops", metrics.getSource());
1170     String exceptionKey = errorType.getMetricName();
1171     long preExceptionCounter = metricsHelper.checkCounterExists(exceptionKey, metrics.getSource()) ?
1172         metricsHelper.getCounter(exceptionKey, metrics.getSource()) :
1173         0;
1174     TGet tGet = new TGet(wrap(rowkey));
1175     Map<ByteBuffer, ByteBuffer> attributes = new HashMap<>();
1176     attributes.put(wrap(Bytes.toBytes(ErrorThrowingGetObserver.SHOULD_ERROR_ATTRIBUTE)),
1177         wrap(Bytes.toBytes(errorType.name())));
1178     tGet.setAttributes(attributes);
1179     try {
1180       TResult tResult = handler.get(tTableName, tGet);
1181       fail("Get with error attribute should have thrown an exception");
1182     } catch (TException e) {
1183       LOG.info("Received exception: ", e);
1184       metricsHelper.assertCounter("get_num_ops", preGetCounter + 1, metrics.getSource());
1185       metricsHelper.assertCounter(exceptionKey, preExceptionCounter + 1, metrics.getSource());
1186     }
1187 
1188   }
1189 
1190   /**
1191    * See HBASE-17611
1192    *
1193    * Latency metrics were capped at ~ 2 seconds due to the use of an int variable to capture the
1194    * duration.
1195    */
1196   @Test
1197   public void testMetricsPrecision() throws Exception {
1198     byte[] rowkey = Bytes.toBytes("row1");
1199     byte[] family = Bytes.toBytes("f");
1200     byte[] col = Bytes.toBytes("c");
1201     // create a table which will throw exceptions for requests
1202     TableName tableName = TableName.valueOf("testMetricsPrecision");
1203     HTableDescriptor tableDesc = new HTableDescriptor(tableName);
1204     tableDesc.addCoprocessor(DelayingRegionObserver.class.getName());
1205     tableDesc.addFamily(new HColumnDescriptor(family));
1206 
1207     Table table = null;
1208     try {
1209       table = UTIL.createTable(tableDesc, null);
1210 
1211       table.put(new Put(rowkey).addColumn(family, col, Bytes.toBytes("val1")));
1212 
1213       ThriftHBaseServiceHandler hbaseHandler = createHandler();
1214       ThriftMetrics metrics = getMetrics(UTIL.getConfiguration());
1215       THBaseService.Iface handler =
1216           ThriftHBaseServiceHandler.newInstance(hbaseHandler, metrics);
1217       ByteBuffer tTableName = wrap(tableName.getName());
1218 
1219       // check metrics latency with a successful get
1220       TGet tGet = new TGet(wrap(rowkey));
1221       TResult tResult = handler.get(tTableName, tGet);
1222 
1223       List<TColumnValue> expectedColumnValues = Lists.newArrayList(
1224           new TColumnValue(wrap(family), wrap(col), wrap(Bytes.toBytes("val1")))
1225       );
1226       assertArrayEquals(rowkey, tResult.getRow());
1227       List<TColumnValue> returnedColumnValues = tResult.getColumnValues();
1228       assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
1229 
1230       metricsHelper.assertGaugeGt("get_max", 3000L, metrics.getSource());
1231     } finally {
1232       if (table != null) {
1233         try {
1234           table.close();
1235         } catch (IOException ignored) {
1236         }
1237         UTIL.deleteTable(tableName);
1238       }
1239     }
1240   }
1241 
1242 
1243   @Test
1244   public void testAttribute() throws Exception {
1245     byte[] rowName = "testAttribute".getBytes();
1246     byte[] attributeKey = "attribute1".getBytes();
1247     byte[] attributeValue = "value1".getBytes();
1248     Map<ByteBuffer, ByteBuffer> attributes = new HashMap<ByteBuffer, ByteBuffer>();
1249     attributes.put(wrap(attributeKey), wrap(attributeValue));
1250 
1251     TGet tGet = new TGet(wrap(rowName));
1252     tGet.setAttributes(attributes);
1253     Get get = getFromThrift(tGet);
1254     assertArrayEquals(get.getAttribute("attribute1"), attributeValue);
1255 
1256     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
1257     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
1258     TPut tPut = new TPut(wrap(rowName) , columnValues);
1259     tPut.setAttributes(attributes);
1260     Put put = putFromThrift(tPut);
1261     assertArrayEquals(put.getAttribute("attribute1"), attributeValue);
1262 
1263     TScan tScan = new TScan();
1264     tScan.setAttributes(attributes);
1265     Scan scan = scanFromThrift(tScan);
1266     assertArrayEquals(scan.getAttribute("attribute1"), attributeValue);
1267 
1268     List<TColumnIncrement> incrementColumns = new ArrayList<TColumnIncrement>();
1269     incrementColumns.add(new TColumnIncrement(wrap(familyAname), wrap(qualifierAname)));
1270     TIncrement tIncrement = new TIncrement(wrap(rowName), incrementColumns);
1271     tIncrement.setAttributes(attributes);
1272     Increment increment = incrementFromThrift(tIncrement);
1273     assertArrayEquals(increment.getAttribute("attribute1"), attributeValue);
1274 
1275     TDelete tDelete = new TDelete(wrap(rowName));
1276     tDelete.setAttributes(attributes);
1277     Delete delete = deleteFromThrift(tDelete);
1278     assertArrayEquals(delete.getAttribute("attribute1"), attributeValue);
1279   }
1280 
1281   /**
1282    * Put valueA to a row, make sure put has happened, then create a mutation object to put valueB
1283    * and delete ValueA, then check that the row value is only valueB.
1284    *
1285    * @throws Exception
1286    */
1287   @Test
1288   public void testMutateRow() throws Exception {
1289     ThriftHBaseServiceHandler handler = createHandler();
1290     byte[] rowName = "testMutateRow".getBytes();
1291     ByteBuffer table = wrap(tableAname);
1292 
1293     List<TColumnValue> columnValuesA = new ArrayList<TColumnValue>();
1294     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
1295         wrap(valueAname));
1296     columnValuesA.add(columnValueA);
1297     TPut putA = new TPut(wrap(rowName), columnValuesA);
1298     putA.setColumnValues(columnValuesA);
1299 
1300     handler.put(table,putA);
1301 
1302     TGet get = new TGet(wrap(rowName));
1303     TResult result = handler.get(table, get);
1304     assertArrayEquals(rowName, result.getRow());
1305     List<TColumnValue> returnedColumnValues = result.getColumnValues();
1306 
1307     List<TColumnValue> expectedColumnValues = new ArrayList<TColumnValue>();
1308     expectedColumnValues.add(columnValueA);
1309     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
1310 
1311     List<TColumnValue> columnValuesB = new ArrayList<TColumnValue>();
1312     TColumnValue columnValueB = new TColumnValue(wrap(familyAname), wrap(qualifierBname),
1313         wrap(valueBname));
1314     columnValuesB.add(columnValueB);
1315     TPut putB = new TPut(wrap(rowName), columnValuesB);
1316     putB.setColumnValues(columnValuesB);
1317 
1318     TDelete delete = new TDelete(wrap(rowName));
1319     List<TColumn> deleteColumns = new ArrayList<TColumn>();
1320     TColumn deleteColumn = new TColumn(wrap(familyAname));
1321     deleteColumn.setQualifier(qualifierAname);
1322     deleteColumns.add(deleteColumn);
1323     delete.setColumns(deleteColumns);
1324 
1325     List<TMutation> mutations = new ArrayList<TMutation>();
1326     TMutation mutationA = TMutation.put(putB);
1327     mutations.add(mutationA);
1328 
1329     TMutation mutationB = TMutation.deleteSingle(delete);
1330     mutations.add(mutationB);
1331 
1332     TRowMutations tRowMutations = new TRowMutations(wrap(rowName),mutations);
1333     handler.mutateRow(table,tRowMutations);
1334 
1335     result = handler.get(table, get);
1336     assertArrayEquals(rowName, result.getRow());
1337     returnedColumnValues = result.getColumnValues();
1338 
1339     expectedColumnValues = new ArrayList<TColumnValue>();
1340     expectedColumnValues.add(columnValueB);
1341     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
1342   }
1343 
1344   /**
1345    * Create TPut, TDelete , TIncrement objects, set durability then call ThriftUtility
1346    * functions to get Put , Delete and Increment respectively. Use getDurability to make sure
1347    * the returned objects have the appropriate durability setting.
1348    *
1349    * @throws Exception
1350    */
1351   @Test
1352   public void testDurability() throws Exception {
1353     byte[] rowName = "testDurability".getBytes();
1354     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
1355     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
1356 
1357     List<TColumnIncrement> incrementColumns = new ArrayList<TColumnIncrement>();
1358     incrementColumns.add(new TColumnIncrement(wrap(familyAname), wrap(qualifierAname)));
1359 
1360     TDelete tDelete = new TDelete(wrap(rowName));
1361     tDelete.setDurability(TDurability.SKIP_WAL);
1362     Delete delete = deleteFromThrift(tDelete);
1363     assertEquals(delete.getDurability(), Durability.SKIP_WAL);
1364 
1365     tDelete.setDurability(TDurability.ASYNC_WAL);
1366     delete = deleteFromThrift(tDelete);
1367     assertEquals(delete.getDurability(), Durability.ASYNC_WAL);
1368 
1369     tDelete.setDurability(TDurability.SYNC_WAL);
1370     delete = deleteFromThrift(tDelete);
1371     assertEquals(delete.getDurability(), Durability.SYNC_WAL);
1372 
1373     tDelete.setDurability(TDurability.FSYNC_WAL);
1374     delete = deleteFromThrift(tDelete);
1375     assertEquals(delete.getDurability(), Durability.FSYNC_WAL);
1376 
1377     TPut tPut = new TPut(wrap(rowName), columnValues);
1378     tPut.setDurability(TDurability.SKIP_WAL);
1379     Put put = putFromThrift(tPut);
1380     assertEquals(put.getDurability(), Durability.SKIP_WAL);
1381 
1382     tPut.setDurability(TDurability.ASYNC_WAL);
1383     put = putFromThrift(tPut);
1384     assertEquals(put.getDurability(), Durability.ASYNC_WAL);
1385 
1386     tPut.setDurability(TDurability.SYNC_WAL);
1387     put = putFromThrift(tPut);
1388     assertEquals(put.getDurability(), Durability.SYNC_WAL);
1389 
1390     tPut.setDurability(TDurability.FSYNC_WAL);
1391     put = putFromThrift(tPut);
1392     assertEquals(put.getDurability(), Durability.FSYNC_WAL);
1393 
1394     TIncrement tIncrement = new TIncrement(wrap(rowName), incrementColumns);
1395 
1396     tIncrement.setDurability(TDurability.SKIP_WAL);
1397     Increment increment = incrementFromThrift(tIncrement);
1398     assertEquals(increment.getDurability(), Durability.SKIP_WAL);
1399 
1400     tIncrement.setDurability(TDurability.ASYNC_WAL);
1401     increment = incrementFromThrift(tIncrement);
1402     assertEquals(increment.getDurability(), Durability.ASYNC_WAL);
1403 
1404     tIncrement.setDurability(TDurability.SYNC_WAL);
1405     increment = incrementFromThrift(tIncrement);
1406     assertEquals(increment.getDurability(), Durability.SYNC_WAL);
1407 
1408     tIncrement.setDurability(TDurability.FSYNC_WAL);
1409     increment = incrementFromThrift(tIncrement);
1410     assertEquals(increment.getDurability(), Durability.FSYNC_WAL);
1411   }
1412 
1413   @Test
1414   public void testCheckAndMutate() throws Exception {
1415     ThriftHBaseServiceHandler handler = createHandler();
1416     ByteBuffer table = wrap(tableAname);
1417     ByteBuffer row = wrap("row".getBytes());
1418     ByteBuffer family = wrap(familyAname);
1419     ByteBuffer qualifier = wrap(qualifierAname);
1420     ByteBuffer value = wrap(valueAname);
1421 
1422     // Create a mutation to write to 'B', our "mutate" of "checkAndMutate"
1423     List<TColumnValue> columnValuesB = new ArrayList<TColumnValue>();
1424     TColumnValue columnValueB = new TColumnValue(family, wrap(qualifierBname), wrap(valueBname));
1425     columnValuesB.add(columnValueB);
1426     TPut putB = new TPut(row, columnValuesB);
1427     putB.setColumnValues(columnValuesB);
1428 
1429     TRowMutations tRowMutations = new TRowMutations(row,
1430         Arrays.<TMutation> asList(TMutation.put(putB)));
1431 
1432     // Empty table when we begin
1433     TResult result = handler.get(table, new TGet(row));
1434     assertEquals(0, result.getColumnValuesSize());
1435 
1436     // checkAndMutate -- condition should fail because the value doesn't exist.
1437     assertFalse("Expected condition to not pass",
1438         handler.checkAndMutate(table, row, family, qualifier, TCompareOp.EQUAL, value,
1439             tRowMutations));
1440 
1441     List<TColumnValue> columnValuesA = new ArrayList<TColumnValue>();
1442     TColumnValue columnValueA = new TColumnValue(family, qualifier, value);
1443     columnValuesA.add(columnValueA);
1444 
1445     // Put an update 'A'
1446     handler.put(table, new TPut(row, columnValuesA));
1447 
1448     // Verify that the update is there
1449     result = handler.get(table, new TGet(row));
1450     assertEquals(1, result.getColumnValuesSize());
1451     assertTColumnValueEqual(columnValueA, result.getColumnValues().get(0));
1452 
1453     // checkAndMutate -- condition should pass since we added the value
1454     assertTrue("Expected condition to pass",
1455         handler.checkAndMutate(table, row, family, qualifier, TCompareOp.EQUAL, value,
1456             tRowMutations));
1457 
1458     result = handler.get(table, new TGet(row));
1459     assertEquals(2, result.getColumnValuesSize());
1460     assertTColumnValueEqual(columnValueA, result.getColumnValues().get(0));
1461     assertTColumnValueEqual(columnValueB, result.getColumnValues().get(1));
1462   }
1463 
1464   public static class DelayingRegionObserver extends BaseRegionObserver {
1465     private static final Log LOG = LogFactory.getLog(DelayingRegionObserver.class);
1466     // sleep time in msec
1467     private long delayMillis;
1468 
1469     @Override
1470     public void start(CoprocessorEnvironment e) throws IOException {
1471       this.delayMillis = e.getConfiguration()
1472           .getLong("delayingregionobserver.delay", 3000);
1473     }
1474 
1475     @Override
1476     public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get,
1477                          List<Cell> results) throws IOException {
1478       try {
1479         long start = System.currentTimeMillis();
1480         TimeUnit.MILLISECONDS.sleep(delayMillis);
1481         if (LOG.isTraceEnabled()) {
1482           LOG.trace("Slept for " + (System.currentTimeMillis() - start) + " msec");
1483         }
1484       } catch (InterruptedException ie) {
1485         throw new InterruptedIOException("Interrupted while sleeping");
1486       }
1487     }
1488   }
1489 }
1490