001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.rest.client;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNotNull;
023import static org.junit.Assert.assertNull;
024import static org.junit.Assert.assertTrue;
025
026import java.io.IOException;
027import java.util.ArrayList;
028import java.util.Collections;
029import java.util.Iterator;
030import java.util.List;
031import org.apache.hadoop.hbase.Cell;
032import org.apache.hadoop.hbase.CellUtil;
033import org.apache.hadoop.hbase.HBaseClassTestRule;
034import org.apache.hadoop.hbase.HBaseTestingUtil;
035import org.apache.hadoop.hbase.TableName;
036import org.apache.hadoop.hbase.client.Admin;
037import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
038import org.apache.hadoop.hbase.client.Delete;
039import org.apache.hadoop.hbase.client.Get;
040import org.apache.hadoop.hbase.client.Put;
041import org.apache.hadoop.hbase.client.Result;
042import org.apache.hadoop.hbase.client.ResultScanner;
043import org.apache.hadoop.hbase.client.Scan;
044import org.apache.hadoop.hbase.client.Table;
045import org.apache.hadoop.hbase.client.TableDescriptor;
046import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
047import org.apache.hadoop.hbase.rest.HBaseRESTTestingUtility;
048import org.apache.hadoop.hbase.rest.RESTServlet;
049import org.apache.hadoop.hbase.testclassification.MediumTests;
050import org.apache.hadoop.hbase.testclassification.RestTests;
051import org.apache.hadoop.hbase.util.Bytes;
052import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
053import org.apache.http.Header;
054import org.apache.http.message.BasicHeader;
055import org.junit.After;
056import org.junit.AfterClass;
057import org.junit.Before;
058import org.junit.BeforeClass;
059import org.junit.ClassRule;
060import org.junit.Test;
061import org.junit.experimental.categories.Category;
062
063@Category({ RestTests.class, MediumTests.class })
064public class TestRemoteTable {
065  @ClassRule
066  public static final HBaseClassTestRule CLASS_RULE =
067    HBaseClassTestRule.forClass(TestRemoteTable.class);
068
069  // Verify that invalid URL characters and arbitrary bytes are escaped when
070  // constructing REST URLs per HBASE-7621. RemoteHTable should support row keys
071  // and qualifiers containing any byte for all table operations.
072  private static final String INVALID_URL_CHARS_1 =
073    "|\"\\^{}\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000B\u000C";
074
075  // HColumnDescriptor prevents certain characters in column names. The following
076  // are examples of characters are allowed in column names but are not valid in
077  // URLs.
078  private static final String INVALID_URL_CHARS_2 = "|^{}\u0242";
079
080  // Besides alphanumeric these characters can also be present in table names.
081  private static final String VALID_TABLE_NAME_CHARS = "_-.";
082
083  private static final TableName TABLE =
084    TableName.valueOf("TestRemoteTable" + VALID_TABLE_NAME_CHARS);
085
086  private static final byte[] ROW_1 = Bytes.toBytes("testrow1" + INVALID_URL_CHARS_1);
087  private static final byte[] ROW_2 = Bytes.toBytes("testrow2" + INVALID_URL_CHARS_1);
088  private static final byte[] ROW_3 = Bytes.toBytes("testrow3" + INVALID_URL_CHARS_1);
089  private static final byte[] ROW_4 = Bytes.toBytes("testrow4" + INVALID_URL_CHARS_1);
090
091  private static final byte[] COLUMN_1 = Bytes.toBytes("a" + INVALID_URL_CHARS_2);
092  private static final byte[] COLUMN_2 = Bytes.toBytes("b" + INVALID_URL_CHARS_2);
093  private static final byte[] COLUMN_3 = Bytes.toBytes("c" + INVALID_URL_CHARS_2);
094
095  private static final byte[] QUALIFIER_1 = Bytes.toBytes("1" + INVALID_URL_CHARS_1);
096  private static final byte[] QUALIFIER_2 = Bytes.toBytes("2" + INVALID_URL_CHARS_1);
097  private static final byte[] VALUE_1 = Bytes.toBytes("testvalue1");
098  private static final byte[] VALUE_2 = Bytes.toBytes("testvalue2");
099
100  private static final long ONE_HOUR = 60 * 60 * 1000;
101  private static final long TS_2 = EnvironmentEdgeManager.currentTime();
102  private static final long TS_1 = TS_2 - ONE_HOUR;
103
104  private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
105  private static final HBaseRESTTestingUtility REST_TEST_UTIL = new HBaseRESTTestingUtility();
106  private RemoteHTable remoteTable;
107
108  @BeforeClass
109  public static void setUpBeforeClass() throws Exception {
110    TEST_UTIL.startMiniCluster();
111    REST_TEST_UTIL.startServletContainer(TEST_UTIL.getConfiguration());
112  }
113
114  @Before
115  public void before() throws Exception {
116    Admin admin = TEST_UTIL.getAdmin();
117    if (admin.tableExists(TABLE)) {
118      if (admin.isTableEnabled(TABLE)) {
119        admin.disableTable(TABLE);
120      }
121
122      admin.deleteTable(TABLE);
123    }
124
125    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(TABLE)
126      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(COLUMN_1).setMaxVersions(3).build())
127      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(COLUMN_2).setMaxVersions(3).build())
128      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(COLUMN_3).setMaxVersions(3).build())
129      .build();
130    admin.createTable(tableDescriptor);
131    try (Table table = TEST_UTIL.getConnection().getTable(TABLE)) {
132      Put put = new Put(ROW_1);
133      put.addColumn(COLUMN_1, QUALIFIER_1, TS_2, VALUE_1);
134      table.put(put);
135      put = new Put(ROW_2);
136      put.addColumn(COLUMN_1, QUALIFIER_1, TS_1, VALUE_1);
137      put.addColumn(COLUMN_1, QUALIFIER_1, TS_2, VALUE_2);
138      put.addColumn(COLUMN_2, QUALIFIER_2, TS_2, VALUE_2);
139      table.put(put);
140    }
141    remoteTable =
142      new RemoteHTable(new Client(new Cluster().add("localhost", REST_TEST_UTIL.getServletPort())),
143        TEST_UTIL.getConfiguration(), TABLE.toBytes());
144  }
145
146  @After
147  public void after() throws Exception {
148    remoteTable.close();
149  }
150
151  @AfterClass
152  public static void tearDownAfterClass() throws Exception {
153    REST_TEST_UTIL.shutdownServletContainer();
154    TEST_UTIL.shutdownMiniCluster();
155  }
156
157  @Test
158  public void testGetTableDescriptor() throws IOException {
159    try (Table table = TEST_UTIL.getConnection().getTable(TABLE)) {
160      TableDescriptor local = table.getDescriptor();
161      assertEquals(remoteTable.getDescriptor(), local);
162    }
163  }
164
165  @Test
166  public void testGet() throws IOException {
167    // Requires UriCompliance.Violation.SUSPICIOUS_PATH_CHARACTERS
168    // Otherwise fails with "400: Suspicious Path Character"
169    // In this test, the request path resolves to
170    // "/TestRemoteTable_-./testrow1%7C%22%5C%5E%7B%7D%01%02%03%04%05%06%07%08%09%0B%0C/"
171    // and is considered suspicious by the Jetty 12.
172    // Basically ROW_1 contains invalid URL characters here.
173    Get get = new Get(ROW_1);
174    Result result = remoteTable.get(get);
175    byte[] value1 = result.getValue(COLUMN_1, QUALIFIER_1);
176    byte[] value2 = result.getValue(COLUMN_2, QUALIFIER_2);
177    assertNotNull(value1);
178    assertTrue(Bytes.equals(VALUE_1, value1));
179    assertNull(value2);
180
181    get = new Get(ROW_1);
182    get.addFamily(COLUMN_3);
183    result = remoteTable.get(get);
184    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
185    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
186    assertNull(value1);
187    assertNull(value2);
188
189    get = new Get(ROW_1);
190    get.addColumn(COLUMN_1, QUALIFIER_1);
191    get.addColumn(COLUMN_2, QUALIFIER_2);
192    result = remoteTable.get(get);
193    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
194    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
195    assertNotNull(value1);
196    assertTrue(Bytes.equals(VALUE_1, value1));
197    assertNull(value2);
198
199    get = new Get(ROW_2);
200    result = remoteTable.get(get);
201    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
202    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
203    assertNotNull(value1);
204    assertTrue(Bytes.equals(VALUE_2, value1)); // @TS_2
205    assertNotNull(value2);
206    assertTrue(Bytes.equals(VALUE_2, value2));
207
208    get = new Get(ROW_2);
209    get.addFamily(COLUMN_1);
210    result = remoteTable.get(get);
211    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
212    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
213    assertNotNull(value1);
214    assertTrue(Bytes.equals(VALUE_2, value1)); // @TS_2
215    assertNull(value2);
216
217    get = new Get(ROW_2);
218    get.addColumn(COLUMN_1, QUALIFIER_1);
219    get.addColumn(COLUMN_2, QUALIFIER_2);
220    result = remoteTable.get(get);
221    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
222    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
223    assertNotNull(value1);
224    assertTrue(Bytes.equals(VALUE_2, value1)); // @TS_2
225    assertNotNull(value2);
226    assertTrue(Bytes.equals(VALUE_2, value2));
227
228    // test timestamp
229    get = new Get(ROW_2);
230    get.addFamily(COLUMN_1);
231    get.addFamily(COLUMN_2);
232    get.setTimestamp(TS_1);
233    result = remoteTable.get(get);
234    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
235    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
236    assertNotNull(value1);
237    assertTrue(Bytes.equals(VALUE_1, value1)); // @TS_1
238    assertNull(value2);
239
240    // test timerange
241    get = new Get(ROW_2);
242    get.addFamily(COLUMN_1);
243    get.addFamily(COLUMN_2);
244    get.setTimeRange(0, TS_1 + 1);
245    result = remoteTable.get(get);
246    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
247    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
248    assertNotNull(value1);
249    assertTrue(Bytes.equals(VALUE_1, value1)); // @TS_1
250    assertNull(value2);
251
252    // test maxVersions
253    get = new Get(ROW_2);
254    get.addFamily(COLUMN_1);
255    get.readVersions(2);
256    result = remoteTable.get(get);
257    int count = 0;
258    for (Cell kv : result.listCells()) {
259      if (CellUtil.matchingFamily(kv, COLUMN_1) && TS_1 == kv.getTimestamp()) {
260        assertTrue(CellUtil.matchingValue(kv, VALUE_1)); // @TS_1
261        count++;
262      }
263      if (CellUtil.matchingFamily(kv, COLUMN_1) && TS_2 == kv.getTimestamp()) {
264        assertTrue(CellUtil.matchingValue(kv, VALUE_2)); // @TS_2
265        count++;
266      }
267    }
268    assertEquals(2, count);
269  }
270
271  @Test
272  public void testMultiGet() throws Exception {
273    // In case of multi gets, the request path resolves to
274    // "/TestRemoteTable_-./multiget/?row=testrow1%7C%22%5C%5E&row=testrow2%7C%22%5C%5E%&v=3"
275    // and hence is not considered suspicious by the Jetty 12.
276    ArrayList<Get> gets = new ArrayList<>(2);
277    gets.add(new Get(ROW_1));
278    gets.add(new Get(ROW_2));
279    Result[] results = remoteTable.get(gets);
280    assertNotNull(results);
281    assertEquals(2, results.length);
282    assertEquals(1, results[0].size());
283    assertEquals(2, results[1].size());
284
285    // Test Versions
286    gets = new ArrayList<>(2);
287    Get g = new Get(ROW_1);
288    g.readVersions(3);
289    gets.add(g);
290    gets.add(new Get(ROW_2));
291    results = remoteTable.get(gets);
292    assertNotNull(results);
293    assertEquals(2, results.length);
294    assertEquals(1, results[0].size());
295    assertEquals(3, results[1].size());
296
297    // 404
298    gets = new ArrayList<>(1);
299    gets.add(new Get(Bytes.toBytes("RESALLYREALLYNOTTHERE")));
300    results = remoteTable.get(gets);
301    assertNotNull(results);
302    assertEquals(0, results.length);
303
304    gets = new ArrayList<>(3);
305    gets.add(new Get(Bytes.toBytes("RESALLYREALLYNOTTHERE")));
306    gets.add(new Get(ROW_1));
307    gets.add(new Get(ROW_2));
308    results = remoteTable.get(gets);
309    assertNotNull(results);
310    assertEquals(2, results.length);
311  }
312
313  @Test
314  public void testPut() throws IOException {
315    // Requires UriCompliance.Violation.SUSPICIOUS_PATH_CHARACTERS
316    // Otherwise fails with "400: Suspicious Path Character"
317    Put put = new Put(ROW_3);
318    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
319    remoteTable.put(put);
320
321    Get get = new Get(ROW_3);
322    get.addFamily(COLUMN_1);
323    Result result = remoteTable.get(get);
324    byte[] value = result.getValue(COLUMN_1, QUALIFIER_1);
325    assertNotNull(value);
326    assertTrue(Bytes.equals(VALUE_1, value));
327
328    // multiput
329    List<Put> puts = new ArrayList<>(3);
330    put = new Put(ROW_3);
331    put.addColumn(COLUMN_2, QUALIFIER_2, VALUE_2);
332    puts.add(put);
333    put = new Put(ROW_4);
334    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
335    puts.add(put);
336    put = new Put(ROW_4);
337    put.addColumn(COLUMN_2, QUALIFIER_2, VALUE_2);
338    puts.add(put);
339    remoteTable.put(puts);
340
341    get = new Get(ROW_3);
342    get.addFamily(COLUMN_2);
343    result = remoteTable.get(get);
344    value = result.getValue(COLUMN_2, QUALIFIER_2);
345    assertNotNull(value);
346    assertTrue(Bytes.equals(VALUE_2, value));
347    get = new Get(ROW_4);
348    result = remoteTable.get(get);
349    value = result.getValue(COLUMN_1, QUALIFIER_1);
350    assertNotNull(value);
351    assertTrue(Bytes.equals(VALUE_1, value));
352    value = result.getValue(COLUMN_2, QUALIFIER_2);
353    assertNotNull(value);
354    assertTrue(Bytes.equals(VALUE_2, value));
355
356    assertTrue(Bytes.equals(Bytes.toBytes("TestRemoteTable" + VALID_TABLE_NAME_CHARS),
357      remoteTable.getTableName()));
358  }
359
360  @Test
361  public void testDelete() throws IOException {
362    // Requires UriCompliance.Violation.SUSPICIOUS_PATH_CHARACTERS for put,
363    // otherwise fails with "400: Suspicious Path Character"
364    // This example is considered suspicious by the Jetty 12 due to reasons same as shown in
365    // testGet()
366
367    // Also, requires UriCompliance.Violation.AMBIGUOUS_EMPTY_SEGMENT
368    // Otherwise fails with "400: Ambiguous URI empty segment"
369    Put put = new Put(ROW_3);
370    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
371    put.addColumn(COLUMN_2, QUALIFIER_2, VALUE_2);
372    put.addColumn(COLUMN_3, QUALIFIER_1, VALUE_1);
373    put.addColumn(COLUMN_3, QUALIFIER_2, VALUE_2);
374    remoteTable.put(put);
375
376    Get get = new Get(ROW_3);
377    get.addFamily(COLUMN_1);
378    get.addFamily(COLUMN_2);
379    get.addFamily(COLUMN_3);
380    Result result = remoteTable.get(get);
381    byte[] value1 = result.getValue(COLUMN_1, QUALIFIER_1);
382    byte[] value2 = result.getValue(COLUMN_2, QUALIFIER_2);
383    byte[] value3 = result.getValue(COLUMN_3, QUALIFIER_1);
384    byte[] value4 = result.getValue(COLUMN_3, QUALIFIER_2);
385    assertNotNull(value1);
386    assertTrue(Bytes.equals(VALUE_1, value1));
387    assertNotNull(value2);
388    assertTrue(Bytes.equals(VALUE_2, value2));
389    assertNotNull(value3);
390    assertTrue(Bytes.equals(VALUE_1, value3));
391    assertNotNull(value4);
392    assertTrue(Bytes.equals(VALUE_2, value4));
393
394    Delete delete = new Delete(ROW_3);
395    delete.addColumn(COLUMN_2, QUALIFIER_2);
396    remoteTable.delete(delete);
397
398    get = new Get(ROW_3);
399    get.addFamily(COLUMN_1);
400    get.addFamily(COLUMN_2);
401    result = remoteTable.get(get);
402    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
403    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
404    assertNotNull(value1);
405    assertTrue(Bytes.equals(VALUE_1, value1));
406    assertNull(value2);
407
408    // This leads to path which resolves to
409    // "/TestRemoteTable_-./testrow3%7C%22%5C%5E%7B%7D%01%02%03%04%05%06%07%08%09%0B%0C//1"
410    // causing "400: Ambiguous URI empty segment" error with Jetty 12.
411    delete = new Delete(ROW_3);
412    delete.setTimestamp(1L);
413    remoteTable.delete(delete);
414
415    get = new Get(ROW_3);
416    get.addFamily(COLUMN_1);
417    get.addFamily(COLUMN_2);
418    result = remoteTable.get(get);
419    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
420    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
421    assertNotNull(value1);
422    assertTrue(Bytes.equals(VALUE_1, value1));
423    assertNull(value2);
424
425    // Delete column family from row
426    delete = new Delete(ROW_3);
427    delete.addFamily(COLUMN_3);
428    remoteTable.delete(delete);
429
430    get = new Get(ROW_3);
431    get.addFamily(COLUMN_3);
432    result = remoteTable.get(get);
433    value3 = result.getValue(COLUMN_3, QUALIFIER_1);
434    value4 = result.getValue(COLUMN_3, QUALIFIER_2);
435    assertNull(value3);
436    assertNull(value4);
437
438    delete = new Delete(ROW_3);
439    remoteTable.delete(delete);
440
441    get = new Get(ROW_3);
442    get.addFamily(COLUMN_1);
443    get.addFamily(COLUMN_2);
444    result = remoteTable.get(get);
445    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
446    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
447    assertNull(value1);
448    assertNull(value2);
449  }
450
451  /**
452   * Test RemoteHTable.Scanner
453   */
454  @Test
455  public void testScanner() throws IOException {
456    List<Put> puts = new ArrayList<>(4);
457    Put put = new Put(ROW_1);
458    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
459    puts.add(put);
460    put = new Put(ROW_2);
461    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
462    puts.add(put);
463    put = new Put(ROW_3);
464    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
465    puts.add(put);
466    put = new Put(ROW_4);
467    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
468    puts.add(put);
469    remoteTable.put(puts);
470
471    ResultScanner scanner = remoteTable.getScanner(new Scan());
472
473    Result[] results = scanner.next(1);
474    assertNotNull(results);
475    assertEquals(1, results.length);
476    assertTrue(Bytes.equals(ROW_1, results[0].getRow()));
477
478    Result result = scanner.next();
479    assertNotNull(result);
480    assertTrue(Bytes.equals(ROW_2, result.getRow()));
481
482    results = scanner.next(2);
483    assertNotNull(results);
484    assertEquals(2, results.length);
485    assertTrue(Bytes.equals(ROW_3, results[0].getRow()));
486    assertTrue(Bytes.equals(ROW_4, results[1].getRow()));
487
488    results = scanner.next(1);
489    assertNull(results);
490    scanner.close();
491
492    scanner = remoteTable.getScanner(COLUMN_1);
493    results = scanner.next(4);
494    assertNotNull(results);
495    assertEquals(4, results.length);
496    assertTrue(Bytes.equals(ROW_1, results[0].getRow()));
497    assertTrue(Bytes.equals(ROW_2, results[1].getRow()));
498    assertTrue(Bytes.equals(ROW_3, results[2].getRow()));
499    assertTrue(Bytes.equals(ROW_4, results[3].getRow()));
500
501    scanner.close();
502
503    scanner = remoteTable.getScanner(COLUMN_1, QUALIFIER_1);
504    results = scanner.next(4);
505    assertNotNull(results);
506    assertEquals(4, results.length);
507    assertTrue(Bytes.equals(ROW_1, results[0].getRow()));
508    assertTrue(Bytes.equals(ROW_2, results[1].getRow()));
509    assertTrue(Bytes.equals(ROW_3, results[2].getRow()));
510    assertTrue(Bytes.equals(ROW_4, results[3].getRow()));
511    scanner.close();
512    assertTrue(remoteTable.isAutoFlush());
513  }
514
515  @Test
516  public void testCheckAndDelete() throws IOException {
517    // Requires UriCompliance.Violation.SUSPICIOUS_PATH_CHARACTERS
518    // Otherwise fails with "400: Suspicious Path Character"
519    // This example is considered suspicious by the Jetty 12 due to reasons same as shown in
520    // testGet()
521    Get get = new Get(ROW_1);
522    Result result = remoteTable.get(get);
523    byte[] value1 = result.getValue(COLUMN_1, QUALIFIER_1);
524    byte[] value2 = result.getValue(COLUMN_2, QUALIFIER_2);
525    assertNotNull(value1);
526    assertTrue(Bytes.equals(VALUE_1, value1));
527    assertNull(value2);
528    assertTrue(remoteTable.exists(get));
529    assertEquals(1, remoteTable.exists(Collections.singletonList(get)).length);
530    Delete delete = new Delete(ROW_1);
531
532    remoteTable.checkAndMutate(ROW_1, COLUMN_1).qualifier(QUALIFIER_1).ifEquals(VALUE_1)
533      .thenDelete(delete);
534    assertFalse(remoteTable.exists(get));
535
536    Put put = new Put(ROW_1);
537    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
538    remoteTable.put(put);
539
540    assertTrue(remoteTable.checkAndMutate(ROW_1, COLUMN_1).qualifier(QUALIFIER_1).ifEquals(VALUE_1)
541      .thenPut(put));
542    assertFalse(remoteTable.checkAndMutate(ROW_1, COLUMN_1).qualifier(QUALIFIER_1).ifEquals(VALUE_2)
543      .thenPut(put));
544  }
545
546  /**
547   * Test RemoteHable.Scanner.iterator method
548   */
549  @Test
550  public void testIteratorScaner() throws IOException {
551    List<Put> puts = new ArrayList<>(4);
552    Put put = new Put(ROW_1);
553    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
554    puts.add(put);
555    put = new Put(ROW_2);
556    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
557    puts.add(put);
558    put = new Put(ROW_3);
559    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
560    puts.add(put);
561    put = new Put(ROW_4);
562    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
563    puts.add(put);
564    remoteTable.put(puts);
565
566    ResultScanner scanner = remoteTable.getScanner(new Scan());
567    Iterator<Result> iterator = scanner.iterator();
568    assertTrue(iterator.hasNext());
569    int counter = 0;
570    while (iterator.hasNext()) {
571      iterator.next();
572      counter++;
573    }
574    assertEquals(4, counter);
575  }
576
577  /**
578   * Test a some methods of class Response.
579   */
580  @Test
581  public void testResponse() {
582    Response response = new Response(200);
583    assertEquals(200, response.getCode());
584    Header[] headers = new Header[2];
585    headers[0] = new BasicHeader("header1", "value1");
586    headers[1] = new BasicHeader("header2", "value2");
587    response = new Response(200, headers);
588    assertEquals("value1", response.getHeader("header1"));
589    assertFalse(response.hasBody());
590    response.setCode(404);
591    assertEquals(404, response.getCode());
592    headers = new Header[2];
593    headers[0] = new BasicHeader("header1", "value1.1");
594    headers[1] = new BasicHeader("header2", "value2");
595    response.setHeaders(headers);
596    assertEquals("value1.1", response.getHeader("header1"));
597    response.setBody(Bytes.toBytes("body"));
598    assertTrue(response.hasBody());
599  }
600
601  /**
602   * Tests scanner with limitation limit the number of rows each scanner scan fetch at life time The
603   * number of rows returned should be equal to the limit
604   */
605  @Test
606  public void testLimitedScan() throws Exception {
607    int numTrials = 100;
608    int limit = 60;
609
610    // Truncate the test table for inserting test scenarios rows keys
611    TEST_UTIL.getAdmin().disableTable(TABLE);
612    TEST_UTIL.getAdmin().truncateTable(TABLE, false);
613    String row = "testrow";
614
615    try (Table table = TEST_UTIL.getConnection().getTable(TABLE)) {
616      List<Put> puts = new ArrayList<>();
617      Put put = null;
618      for (int i = 1; i <= numTrials; i++) {
619        put = new Put(Bytes.toBytes(row + i));
620        put.addColumn(COLUMN_1, QUALIFIER_1, TS_2, Bytes.toBytes("testvalue" + i));
621        puts.add(put);
622      }
623      table.put(puts);
624    }
625
626    remoteTable =
627      new RemoteHTable(new Client(new Cluster().add("localhost", REST_TEST_UTIL.getServletPort())),
628        TEST_UTIL.getConfiguration(), TABLE.toBytes());
629
630    Scan scan = new Scan();
631    scan.setLimit(limit);
632    ResultScanner scanner = remoteTable.getScanner(scan);
633    Iterator<Result> resultIterator = scanner.iterator();
634    int counter = 0;
635    while (resultIterator.hasNext()) {
636      resultIterator.next();
637      counter++;
638    }
639    assertEquals(limit, counter);
640  }
641
642  /**
643   * Tests keeping a HBase scanner alive for long periods of time. Each call to next() should reset
644   * the ConnectionCache timeout for the scanner's connection.
645   * @throws Exception if starting the servlet container or disabling or truncating the table fails
646   */
647  @Test
648  public void testLongLivedScan() throws Exception {
649    int numTrials = 6;
650    int trialPause = 1000;
651    int cleanUpInterval = 100;
652
653    // Shutdown the Rest Servlet container
654    REST_TEST_UTIL.shutdownServletContainer();
655
656    // Set the ConnectionCache timeout to trigger halfway through the trials
657    TEST_UTIL.getConfiguration().setLong(RESTServlet.MAX_IDLETIME, (numTrials / 2) * trialPause);
658    TEST_UTIL.getConfiguration().setLong(RESTServlet.CLEANUP_INTERVAL, cleanUpInterval);
659
660    // Start the Rest Servlet container
661    REST_TEST_UTIL.startServletContainer(TEST_UTIL.getConfiguration());
662
663    // Truncate the test table for inserting test scenarios rows keys
664    TEST_UTIL.getAdmin().disableTable(TABLE);
665    TEST_UTIL.getAdmin().truncateTable(TABLE, false);
666
667    remoteTable =
668      new RemoteHTable(new Client(new Cluster().add("localhost", REST_TEST_UTIL.getServletPort())),
669        TEST_UTIL.getConfiguration(), TABLE.toBytes());
670
671    String row = "testrow";
672
673    try (Table table = TEST_UTIL.getConnection().getTable(TABLE)) {
674      List<Put> puts = new ArrayList<Put>();
675      Put put = null;
676      for (int i = 1; i <= numTrials; i++) {
677        put = new Put(Bytes.toBytes(row + i));
678        put.addColumn(COLUMN_1, QUALIFIER_1, TS_2, Bytes.toBytes("testvalue" + i));
679        puts.add(put);
680      }
681      table.put(puts);
682    }
683
684    Scan scan = new Scan();
685    scan.setCaching(1);
686    scan.setBatch(1);
687
688    ResultScanner scanner = remoteTable.getScanner(scan);
689    Result result = null;
690    // get scanner and rows
691    for (int i = 1; i <= numTrials; i++) {
692      // Make sure that the Scanner doesn't throw an exception after the ConnectionCache timeout
693      result = scanner.next();
694      assertEquals(row + i, Bytes.toString(result.getRow()));
695      Thread.sleep(trialPause);
696    }
697  }
698
699  @Test
700  public void testScanWithInlcudeStartStopRow() throws Exception {
701    int numTrials = 6;
702
703    // Truncate the test table for inserting test scenarios rows keys
704    TEST_UTIL.getAdmin().disableTable(TABLE);
705    TEST_UTIL.getAdmin().truncateTable(TABLE, false);
706    String row = "testrow";
707
708    try (Table table = TEST_UTIL.getConnection().getTable(TABLE)) {
709      List<Put> puts = new ArrayList<>();
710      Put put = null;
711      for (int i = 1; i <= numTrials; i++) {
712        put = new Put(Bytes.toBytes(row + i));
713        put.addColumn(COLUMN_1, QUALIFIER_1, TS_2, Bytes.toBytes("testvalue" + i));
714        puts.add(put);
715      }
716      table.put(puts);
717    }
718
719    remoteTable =
720      new RemoteHTable(new Client(new Cluster().add("localhost", REST_TEST_UTIL.getServletPort())),
721        TEST_UTIL.getConfiguration(), TABLE.toBytes());
722
723    Scan scan =
724      new Scan().withStartRow(Bytes.toBytes(row + "1")).withStopRow(Bytes.toBytes(row + "5"));
725
726    ResultScanner scanner = remoteTable.getScanner(scan);
727    Iterator<Result> resultIterator = scanner.iterator();
728    int counter = 0;
729    while (resultIterator.hasNext()) {
730      byte[] row1 = resultIterator.next().getRow();
731      System.out.println(Bytes.toString(row1));
732      counter++;
733    }
734    assertEquals(4, counter);
735
736    // test with include start row false
737    scan = new Scan().withStartRow(Bytes.toBytes(row + "1"), false)
738      .withStopRow(Bytes.toBytes(row + "5"));
739    scanner = remoteTable.getScanner(scan);
740    resultIterator = scanner.iterator();
741    counter = 0;
742    while (resultIterator.hasNext()) {
743      byte[] row1 = resultIterator.next().getRow();
744      System.out.println(Bytes.toString(row1));
745      counter++;
746    }
747    assertEquals(3, counter);
748
749    // test with include start row false and stop row true
750    scan = new Scan().withStartRow(Bytes.toBytes(row + "1"), false)
751      .withStopRow(Bytes.toBytes(row + "5"), true);
752    scanner = remoteTable.getScanner(scan);
753    resultIterator = scanner.iterator();
754    counter = 0;
755    while (resultIterator.hasNext()) {
756      byte[] row1 = resultIterator.next().getRow();
757      System.out.println(Bytes.toString(row1));
758      counter++;
759    }
760    assertEquals(4, counter);
761
762    // test with include start row true and stop row true
763    scan = new Scan().withStartRow(Bytes.toBytes(row + "1"), true)
764      .withStopRow(Bytes.toBytes(row + "5"), true);
765    scanner = remoteTable.getScanner(scan);
766    resultIterator = scanner.iterator();
767    counter = 0;
768    while (resultIterator.hasNext()) {
769      byte[] row1 = resultIterator.next().getRow();
770      System.out.println(Bytes.toString(row1));
771      counter++;
772    }
773    assertEquals(5, counter);
774  }
775
776}