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 static org.junit.Assert.assertArrayEquals;
23  import static org.junit.Assert.assertEquals;
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.assertNull;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import java.io.IOException;
30  import java.nio.ByteBuffer;
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.Comparator;
34  import java.util.List;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.apache.hadoop.conf.Configuration;
39  import org.apache.hadoop.hbase.HBaseTestingUtility;
40  import org.apache.hadoop.hbase.HColumnDescriptor;
41  import org.apache.hadoop.hbase.HTableDescriptor;
42  import org.apache.hadoop.hbase.MediumTests;
43  import org.apache.hadoop.hbase.client.HBaseAdmin;
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.util.Bytes;
59  import org.apache.hadoop.metrics.ContextFactory;
60  import org.apache.hadoop.metrics.MetricsContext;
61  import org.apache.hadoop.metrics.MetricsUtil;
62  import org.apache.hadoop.metrics.spi.NoEmitMetricsContext;
63  import org.apache.hadoop.metrics.spi.OutputRecord;
64  import org.apache.thrift.TException;
65  import org.junit.AfterClass;
66  import org.junit.Before;
67  import org.junit.BeforeClass;
68  import org.junit.Test;
69  import org.junit.experimental.categories.Category;
70  
71  /**
72   * Unit testing for ThriftServer.HBaseHandler, a part of the org.apache.hadoop.hbase.thrift2 package.
73   */
74  @Category(MediumTests.class)
75  public class TestThriftHBaseServiceHandler {
76  
77    public static final Log LOG = LogFactory.getLog(TestThriftHBaseServiceHandler.class);
78    private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
79  
80    // Static names for tables, columns, rows, and values
81    private static byte[] tableAname = Bytes.toBytes("tableA");
82    private static byte[] familyAname = Bytes.toBytes("familyA");
83    private static byte[] familyBname = Bytes.toBytes("familyB");
84    private static byte[] qualifierAname = Bytes.toBytes("qualifierA");
85    private static byte[] qualifierBname = Bytes.toBytes("qualifierB");
86    private static byte[] valueAname = Bytes.toBytes("valueA");
87    private static byte[] valueBname = Bytes.toBytes("valueB");
88    private static HColumnDescriptor[] families = new HColumnDescriptor[] {
89        new HColumnDescriptor(familyAname),
90        new HColumnDescriptor(familyBname)
91            .setMaxVersions(2)
92    };
93  
94    public void assertTColumnValuesEqual(List<TColumnValue> columnValuesA, List<TColumnValue> columnValuesB) {
95      assertEquals(columnValuesA.size(), columnValuesB.size());
96      Comparator<TColumnValue> comparator = new Comparator<TColumnValue>() {
97        @Override
98        public int compare(TColumnValue o1, TColumnValue o2) {
99          return Bytes.compareTo(Bytes.add(o1.getFamily(), o1.getQualifier()),
100             Bytes.add(o2.getFamily(), o2.getQualifier()));
101       }
102     };
103     Collections.sort(columnValuesA, comparator);
104     Collections.sort(columnValuesB, comparator);
105 
106     for (int i = 0; i < columnValuesA.size(); i++) {
107       TColumnValue a = columnValuesA.get(i);
108       TColumnValue b = columnValuesB.get(i);
109       assertArrayEquals(a.getFamily(), b.getFamily());
110       assertArrayEquals(a.getQualifier(), b.getQualifier());
111       assertArrayEquals(a.getValue(), b.getValue());
112     }
113   }
114 
115   @BeforeClass
116   public static void beforeClass() throws Exception {
117     UTIL.startMiniCluster();
118     HBaseAdmin admin = new HBaseAdmin(UTIL.getConfiguration());
119     HTableDescriptor tableDescriptor = new HTableDescriptor(tableAname);
120     for (HColumnDescriptor family : families) {
121       tableDescriptor.addFamily(family);
122     }
123     admin.createTable(tableDescriptor);
124   }
125 
126   @AfterClass
127   public static void afterClass() throws Exception {
128     UTIL.shutdownMiniCluster();
129   }
130 
131   @Before
132   public void setup() throws Exception {
133 
134   }
135 
136   private ThriftHBaseServiceHandler createHandler() {
137     return new ThriftHBaseServiceHandler(UTIL.getConfiguration());
138   }
139 
140   @Test
141   public void testExists() throws TIOError, TException {
142     ThriftHBaseServiceHandler handler = createHandler();
143     byte[] rowName = "testExists".getBytes();
144     ByteBuffer table = ByteBuffer.wrap(tableAname);
145 
146     TGet get = new TGet(ByteBuffer.wrap(rowName));
147     assertFalse(handler.exists(table, get));
148 
149     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
150     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname), ByteBuffer
151         .wrap(valueAname)));
152     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyBname), ByteBuffer.wrap(qualifierBname), ByteBuffer
153         .wrap(valueBname)));
154     TPut put = new TPut(ByteBuffer.wrap(rowName), columnValues);
155     put.setColumnValues(columnValues);
156 
157     handler.put(table, put);
158 
159     assertTrue(handler.exists(table, get));
160   }
161 
162   @Test
163   public void testPutGet() throws Exception {
164     ThriftHBaseServiceHandler handler = createHandler();
165     byte[] rowName = "testPutGet".getBytes();
166     ByteBuffer table = ByteBuffer.wrap(tableAname);
167 
168     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
169     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname), ByteBuffer
170         .wrap(valueAname)));
171     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyBname), ByteBuffer.wrap(qualifierBname), ByteBuffer
172         .wrap(valueBname)));
173     TPut put = new TPut(ByteBuffer.wrap(rowName), columnValues);
174 
175     put.setColumnValues(columnValues);
176 
177     handler.put(table, put);
178 
179     TGet get = new TGet(ByteBuffer.wrap(rowName));
180 
181     TResult result = handler.get(table, get);
182     assertArrayEquals(rowName, result.getRow());
183     List<TColumnValue> returnedColumnValues = result.getColumnValues();
184     assertTColumnValuesEqual(columnValues, returnedColumnValues);
185   }
186 
187   @Test
188   public void testPutGetMultiple() throws Exception {
189     ThriftHBaseServiceHandler handler = createHandler();
190     ByteBuffer table = ByteBuffer.wrap(tableAname);
191     byte[] rowName1 = "testPutGetMultiple1".getBytes();
192     byte[] rowName2 = "testPutGetMultiple2".getBytes();
193 
194     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
195     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname), ByteBuffer
196         .wrap(valueAname)));
197     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyBname), ByteBuffer.wrap(qualifierBname), ByteBuffer
198         .wrap(valueBname)));
199     List<TPut> puts = new ArrayList<TPut>();
200     puts.add(new TPut(ByteBuffer.wrap(rowName1), columnValues));
201     puts.add(new TPut(ByteBuffer.wrap(rowName2), columnValues));
202 
203     handler.putMultiple(table, puts);
204 
205     List<TGet> gets = new ArrayList<TGet>();
206     gets.add(new TGet(ByteBuffer.wrap(rowName1)));
207     gets.add(new TGet(ByteBuffer.wrap(rowName2)));
208 
209     List<TResult> results = handler.getMultiple(table, gets);
210     assertEquals(2, results.size());
211 
212     assertArrayEquals(rowName1, results.get(0).getRow());
213     assertTColumnValuesEqual(columnValues, results.get(0).getColumnValues());
214 
215     assertArrayEquals(rowName2, results.get(1).getRow());
216     assertTColumnValuesEqual(columnValues, results.get(1).getColumnValues());
217   }
218 
219   @Test
220   public void testDeleteMultiple() throws Exception {
221     ThriftHBaseServiceHandler handler = createHandler();
222     ByteBuffer table = ByteBuffer.wrap(tableAname);
223     byte[] rowName1 = "testDeleteMultiple1".getBytes();
224     byte[] rowName2 = "testDeleteMultiple2".getBytes();
225 
226     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
227     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname), ByteBuffer
228         .wrap(valueAname)));
229     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyBname), ByteBuffer.wrap(qualifierBname), ByteBuffer
230         .wrap(valueBname)));
231     List<TPut> puts = new ArrayList<TPut>();
232     puts.add(new TPut(ByteBuffer.wrap(rowName1), columnValues));
233     puts.add(new TPut(ByteBuffer.wrap(rowName2), columnValues));
234 
235     handler.putMultiple(table, puts);
236 
237     List<TDelete> deletes = new ArrayList<TDelete>();
238     deletes.add(new TDelete(ByteBuffer.wrap(rowName1)));
239     deletes.add(new TDelete(ByteBuffer.wrap(rowName2)));
240 
241     List<TDelete> deleteResults = handler.deleteMultiple(table, deletes);
242     // 0 means they were all successfully applies
243     assertEquals(0, deleteResults.size());
244 
245     assertFalse(handler.exists(table, new TGet(ByteBuffer.wrap(rowName1))));
246     assertFalse(handler.exists(table, new TGet(ByteBuffer.wrap(rowName2))));
247   }
248 
249   @Test
250   public void testDelete() throws Exception {
251     ThriftHBaseServiceHandler handler = createHandler();
252     byte[] rowName = "testDelete".getBytes();
253     ByteBuffer table = ByteBuffer.wrap(tableAname);
254 
255     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
256     TColumnValue columnValueA = new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname),
257         ByteBuffer.wrap(valueAname));
258     TColumnValue columnValueB = new TColumnValue(ByteBuffer.wrap(familyBname), ByteBuffer.wrap(qualifierBname),
259         ByteBuffer.wrap(valueBname));
260     columnValues.add(columnValueA);
261     columnValues.add(columnValueB);
262     TPut put = new TPut(ByteBuffer.wrap(rowName), columnValues);
263 
264     put.setColumnValues(columnValues);
265 
266     handler.put(table, put);
267 
268     TDelete delete = new TDelete(ByteBuffer.wrap(rowName));
269     List<TColumn> deleteColumns = new ArrayList<TColumn>();
270     TColumn deleteColumn = new TColumn(ByteBuffer.wrap(familyAname));
271     deleteColumn.setQualifier(qualifierAname);
272     deleteColumns.add(deleteColumn);
273     delete.setColumns(deleteColumns);
274 
275     handler.deleteSingle(table, delete);
276 
277     TGet get = new TGet(ByteBuffer.wrap(rowName));
278     TResult result = handler.get(table, get);
279     assertArrayEquals(rowName, result.getRow());
280     List<TColumnValue> returnedColumnValues = result.getColumnValues();
281     List<TColumnValue> expectedColumnValues = new ArrayList<TColumnValue>();
282     expectedColumnValues.add(columnValueB);
283     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
284   }
285 
286   @Test
287   public void testDeleteAllTimestamps() throws Exception {
288     ThriftHBaseServiceHandler handler = createHandler();
289     byte[] rowName = "testDeleteAllTimestamps".getBytes();
290     ByteBuffer table = ByteBuffer.wrap(tableAname);
291 
292     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
293     TColumnValue columnValueA = new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname),
294         ByteBuffer.wrap(valueAname));
295     columnValueA.setTimestamp(System.currentTimeMillis() - 10);
296     columnValues.add(columnValueA);
297     TPut put = new TPut(ByteBuffer.wrap(rowName), columnValues);
298 
299     put.setColumnValues(columnValues);
300 
301     handler.put(table, put);
302     columnValueA.setTimestamp(System.currentTimeMillis());
303     handler.put(table, put);
304 
305     TGet get = new TGet(ByteBuffer.wrap(rowName));
306     get.setMaxVersions(2);
307     TResult result = handler.get(table, get);
308     assertEquals(2, result.getColumnValuesSize());
309 
310     TDelete delete = new TDelete(ByteBuffer.wrap(rowName));
311     List<TColumn> deleteColumns = new ArrayList<TColumn>();
312     TColumn deleteColumn = new TColumn(ByteBuffer.wrap(familyAname));
313     deleteColumn.setQualifier(qualifierAname);
314     deleteColumns.add(deleteColumn);
315     delete.setColumns(deleteColumns);
316     delete.setDeleteType(TDeleteType.DELETE_COLUMNS); // This is the default anyway.
317 
318     handler.deleteSingle(table, delete);
319 
320     get = new TGet(ByteBuffer.wrap(rowName));
321     result = handler.get(table, get);
322     assertNull(result.getRow());
323     assertEquals(0, result.getColumnValuesSize());
324   }
325 
326   @Test
327   public void testDeleteSingleTimestamp() throws Exception {
328     ThriftHBaseServiceHandler handler = createHandler();
329     byte[] rowName = "testDeleteSingleTimestamp".getBytes();
330     ByteBuffer table = ByteBuffer.wrap(tableAname);
331 
332     long timestamp1 = System.currentTimeMillis() - 10;
333     long timestamp2 = System.currentTimeMillis();
334     
335     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
336     TColumnValue columnValueA = new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname),
337         ByteBuffer.wrap(valueAname));
338     columnValueA.setTimestamp(timestamp1);
339     columnValues.add(columnValueA);
340     TPut put = new TPut(ByteBuffer.wrap(rowName), columnValues);
341 
342     put.setColumnValues(columnValues);
343 
344     handler.put(table, put);
345     columnValueA.setTimestamp(timestamp2);
346     handler.put(table, put);
347 
348     TGet get = new TGet(ByteBuffer.wrap(rowName));
349     get.setMaxVersions(2);
350     TResult result = handler.get(table, get);
351     assertEquals(2, result.getColumnValuesSize());
352 
353     TDelete delete = new TDelete(ByteBuffer.wrap(rowName));
354     List<TColumn> deleteColumns = new ArrayList<TColumn>();
355     TColumn deleteColumn = new TColumn(ByteBuffer.wrap(familyAname));
356     deleteColumn.setQualifier(qualifierAname);
357     deleteColumns.add(deleteColumn);
358     delete.setColumns(deleteColumns);
359     delete.setDeleteType(TDeleteType.DELETE_COLUMN);
360 
361     handler.deleteSingle(table, delete);
362 
363     get = new TGet(ByteBuffer.wrap(rowName));
364     result = handler.get(table, get);
365     assertArrayEquals(rowName, result.getRow());
366     assertEquals(1, result.getColumnValuesSize());
367     // the older timestamp should remain.
368     assertEquals(timestamp1, result.getColumnValues().get(0).getTimestamp());
369   }
370 
371   @Test
372   public void testIncrement() throws Exception {
373     ThriftHBaseServiceHandler handler = createHandler();
374     byte[] rowName = "testIncrement".getBytes();
375     ByteBuffer table = ByteBuffer.wrap(tableAname);
376 
377     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
378     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname), ByteBuffer
379         .wrap(Bytes.toBytes(1L))));
380     TPut put = new TPut(ByteBuffer.wrap(rowName), columnValues);
381     put.setColumnValues(columnValues);
382     handler.put(table, put);
383 
384     List<TColumnIncrement> incrementColumns = new ArrayList<TColumnIncrement>();
385     incrementColumns.add(new TColumnIncrement(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname)));
386     TIncrement increment = new TIncrement(ByteBuffer.wrap(rowName), incrementColumns);
387     handler.increment(table, increment);
388 
389     TGet get = new TGet(ByteBuffer.wrap(rowName));
390     TResult result = handler.get(table, get);
391 
392     assertArrayEquals(rowName, result.getRow());
393     assertEquals(1, result.getColumnValuesSize());
394     TColumnValue columnValue = result.getColumnValues().get(0);
395     assertArrayEquals(Bytes.toBytes(2L), columnValue.getValue());
396   }
397 
398   /**
399    * check that checkAndPut fails if the cell does not exist, then put in the cell, then check that the checkAndPut
400    * succeeds.
401    * 
402    * @throws Exception
403    */
404   @Test
405   public void testCheckAndPut() throws Exception {
406     ThriftHBaseServiceHandler handler = createHandler();
407     byte[] rowName = "testCheckAndPut".getBytes();
408     ByteBuffer table = ByteBuffer.wrap(tableAname);
409 
410     List<TColumnValue> columnValuesA = new ArrayList<TColumnValue>();
411     TColumnValue columnValueA = new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname),
412         ByteBuffer.wrap(valueAname));
413     columnValuesA.add(columnValueA);
414     TPut putA = new TPut(ByteBuffer.wrap(rowName), columnValuesA);
415     putA.setColumnValues(columnValuesA);
416 
417     List<TColumnValue> columnValuesB = new ArrayList<TColumnValue>();
418     TColumnValue columnValueB = new TColumnValue(ByteBuffer.wrap(familyBname), ByteBuffer.wrap(qualifierBname),
419         ByteBuffer.wrap(valueBname));
420     columnValuesB.add(columnValueB);
421     TPut putB = new TPut(ByteBuffer.wrap(rowName), columnValuesB);
422     putB.setColumnValues(columnValuesB);
423 
424     assertFalse(handler.checkAndPut(table, ByteBuffer.wrap(rowName), ByteBuffer.wrap(familyAname),
425         ByteBuffer.wrap(qualifierAname), ByteBuffer.wrap(valueAname), putB));
426 
427     TGet get = new TGet(ByteBuffer.wrap(rowName));
428     TResult result = handler.get(table, get);
429     assertEquals(0, result.getColumnValuesSize());
430 
431     handler.put(table, putA);
432 
433     assertTrue(handler.checkAndPut(table, ByteBuffer.wrap(rowName), ByteBuffer.wrap(familyAname),
434         ByteBuffer.wrap(qualifierAname), ByteBuffer.wrap(valueAname), putB));
435 
436     result = handler.get(table, get);
437     assertArrayEquals(rowName, result.getRow());
438     List<TColumnValue> returnedColumnValues = result.getColumnValues();
439     List<TColumnValue> expectedColumnValues = new ArrayList<TColumnValue>();
440     expectedColumnValues.add(columnValueA);
441     expectedColumnValues.add(columnValueB);
442     assertTColumnValuesEqual(expectedColumnValues, returnedColumnValues);
443   }
444 
445   /**
446    * check that checkAndDelete fails if the cell does not exist, then put in the cell, then check that the
447    * checkAndDelete succeeds.
448    * 
449    * @throws Exception
450    */
451   @Test
452   public void testCheckAndDelete() throws Exception {
453     ThriftHBaseServiceHandler handler = createHandler();
454     byte[] rowName = "testCheckAndDelete".getBytes();
455     ByteBuffer table = ByteBuffer.wrap(tableAname);
456 
457     List<TColumnValue> columnValuesA = new ArrayList<TColumnValue>();
458     TColumnValue columnValueA = new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname),
459         ByteBuffer.wrap(valueAname));
460     columnValuesA.add(columnValueA);
461     TPut putA = new TPut(ByteBuffer.wrap(rowName), columnValuesA);
462     putA.setColumnValues(columnValuesA);
463 
464     List<TColumnValue> columnValuesB = new ArrayList<TColumnValue>();
465     TColumnValue columnValueB = new TColumnValue(ByteBuffer.wrap(familyBname), ByteBuffer.wrap(qualifierBname),
466         ByteBuffer.wrap(valueBname));
467     columnValuesB.add(columnValueB);
468     TPut putB = new TPut(ByteBuffer.wrap(rowName), columnValuesB);
469     putB.setColumnValues(columnValuesB);
470 
471     // put putB so that we know whether the row has been deleted or not
472     handler.put(table, putB);
473 
474     TDelete delete = new TDelete(ByteBuffer.wrap(rowName));
475 
476     assertFalse(handler.checkAndDelete(table, ByteBuffer.wrap(rowName), ByteBuffer.wrap(familyAname),
477         ByteBuffer.wrap(qualifierAname), ByteBuffer.wrap(valueAname), delete));
478 
479     TGet get = new TGet(ByteBuffer.wrap(rowName));
480     TResult result = handler.get(table, get);
481     assertArrayEquals(rowName, result.getRow());
482     assertTColumnValuesEqual(columnValuesB, result.getColumnValues());
483 
484     handler.put(table, putA);
485 
486     assertTrue(handler.checkAndDelete(table, ByteBuffer.wrap(rowName), ByteBuffer.wrap(familyAname),
487         ByteBuffer.wrap(qualifierAname), ByteBuffer.wrap(valueAname), delete));
488 
489     result = handler.get(table, get);
490     assertFalse(result.isSetRow());
491     assertEquals(0, result.getColumnValuesSize());
492   }
493 
494   @Test
495   public void testScan() throws Exception {
496     ThriftHBaseServiceHandler handler = createHandler();
497     ByteBuffer table = ByteBuffer.wrap(tableAname);
498 
499     TScan scan = new TScan();
500     List<TColumn> columns = new ArrayList<TColumn>();
501     TColumn column = new TColumn();
502     column.setFamily(familyAname);
503     column.setQualifier(qualifierAname);
504     columns.add(column);
505     scan.setColumns(columns);
506     scan.setStartRow("testScan".getBytes());
507 
508     TColumnValue columnValue = new TColumnValue(ByteBuffer.wrap(familyAname), ByteBuffer.wrap(qualifierAname),
509         ByteBuffer.wrap(valueAname));
510     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
511     columnValues.add(columnValue);
512     for (int i = 0; i < 10; i++) {
513       TPut put = new TPut(ByteBuffer.wrap(("testScan" + i).getBytes()), columnValues);
514       handler.put(table, put);
515     }
516 
517     int scanId = handler.openScanner(table, scan);
518     List<TResult> results = handler.getScannerRows(scanId, 10);
519     assertEquals(10, results.size());
520     for (int i = 0; i < 10; i++) {
521       assertArrayEquals(("testScan" + i).getBytes(), results.get(i).getRow());
522     }
523 
524     results = handler.getScannerRows(scanId, 10);
525     assertEquals(0, results.size());
526 
527     handler.closeScanner(scanId);
528 
529     try {
530       handler.getScannerRows(scanId, 10);
531       fail("Scanner id should be invalid");
532     } catch (TIllegalArgument e) {
533     }
534   }
535 
536   @Test
537   public void testMetrics() throws Exception {
538     Configuration conf = UTIL.getConfiguration();
539     ThriftMetrics metrics = getMetrics(conf);
540     THBaseService.Iface handler =
541         ThriftHBaseServiceHandler.newInstance(conf, metrics);
542     byte[] rowName = "testMetrics".getBytes();
543     ByteBuffer table = ByteBuffer.wrap(tableAname);
544 
545     TGet get = new TGet(ByteBuffer.wrap(rowName));
546     assertFalse(handler.exists(table, get));
547 
548     List<TColumnValue> columnValues = new ArrayList<TColumnValue>();
549     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyAname),
550                                       ByteBuffer.wrap(qualifierAname),
551                                       ByteBuffer.wrap(valueAname)));
552     columnValues.add(new TColumnValue(ByteBuffer.wrap(familyBname),
553                                       ByteBuffer.wrap(qualifierBname),
554                                       ByteBuffer.wrap(valueBname)));
555     TPut put = new TPut(ByteBuffer.wrap(rowName), columnValues);
556     put.setColumnValues(columnValues);
557 
558     handler.put(table, put);
559 
560     assertTrue(handler.exists(table, get));
561     logMetrics(metrics);
562     verifyMetrics(metrics, "put_num_ops", 1);
563     verifyMetrics(metrics, "exists_num_ops", 2);
564   }
565  
566   private static ThriftMetrics getMetrics(Configuration conf) throws Exception {
567     setupMetricsContext();
568     return new ThriftMetrics(Integer.parseInt(ThriftServer.DEFAULT_LISTEN_PORT),
569         conf, THBaseService.Iface.class);
570   }
571  
572   private static void setupMetricsContext() throws IOException {
573     ContextFactory factory = ContextFactory.getFactory();
574     factory.setAttribute(ThriftMetrics.CONTEXT_NAME + ".class",
575         NoEmitMetricsContext.class.getName());
576     MetricsUtil.getContext(ThriftMetrics.CONTEXT_NAME)
577                .createRecord(ThriftMetrics.CONTEXT_NAME).remove();
578   }
579  
580   private static void logMetrics(ThriftMetrics metrics) throws Exception {
581     if (LOG.isDebugEnabled()) {
582       return;
583     }
584     MetricsContext context = MetricsUtil.getContext( 
585         ThriftMetrics.CONTEXT_NAME); 
586     metrics.doUpdates(context); 
587     for (String key : context.getAllRecords().keySet()) {
588       for (OutputRecord record : context.getAllRecords().get(key)) {
589         for (String name : record.getMetricNames()) {
590           LOG.debug("metrics:" + name + " value:" +
591               record.getMetric(name).intValue());
592         }
593       }
594     }
595   }
596 
597   private static void verifyMetrics(ThriftMetrics metrics, String name, int expectValue)
598       throws Exception { 
599     MetricsContext context = MetricsUtil.getContext( 
600         ThriftMetrics.CONTEXT_NAME); 
601     metrics.doUpdates(context); 
602     OutputRecord record = context.getAllRecords().get( 
603         ThriftMetrics.CONTEXT_NAME).iterator().next(); 
604     assertEquals(expectValue, record.getMetric(name).intValue()); 
605   }
606 
607   @org.junit.Rule
608   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
609     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
610 }
611