1   /*
2    * Copyright 2009 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.thrift2;
21  
22  import java.io.IOException;
23  import java.nio.ByteBuffer;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.Comparator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.HashMap;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.hadoop.conf.Configuration;
34  import org.apache.hadoop.hbase.HBaseTestingUtility;
35  import org.apache.hadoop.hbase.HColumnDescriptor;
36  import org.apache.hadoop.hbase.HTableDescriptor;
37  import org.apache.hadoop.hbase.MediumTests;
38  import org.apache.hadoop.hbase.client.HBaseAdmin;
39  import org.apache.hadoop.hbase.client.Get;
40  import org.apache.hadoop.hbase.client.Put;
41  import org.apache.hadoop.hbase.client.Scan;
42  import org.apache.hadoop.hbase.client.Delete;
43  import org.apache.hadoop.hbase.filter.ParseFilter;
44  import org.apache.hadoop.hbase.thrift.ThriftMetrics;
45  import org.apache.hadoop.hbase.thrift2.generated.TColumn;
46  import org.apache.hadoop.hbase.thrift2.generated.TColumnIncrement;
47  import org.apache.hadoop.hbase.thrift2.generated.TColumnValue;
48  import org.apache.hadoop.hbase.thrift2.generated.TDelete;
49  import org.apache.hadoop.hbase.thrift2.generated.TDeleteType;
50  import org.apache.hadoop.hbase.thrift2.generated.TGet;
51  import org.apache.hadoop.hbase.thrift2.generated.THBaseService;
52  import org.apache.hadoop.hbase.thrift2.generated.TIOError;
53  import org.apache.hadoop.hbase.thrift2.generated.TIllegalArgument;
54  import org.apache.hadoop.hbase.thrift2.generated.TIncrement;
55  import org.apache.hadoop.hbase.thrift2.generated.TPut;
56  import org.apache.hadoop.hbase.thrift2.generated.TResult;
57  import org.apache.hadoop.hbase.thrift2.generated.TScan;
58  import org.apache.hadoop.hbase.thrift2.generated.TMutation;
59  import org.apache.hadoop.hbase.thrift2.generated.TRowMutations;
60  import org.apache.hadoop.hbase.util.Bytes;
61  import org.apache.hadoop.metrics.ContextFactory;
62  import org.apache.hadoop.metrics.MetricsContext;
63  import org.apache.hadoop.metrics.MetricsUtil;
64  import org.apache.hadoop.metrics.spi.NoEmitMetricsContext;
65  import org.apache.hadoop.metrics.spi.OutputRecord;
66  import org.apache.thrift.TException;
67  import org.junit.AfterClass;
68  import org.junit.Before;
69  import org.junit.BeforeClass;
70  import org.junit.Test;
71  import org.junit.experimental.categories.Category;
72  
73  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.getFromThrift;
74  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.putFromThrift;
75  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.scanFromThrift;
76  import static org.apache.hadoop.hbase.thrift2.ThriftUtilities.deleteFromThrift;
77  import static org.junit.Assert.*;
78  import static java.nio.ByteBuffer.wrap;
79  
80  /**
81   * Unit testing for ThriftServer.HBaseHandler, a part of the org.apache.hadoop.hbase.thrift2 
82   * package.
83   */
84  @Category(MediumTests.class)
85  public class TestThriftHBaseServiceHandler {
86  
87    public static final Log LOG = LogFactory.getLog(TestThriftHBaseServiceHandler.class);
88    private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
89  
90    // Static names for tables, columns, rows, and values
91    private static byte[] tableAname = Bytes.toBytes("tableA");
92    private static byte[] familyAname = Bytes.toBytes("familyA");
93    private static byte[] familyBname = Bytes.toBytes("familyB");
94    private static byte[] qualifierAname = Bytes.toBytes("qualifierA");
95    private static byte[] qualifierBname = Bytes.toBytes("qualifierB");
96    private static byte[] valueAname = Bytes.toBytes("valueA");
97    private static byte[] valueBname = Bytes.toBytes("valueB");
98    private static HColumnDescriptor[] families = new HColumnDescriptor[] {
99        new HColumnDescriptor(familyAname),
100       new HColumnDescriptor(familyBname)
101           .setMaxVersions(2)
102   };
103 
104   public void assertTColumnValuesEqual(List<TColumnValue> columnValuesA,
105       List<TColumnValue> columnValuesB) {
106     assertEquals(columnValuesA.size(), columnValuesB.size());
107     Comparator<TColumnValue> comparator = new Comparator<TColumnValue>() {
108       @Override
109       public int compare(TColumnValue o1, TColumnValue o2) {
110         return Bytes.compareTo(Bytes.add(o1.getFamily(), o1.getQualifier()),
111             Bytes.add(o2.getFamily(), o2.getQualifier()));
112       }
113     };
114     Collections.sort(columnValuesA, comparator);
115     Collections.sort(columnValuesB, comparator);
116 
117     for (int i = 0; i < columnValuesA.size(); i++) {
118       TColumnValue a = columnValuesA.get(i);
119       TColumnValue b = columnValuesB.get(i);
120       assertArrayEquals(a.getFamily(), b.getFamily());
121       assertArrayEquals(a.getQualifier(), b.getQualifier());
122       assertArrayEquals(a.getValue(), b.getValue());
123     }
124   }
125 
126   @BeforeClass
127   public static void beforeClass() throws Exception {
128     UTIL.startMiniCluster();
129     HBaseAdmin admin = new HBaseAdmin(UTIL.getConfiguration());
130     HTableDescriptor tableDescriptor = new HTableDescriptor(tableAname);
131     for (HColumnDescriptor family : families) {
132       tableDescriptor.addFamily(family);
133     }
134     admin.createTable(tableDescriptor);
135   }
136 
137   @AfterClass
138   public static void afterClass() throws Exception {
139     UTIL.shutdownMiniCluster();
140   }
141 
142   @Before
143   public void setup() throws Exception {
144 
145   }
146 
147   private ThriftHBaseServiceHandler createHandler() {
148     return new ThriftHBaseServiceHandler(UTIL.getConfiguration());
149   }
150 
151   @Test
152   public void testExists() throws TIOError, TException {
153     ThriftHBaseServiceHandler handler = createHandler();
154     byte[] rowName = "testExists".getBytes();
155     ByteBuffer table = wrap(tableAname);
156 
157     TGet get = new TGet(wrap(rowName));
158     assertFalse(handler.exists(table, get));
159 
160     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
161     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
162     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
163     TPut put = new TPut(wrap(rowName), columnValues);
164     put.setColumnValues(columnValues);
165 
166     handler.put(table, put);
167 
168     assertTrue(handler.exists(table, get));
169   }
170 
171   @Test
172   public void testPutGet() throws Exception {
173     ThriftHBaseServiceHandler handler = createHandler();
174     byte[] rowName = "testPutGet".getBytes();
175     ByteBuffer table = wrap(tableAname);
176 
177     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
178     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
179     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
180     TPut put = new TPut(wrap(rowName), columnValues);
181 
182     put.setColumnValues(columnValues);
183 
184     handler.put(table, put);
185 
186     TGet get = new TGet(wrap(rowName));
187 
188     TResult result = handler.get(table, get);
189     assertArrayEquals(rowName, result.getRow());
190     List<TColumnValue> returnedColumnValues = result.getColumnValues();
191     assertTColumnValuesEqual(columnValues, returnedColumnValues);
192   }
193 
194   @Test
195   public void testPutGetMultiple() throws Exception {
196     ThriftHBaseServiceHandler handler = createHandler();
197     ByteBuffer table = wrap(tableAname);
198     byte[] rowName1 = "testPutGetMultiple1".getBytes();
199     byte[] rowName2 = "testPutGetMultiple2".getBytes();
200 
201     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
202     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
203     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
204     List<TPut> puts = new ArrayList<TPut>();
205     puts.add(new TPut(wrap(rowName1), columnValues));
206     puts.add(new TPut(wrap(rowName2), columnValues));
207 
208     handler.putMultiple(table, puts);
209 
210     List<TGet> gets = new ArrayList<TGet>();
211     gets.add(new TGet(wrap(rowName1)));
212     gets.add(new TGet(wrap(rowName2)));
213 
214     List<TResult> results = handler.getMultiple(table, gets);
215     assertEquals(2, results.size());
216 
217     assertArrayEquals(rowName1, results.get(0).getRow());
218     assertTColumnValuesEqual(columnValues, results.get(0).getColumnValues());
219 
220     assertArrayEquals(rowName2, results.get(1).getRow());
221     assertTColumnValuesEqual(columnValues, results.get(1).getColumnValues());
222   }
223 
224   @Test
225   public void testDeleteMultiple() throws Exception {
226     ThriftHBaseServiceHandler handler = createHandler();
227     ByteBuffer table = wrap(tableAname);
228     byte[] rowName1 = "testDeleteMultiple1".getBytes();
229     byte[] rowName2 = "testDeleteMultiple2".getBytes();
230 
231     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
232     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
233     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname)));
234     List<TPut> puts = new ArrayList<TPut>();
235     puts.add(new TPut(wrap(rowName1), columnValues));
236     puts.add(new TPut(wrap(rowName2), columnValues));
237 
238     handler.putMultiple(table, puts);
239 
240     List<TDelete> deletes = new ArrayList<TDelete>();
241     deletes.add(new TDelete(wrap(rowName1)));
242     deletes.add(new TDelete(wrap(rowName2)));
243 
244     List<TDelete> deleteResults = handler.deleteMultiple(table, deletes);
245     // 0 means they were all successfully applies
246     assertEquals(0, deleteResults.size());
247 
248     assertFalse(handler.exists(table, new TGet(wrap(rowName1))));
249     assertFalse(handler.exists(table, new TGet(wrap(rowName2))));
250   }
251 
252   @Test
253   public void testDelete() throws Exception {
254     ThriftHBaseServiceHandler handler = createHandler();
255     byte[] rowName = "testDelete".getBytes();
256     ByteBuffer table = wrap(tableAname);
257 
258     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
259     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
260       wrap(valueAname));
261     TColumnValue columnValueB = new TColumnValue(wrap(familyBname), wrap(qualifierBname),
262       wrap(valueBname));
263     columnValues.add(columnValueA);
264     columnValues.add(columnValueB);
265     TPut put = new TPut(wrap(rowName), columnValues);
266 
267     put.setColumnValues(columnValues);
268 
269     handler.put(table, put);
270 
271     TDelete delete = new TDelete(wrap(rowName));
272     List<TColumn> deleteColumns = new ArrayList<TColumn>();
273     TColumn deleteColumn = new TColumn(wrap(familyAname));
274     deleteColumn.setQualifier(qualifierAname);
275     deleteColumns.add(deleteColumn);
276     delete.setColumns(deleteColumns);
277 
278     handler.deleteSingle(table, delete);
279 
280     TGet get = new TGet(wrap(rowName));
281     TResult result = handler.get(table, get);
282     assertArrayEquals(rowName, result.getRow());
283     List<TColumnValue> returnedColumnValues = result.getColumnValues();
284     List<TColumnValue> expectedColumnValues = new ArrayList<TColumnValue>();
285     expectedColumnValues.add(columnValueB);
286     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
287   }
288 
289   @Test
290   public void testDeleteAllTimestamps() throws Exception {
291     ThriftHBaseServiceHandler handler = createHandler();
292     byte[] rowName = "testDeleteAllTimestamps".getBytes();
293     ByteBuffer table = wrap(tableAname);
294 
295     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
296     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
297       wrap(valueAname));
298     columnValueA.setTimestamp(System.currentTimeMillis() - 10);
299     columnValues.add(columnValueA);
300     TPut put = new TPut(wrap(rowName), columnValues);
301 
302     put.setColumnValues(columnValues);
303 
304     handler.put(table, put);
305     columnValueA.setTimestamp(System.currentTimeMillis());
306     handler.put(table, put);
307 
308     TGet get = new TGet(wrap(rowName));
309     get.setMaxVersions(2);
310     TResult result = handler.get(table, get);
311     assertEquals(2, result.getColumnValuesSize());
312 
313     TDelete delete = new TDelete(wrap(rowName));
314     List<TColumn> deleteColumns = new ArrayList<TColumn>();
315     TColumn deleteColumn = new TColumn(wrap(familyAname));
316     deleteColumn.setQualifier(qualifierAname);
317     deleteColumns.add(deleteColumn);
318     delete.setColumns(deleteColumns);
319     delete.setDeleteType(TDeleteType.DELETE_COLUMNS); // This is the default anyway.
320 
321     handler.deleteSingle(table, delete);
322 
323     get = new TGet(wrap(rowName));
324     result = handler.get(table, get);
325     assertNull(result.getRow());
326     assertEquals(0, result.getColumnValuesSize());
327   }
328 
329   @Test
330   public void testDeleteSingleTimestamp() throws Exception {
331     ThriftHBaseServiceHandler handler = createHandler();
332     byte[] rowName = "testDeleteSingleTimestamp".getBytes();
333     ByteBuffer table = wrap(tableAname);
334 
335     long timestamp1 = System.currentTimeMillis() - 10;
336     long timestamp2 = System.currentTimeMillis();
337     
338     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
339     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
340       wrap(valueAname));
341     columnValueA.setTimestamp(timestamp1);
342     columnValues.add(columnValueA);
343     TPut put = new TPut(wrap(rowName), columnValues);
344 
345     put.setColumnValues(columnValues);
346 
347     handler.put(table, put);
348     columnValueA.setTimestamp(timestamp2);
349     handler.put(table, put);
350 
351     TGet get = new TGet(wrap(rowName));
352     get.setMaxVersions(2);
353     TResult result = handler.get(table, get);
354     assertEquals(2, result.getColumnValuesSize());
355 
356     TDelete delete = new TDelete(wrap(rowName));
357     List<TColumn> deleteColumns = new ArrayList<TColumn>();
358     TColumn deleteColumn = new TColumn(wrap(familyAname));
359     deleteColumn.setQualifier(qualifierAname);
360     deleteColumns.add(deleteColumn);
361     delete.setColumns(deleteColumns);
362     delete.setDeleteType(TDeleteType.DELETE_COLUMN);
363 
364     handler.deleteSingle(table, delete);
365 
366     get = new TGet(wrap(rowName));
367     result = handler.get(table, get);
368     assertArrayEquals(rowName, result.getRow());
369     assertEquals(1, result.getColumnValuesSize());
370     // the older timestamp should remain.
371     assertEquals(timestamp1, result.getColumnValues().get(0).getTimestamp());
372   }
373 
374   @Test
375   public void testIncrement() throws Exception {
376     ThriftHBaseServiceHandler handler = createHandler();
377     byte[] rowName = "testIncrement".getBytes();
378     ByteBuffer table = wrap(tableAname);
379 
380     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
381     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname),
382       wrap(Bytes.toBytes(1L))));
383     TPut put = new TPut(wrap(rowName), columnValues);
384     put.setColumnValues(columnValues);
385     handler.put(table, put);
386 
387     List<TColumnIncrement> incrementColumns = new ArrayList<TColumnIncrement>();
388     incrementColumns.add(new TColumnIncrement(wrap(familyAname), wrap(qualifierAname)));
389     TIncrement increment = new TIncrement(wrap(rowName), incrementColumns);
390     handler.increment(table, increment);
391 
392     TGet get = new TGet(wrap(rowName));
393     TResult result = handler.get(table, get);
394 
395     assertArrayEquals(rowName, result.getRow());
396     assertEquals(1, result.getColumnValuesSize());
397     TColumnValue columnValue = result.getColumnValues().get(0);
398     assertArrayEquals(Bytes.toBytes(2L), columnValue.getValue());
399   }
400 
401   /**
402    * check that checkAndPut fails if the cell does not exist, then put in the cell, then check
403    * that the checkAndPut succeeds.
404    * 
405    * @throws Exception
406    */
407   @Test
408   public void testCheckAndPut() throws Exception {
409     ThriftHBaseServiceHandler handler = createHandler();
410     byte[] rowName = "testCheckAndPut".getBytes();
411     ByteBuffer table = wrap(tableAname);
412 
413     List<TColumnValue> columnValuesA = new ArrayList<TColumnValue>();
414     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
415       wrap(valueAname));
416     columnValuesA.add(columnValueA);
417     TPut putA = new TPut(wrap(rowName), columnValuesA);
418     putA.setColumnValues(columnValuesA);
419 
420     List<TColumnValue> columnValuesB = new ArrayList<TColumnValue>();
421     TColumnValue columnValueB = new TColumnValue(wrap(familyBname), wrap(qualifierBname),
422       wrap(valueBname));
423     columnValuesB.add(columnValueB);
424     TPut putB = new TPut(wrap(rowName), columnValuesB);
425     putB.setColumnValues(columnValuesB);
426 
427     assertFalse(handler.checkAndPut(table, wrap(rowName), wrap(familyAname),
428       wrap(qualifierAname), wrap(valueAname), putB));
429 
430     TGet get = new TGet(wrap(rowName));
431     TResult result = handler.get(table, get);
432     assertEquals(0, result.getColumnValuesSize());
433 
434     handler.put(table, putA);
435 
436     assertTrue(handler.checkAndPut(table, wrap(rowName), wrap(familyAname),
437       wrap(qualifierAname), wrap(valueAname), putB));
438 
439     result = handler.get(table, get);
440     assertArrayEquals(rowName, result.getRow());
441     List<TColumnValue> returnedColumnValues = result.getColumnValues();
442     List<TColumnValue> expectedColumnValues = new ArrayList<TColumnValue>();
443     expectedColumnValues.add(columnValueA);
444     expectedColumnValues.add(columnValueB);
445     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
446   }
447 
448   /**
449    * check that checkAndDelete fails if the cell does not exist, then put in the cell, then
450    * check that the checkAndDelete succeeds.
451    * 
452    * @throws Exception
453    */
454   @Test
455   public void testCheckAndDelete() throws Exception {
456     ThriftHBaseServiceHandler handler = createHandler();
457     byte[] rowName = "testCheckAndDelete".getBytes();
458     ByteBuffer table = wrap(tableAname);
459 
460     List<TColumnValue> columnValuesA = new ArrayList<TColumnValue>();
461     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
462       wrap(valueAname));
463     columnValuesA.add(columnValueA);
464     TPut putA = new TPut(wrap(rowName), columnValuesA);
465     putA.setColumnValues(columnValuesA);
466 
467     List<TColumnValue> columnValuesB = new ArrayList<TColumnValue>();
468     TColumnValue columnValueB = new TColumnValue(wrap(familyBname), wrap(qualifierBname),
469       wrap(valueBname));
470     columnValuesB.add(columnValueB);
471     TPut putB = new TPut(wrap(rowName), columnValuesB);
472     putB.setColumnValues(columnValuesB);
473 
474     // put putB so that we know whether the row has been deleted or not
475     handler.put(table, putB);
476 
477     TDelete delete = new TDelete(wrap(rowName));
478 
479     assertFalse(handler.checkAndDelete(table, wrap(rowName), wrap(familyAname),
480       wrap(qualifierAname), wrap(valueAname), delete));
481 
482     TGet get = new TGet(wrap(rowName));
483     TResult result = handler.get(table, get);
484     assertArrayEquals(rowName, result.getRow());
485     assertTColumnValuesEqual(columnValuesB, result.getColumnValues());
486 
487     handler.put(table, putA);
488 
489     assertTrue(handler.checkAndDelete(table, wrap(rowName), wrap(familyAname),
490       wrap(qualifierAname), wrap(valueAname), delete));
491 
492     result = handler.get(table, get);
493     assertFalse(result.isSetRow());
494     assertEquals(0, result.getColumnValuesSize());
495   }
496 
497   @Test
498   public void testScan() throws Exception {
499     ThriftHBaseServiceHandler handler = createHandler();
500     ByteBuffer table = wrap(tableAname);
501 
502     // insert data
503     TColumnValue columnValue = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
504       wrap(valueAname));
505     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
506     columnValues.add(columnValue);
507     for (int i = 0; i < 10; i++) {
508       TPut put = new TPut(wrap(("testScan" + i).getBytes()), columnValues);
509       handler.put(table, put);
510     }
511 
512     // create scan instance
513     TScan scan = new TScan();
514     List<TColumn> columns = new ArrayList<TColumn>();
515     TColumn column = new TColumn();
516     column.setFamily(familyAname);
517     column.setQualifier(qualifierAname);
518     columns.add(column);
519     scan.setColumns(columns);
520     scan.setStartRow("testScan".getBytes());
521     scan.setStopRow("testScan\uffff".getBytes());
522 
523     // get scanner and rows
524     int scanId = handler.openScanner(table, scan);
525     List<TResult> results = handler.getScannerRows(scanId, 10);
526     assertEquals(10, results.size());
527     for (int i = 0; i < 10; i++) {
528       // check if the rows are returned and in order
529       assertArrayEquals(("testScan" + i).getBytes(), results.get(i).getRow());
530     }
531 
532     // check that we are at the end of the scan
533     results = handler.getScannerRows(scanId, 10);
534     assertEquals(0, results.size());
535 
536     // close scanner and check that it was indeed closed
537     handler.closeScanner(scanId);
538     try {
539       handler.getScannerRows(scanId, 10);
540       fail("Scanner id should be invalid");
541     } catch (TIllegalArgument e) {
542     }
543   }
544 
545   @Test
546   public void testScanWithFilter() throws Exception {
547     ThriftHBaseServiceHandler handler = createHandler();
548     ByteBuffer table = wrap(tableAname);
549 
550     // insert data
551     TColumnValue columnValue = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
552       wrap(valueAname));
553     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
554     columnValues.add(columnValue);
555     for (int i = 0; i < 10; i++) {
556       TPut put = new TPut(wrap(("testScanWithFilter" + i).getBytes()), columnValues);
557       handler.put(table, put);
558     }
559 
560     // create scan instance with filter
561     TScan scan = new TScan();
562     List<TColumn> columns = new ArrayList<TColumn>();
563     TColumn column = new TColumn();
564     column.setFamily(familyAname);
565     column.setQualifier(qualifierAname);
566     columns.add(column);
567     scan.setColumns(columns);
568     scan.setStartRow("testScanWithFilter".getBytes());
569     scan.setStopRow("testScanWithFilter\uffff".getBytes());
570     // only get the key part
571     scan.setFilterString(wrap(("KeyOnlyFilter()").getBytes()));
572 
573     // get scanner and rows
574     int scanId = handler.openScanner(table, scan);
575     List<TResult> results = handler.getScannerRows(scanId, 10);
576     assertEquals(10, results.size());
577     for (int i = 0; i < 10; i++) {
578       // check if the rows are returned and in order
579       assertArrayEquals(("testScanWithFilter" + i).getBytes(), results.get(i).getRow());
580       // check that the value is indeed stripped by the filter
581       assertEquals(0, results.get(i).getColumnValues().get(0).getValue().length);
582     }
583 
584     // check that we are at the end of the scan
585     results = handler.getScannerRows(scanId, 10);
586     assertEquals(0, results.size());
587 
588     // close scanner and check that it was indeed closed
589     handler.closeScanner(scanId);
590     try {
591       handler.getScannerRows(scanId, 10);
592       fail("Scanner id should be invalid");
593     } catch (TIllegalArgument e) {
594     }
595   }
596 
597   /**
598    * Padding numbers to make comparison of sort order easier in a for loop
599    *
600    * @param n  The number to pad.
601    * @param pad  The length to pad up to.
602    * @return The padded number as a string.
603    */
604   private String pad(int n, byte pad) {
605     String res = Integer.toString(n);
606     while (res.length() < pad) res = "0" + res;
607     return res;
608   }
609 
610   @Test
611   public void testScanWithBatchSize() throws Exception {
612     ThriftHBaseServiceHandler handler = createHandler();
613     ByteBuffer table = wrap(tableAname);
614 
615     // insert data
616     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
617     for (int i = 0; i < 100; i++) {
618       String colNum = pad(i, (byte) 3);
619       TColumnValue columnValue = new TColumnValue(wrap(familyAname),
620         wrap(("col" + colNum).getBytes()), wrap(("val" + colNum).getBytes()));
621       columnValues.add(columnValue);
622     }
623     TPut put = new TPut(wrap(("testScanWithBatchSize").getBytes()), columnValues);
624     handler.put(table, put);
625 
626     // create scan instance
627     TScan scan = new TScan();
628     List<TColumn> columns = new ArrayList<TColumn>();
629     TColumn column = new TColumn();
630     column.setFamily(familyAname);
631     columns.add(column);
632     scan.setColumns(columns);
633     scan.setStartRow("testScanWithBatchSize".getBytes());
634     scan.setStopRow("testScanWithBatchSize\uffff".getBytes());
635     // set batch size to 10 columns per call
636     scan.setBatchSize(10);
637 
638     // get scanner
639     int scanId = handler.openScanner(table, scan);
640     List<TResult> results = null;
641     for (int i = 0; i < 10; i++) {
642       // get batch for single row (10x10 is what we expect)
643       results = handler.getScannerRows(scanId, 1);
644       assertEquals(1, results.size());
645       // check length of batch
646       List<TColumnValue> cols = results.get(0).getColumnValues();
647       assertEquals(10, cols.size());
648       // check if the columns are returned and in order
649       for (int y = 0; y < 10; y++) {
650         int colNum = y + (10 * i);
651         String colNumPad = pad(colNum, (byte) 3);
652         assertArrayEquals(("col" + colNumPad).getBytes(), cols.get(y).getQualifier());
653       }
654     }
655 
656     // check that we are at the end of the scan
657     results = handler.getScannerRows(scanId, 1);
658     assertEquals(0, results.size());
659 
660     // close scanner and check that it was indeed closed
661     handler.closeScanner(scanId);
662     try {
663       handler.getScannerRows(scanId, 1);
664       fail("Scanner id should be invalid");
665     } catch (TIllegalArgument e) {
666     }
667   }
668 
669   @Test
670   public void testFilterRegistration() throws Exception {
671     Configuration conf = UTIL.getConfiguration();
672     conf.set("hbase.thrift.filters", "MyFilter:filterclass");
673     ThriftServer.registerFilters(conf);
674     Map<String, String> registeredFilters = ParseFilter.getAllFilters();
675     assertEquals("filterclass", registeredFilters.get("MyFilter"));
676   }
677 
678   @Test
679   public void testMetrics() throws Exception {
680     Configuration conf = UTIL.getConfiguration();
681     ThriftMetrics metrics = getMetrics(conf);
682     THBaseService.Iface handler =
683         ThriftHBaseServiceHandler.newInstance(conf, metrics);
684     byte[] rowName = "testMetrics".getBytes();
685     ByteBuffer table = wrap(tableAname);
686 
687     TGet get = new TGet(wrap(rowName));
688     assertFalse(handler.exists(table, get));
689 
690     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
691     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
692     columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname),  wrap(valueBname)));
693     TPut put = new TPut(wrap(rowName), columnValues);
694     put.setColumnValues(columnValues);
695 
696     handler.put(table, put);
697 
698     assertTrue(handler.exists(table, get));
699     logMetrics(metrics);
700     verifyMetrics(metrics, "put_num_ops", 1);
701     verifyMetrics(metrics, "exists_num_ops", 2);
702   }
703 
704   @Test
705   public void testAttribute() throws Exception {
706     byte[] rowName = "testAttribute".getBytes();
707     byte[] attributeKey = "attribute1".getBytes();
708     byte[] attributeValue = "value1".getBytes();
709     Map<ByteBuffer, ByteBuffer> attributes = new HashMap<ByteBuffer, ByteBuffer>();
710     attributes.put(wrap(attributeKey), wrap(attributeValue));
711 
712     TGet tGet = new TGet(wrap(rowName));
713     tGet.setAttributes(attributes);
714     Get get = getFromThrift(tGet);
715     assertArrayEquals(get.getAttribute("attribute1"), attributeValue);
716 
717     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
718     columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)));
719     TPut tPut = new TPut(wrap(rowName) , columnValues);
720     tPut.setAttributes(attributes);
721     Put put = putFromThrift(tPut);
722     assertArrayEquals(put.getAttribute("attribute1"), attributeValue);
723 
724     TScan tScan = new TScan();
725     tScan.setAttributes(attributes);
726     Scan scan = scanFromThrift(tScan);
727     assertArrayEquals(scan.getAttribute("attribute1"), attributeValue);
728 
729     TDelete tDelete = new TDelete(wrap(rowName));
730     tDelete.setAttributes(attributes);
731     Delete delete = deleteFromThrift(tDelete);
732     assertArrayEquals(delete.getAttribute("attribute1"), attributeValue);
733   }
734 
735   /**
736    * Put valueA to a row, make sure put has happened, then create a mutation object to put valueB
737    * and delete ValueA, then check that the row value is only valueB.
738    *
739    * @throws Exception
740    */
741   @Test
742   public void testMutateRow() throws Exception {
743     ThriftHBaseServiceHandler handler = createHandler();
744     byte[] rowName = "testMutateRow".getBytes();
745     ByteBuffer table = wrap(tableAname);
746 
747     List<TColumnValue> columnValuesA = new ArrayList<TColumnValue>();
748     TColumnValue columnValueA = new TColumnValue(wrap(familyAname), wrap(qualifierAname),
749         wrap(valueAname));
750     columnValuesA.add(columnValueA);
751     TPut putA = new TPut(wrap(rowName), columnValuesA);
752     putA.setColumnValues(columnValuesA);
753 
754     handler.put(table,putA);
755 
756     TGet get = new TGet(wrap(rowName));
757     TResult result = handler.get(table, get);
758     assertArrayEquals(rowName, result.getRow());
759     List<TColumnValue> returnedColumnValues = result.getColumnValues();
760 
761     List<TColumnValue> expectedColumnValues = new ArrayList<TColumnValue>();
762     expectedColumnValues.add(columnValueA);
763     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
764 
765     List<TColumnValue> columnValuesB = new ArrayList<TColumnValue>();
766     TColumnValue columnValueB = new TColumnValue(wrap(familyAname), wrap(qualifierBname),
767         wrap(valueBname));
768     columnValuesB.add(columnValueB);
769     TPut putB = new TPut(wrap(rowName), columnValuesB);
770     putB.setColumnValues(columnValuesB);
771 
772     TDelete delete = new TDelete(wrap(rowName));
773     List<TColumn> deleteColumns = new ArrayList<TColumn>();
774     TColumn deleteColumn = new TColumn(wrap(familyAname));
775     deleteColumn.setQualifier(qualifierAname);
776     deleteColumns.add(deleteColumn);
777     delete.setColumns(deleteColumns);
778 
779     List<TMutation> mutations = new ArrayList<TMutation>();
780     TMutation mutationA = TMutation.put(putB);
781     mutations.add(mutationA);
782 
783     TMutation mutationB = TMutation.deleteSingle(delete);
784     mutations.add(mutationB);
785 
786     TRowMutations tRowMutations = new TRowMutations(wrap(rowName),mutations);
787     handler.mutateRow(table,tRowMutations);
788 
789     result = handler.get(table, get);
790     assertArrayEquals(rowName, result.getRow());
791     returnedColumnValues = result.getColumnValues();
792 
793     expectedColumnValues = new ArrayList<TColumnValue>();
794     expectedColumnValues.add(columnValueB);
795     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
796   }
797 
798   private static ThriftMetrics getMetrics(Configuration conf) throws Exception {
799     setupMetricsContext();
800     return new ThriftMetrics(Integer.parseInt(ThriftServer.DEFAULT_LISTEN_PORT),
801         conf, THBaseService.Iface.class);
802   }
803  
804   private static void setupMetricsContext() throws IOException {
805     ContextFactory factory = ContextFactory.getFactory();
806     factory.setAttribute(ThriftMetrics.CONTEXT_NAME + ".class",
807         NoEmitMetricsContext.class.getName());
808     MetricsUtil.getContext(ThriftMetrics.CONTEXT_NAME)
809                .createRecord(ThriftMetrics.CONTEXT_NAME).remove();
810   }
811  
812   private static void logMetrics(ThriftMetrics metrics) throws Exception {
813     if (LOG.isDebugEnabled()) {
814       return;
815     }
816     MetricsContext context = MetricsUtil.getContext( 
817         ThriftMetrics.CONTEXT_NAME); 
818     metrics.doUpdates(context); 
819     for (String key : context.getAllRecords().keySet()) {
820       for (OutputRecord record : context.getAllRecords().get(key)) {
821         for (String name : record.getMetricNames()) {
822           LOG.debug("metrics:" + name + " value:" +
823               record.getMetric(name).intValue());
824         }
825       }
826     }
827   }
828 
829   private static void verifyMetrics(ThriftMetrics metrics, String name, int expectValue)
830       throws Exception { 
831     MetricsContext context = MetricsUtil.getContext( 
832         ThriftMetrics.CONTEXT_NAME); 
833     metrics.doUpdates(context); 
834     OutputRecord record = context.getAllRecords().get( 
835         ThriftMetrics.CONTEXT_NAME).iterator().next(); 
836     assertEquals(expectValue, record.getMetric(name).intValue()); 
837   }
838 
839   @org.junit.Rule
840   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
841     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
842 }
843