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.HBaseTestingUtility;
035import org.apache.hadoop.hbase.HColumnDescriptor;
036import org.apache.hadoop.hbase.HTableDescriptor;
037import org.apache.hadoop.hbase.TableName;
038import org.apache.hadoop.hbase.client.Admin;
039import org.apache.hadoop.hbase.client.Delete;
040import org.apache.hadoop.hbase.client.Get;
041import org.apache.hadoop.hbase.client.Put;
042import org.apache.hadoop.hbase.client.Result;
043import org.apache.hadoop.hbase.client.ResultScanner;
044import org.apache.hadoop.hbase.client.Scan;
045import org.apache.hadoop.hbase.client.Table;
046import org.apache.hadoop.hbase.rest.HBaseRESTTestingUtility;
047import org.apache.hadoop.hbase.rest.RESTServlet;
048import org.apache.hadoop.hbase.testclassification.MediumTests;
049import org.apache.hadoop.hbase.testclassification.RestTests;
050import org.apache.hadoop.hbase.util.Bytes;
051import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
052import org.apache.http.Header;
053import org.apache.http.message.BasicHeader;
054import org.junit.After;
055import org.junit.AfterClass;
056import org.junit.Before;
057import org.junit.BeforeClass;
058import org.junit.ClassRule;
059import org.junit.Test;
060import org.junit.experimental.categories.Category;
061
062@Category({ RestTests.class, MediumTests.class })
063public class TestRemoteTable {
064  @ClassRule
065  public static final HBaseClassTestRule CLASS_RULE =
066    HBaseClassTestRule.forClass(TestRemoteTable.class);
067
068  // Verify that invalid URL characters and arbitrary bytes are escaped when
069  // constructing REST URLs per HBASE-7621. RemoteHTable should support row keys
070  // and qualifiers containing any byte for all table operations.
071  private static final String INVALID_URL_CHARS_1 =
072    "|\"\\^{}\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000B\u000C";
073
074  // HColumnDescriptor prevents certain characters in column names. The following
075  // are examples of characters are allowed in column names but are not valid in
076  // URLs.
077  private static final String INVALID_URL_CHARS_2 = "|^{}\u0242";
078
079  // Besides alphanumeric these characters can also be present in table names.
080  private static final String VALID_TABLE_NAME_CHARS = "_-.";
081
082  private static final TableName TABLE =
083    TableName.valueOf("TestRemoteTable" + VALID_TABLE_NAME_CHARS);
084
085  private static final byte[] ROW_1 = Bytes.toBytes("testrow1" + INVALID_URL_CHARS_1);
086  private static final byte[] ROW_2 = Bytes.toBytes("testrow2" + INVALID_URL_CHARS_1);
087  private static final byte[] ROW_3 = Bytes.toBytes("testrow3" + INVALID_URL_CHARS_1);
088  private static final byte[] ROW_4 = Bytes.toBytes("testrow4" + INVALID_URL_CHARS_1);
089
090  private static final byte[] COLUMN_1 = Bytes.toBytes("a" + INVALID_URL_CHARS_2);
091  private static final byte[] COLUMN_2 = Bytes.toBytes("b" + INVALID_URL_CHARS_2);
092  private static final byte[] COLUMN_3 = Bytes.toBytes("c" + INVALID_URL_CHARS_2);
093
094  private static final byte[] QUALIFIER_1 = Bytes.toBytes("1" + INVALID_URL_CHARS_1);
095  private static final byte[] QUALIFIER_2 = Bytes.toBytes("2" + INVALID_URL_CHARS_1);
096  private static final byte[] VALUE_1 = Bytes.toBytes("testvalue1");
097  private static final byte[] VALUE_2 = Bytes.toBytes("testvalue2");
098
099  private static final long ONE_HOUR = 60 * 60 * 1000;
100  private static final long TS_2 = EnvironmentEdgeManager.currentTime();
101  private static final long TS_1 = TS_2 - ONE_HOUR;
102
103  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
104  private static final HBaseRESTTestingUtility REST_TEST_UTIL = new HBaseRESTTestingUtility();
105  private RemoteHTable remoteTable;
106
107  @BeforeClass
108  public static void setUpBeforeClass() throws Exception {
109    TEST_UTIL.startMiniCluster();
110    REST_TEST_UTIL.startServletContainer(TEST_UTIL.getConfiguration());
111  }
112
113  @Before
114  public void before() throws Exception {
115    Admin admin = TEST_UTIL.getAdmin();
116    if (admin.tableExists(TABLE)) {
117      if (admin.isTableEnabled(TABLE)) {
118        admin.disableTable(TABLE);
119      }
120
121      admin.deleteTable(TABLE);
122    }
123    HTableDescriptor htd = new HTableDescriptor(TABLE);
124    htd.addFamily(new HColumnDescriptor(COLUMN_1).setMaxVersions(3));
125    htd.addFamily(new HColumnDescriptor(COLUMN_2).setMaxVersions(3));
126    htd.addFamily(new HColumnDescriptor(COLUMN_3).setMaxVersions(3));
127    admin.createTable(htd);
128    try (Table table = TEST_UTIL.getConnection().getTable(TABLE)) {
129      Put put = new Put(ROW_1);
130      put.addColumn(COLUMN_1, QUALIFIER_1, TS_2, VALUE_1);
131      table.put(put);
132      put = new Put(ROW_2);
133      put.addColumn(COLUMN_1, QUALIFIER_1, TS_1, VALUE_1);
134      put.addColumn(COLUMN_1, QUALIFIER_1, TS_2, VALUE_2);
135      put.addColumn(COLUMN_2, QUALIFIER_2, TS_2, VALUE_2);
136      table.put(put);
137    }
138    remoteTable =
139      new RemoteHTable(new Client(new Cluster().add("localhost", REST_TEST_UTIL.getServletPort())),
140        TEST_UTIL.getConfiguration(), TABLE.toBytes());
141  }
142
143  @After
144  public void after() throws Exception {
145    remoteTable.close();
146  }
147
148  @AfterClass
149  public static void tearDownAfterClass() throws Exception {
150    REST_TEST_UTIL.shutdownServletContainer();
151    TEST_UTIL.shutdownMiniCluster();
152  }
153
154  @Test
155  public void testGetTableDescriptor() throws IOException {
156    Table table = null;
157    try {
158      table = TEST_UTIL.getConnection().getTable(TABLE);
159      HTableDescriptor local = table.getTableDescriptor();
160      assertEquals(remoteTable.getTableDescriptor(), local);
161    } finally {
162      if (null != table) table.close();
163    }
164  }
165
166  @Test
167  public void testGet() throws IOException {
168    Get get = new Get(ROW_1);
169    Result result = remoteTable.get(get);
170    byte[] value1 = result.getValue(COLUMN_1, QUALIFIER_1);
171    byte[] value2 = result.getValue(COLUMN_2, QUALIFIER_2);
172    assertNotNull(value1);
173    assertTrue(Bytes.equals(VALUE_1, value1));
174    assertNull(value2);
175
176    get = new Get(ROW_1);
177    get.addFamily(COLUMN_3);
178    result = remoteTable.get(get);
179    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
180    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
181    assertNull(value1);
182    assertNull(value2);
183
184    get = new Get(ROW_1);
185    get.addColumn(COLUMN_1, QUALIFIER_1);
186    get.addColumn(COLUMN_2, QUALIFIER_2);
187    result = remoteTable.get(get);
188    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
189    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
190    assertNotNull(value1);
191    assertTrue(Bytes.equals(VALUE_1, value1));
192    assertNull(value2);
193
194    get = new Get(ROW_2);
195    result = remoteTable.get(get);
196    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
197    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
198    assertNotNull(value1);
199    assertTrue(Bytes.equals(VALUE_2, value1)); // @TS_2
200    assertNotNull(value2);
201    assertTrue(Bytes.equals(VALUE_2, value2));
202
203    get = new Get(ROW_2);
204    get.addFamily(COLUMN_1);
205    result = remoteTable.get(get);
206    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
207    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
208    assertNotNull(value1);
209    assertTrue(Bytes.equals(VALUE_2, value1)); // @TS_2
210    assertNull(value2);
211
212    get = new Get(ROW_2);
213    get.addColumn(COLUMN_1, QUALIFIER_1);
214    get.addColumn(COLUMN_2, QUALIFIER_2);
215    result = remoteTable.get(get);
216    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
217    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
218    assertNotNull(value1);
219    assertTrue(Bytes.equals(VALUE_2, value1)); // @TS_2
220    assertNotNull(value2);
221    assertTrue(Bytes.equals(VALUE_2, value2));
222
223    // test timestamp
224    get = new Get(ROW_2);
225    get.addFamily(COLUMN_1);
226    get.addFamily(COLUMN_2);
227    get.setTimestamp(TS_1);
228    result = remoteTable.get(get);
229    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
230    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
231    assertNotNull(value1);
232    assertTrue(Bytes.equals(VALUE_1, value1)); // @TS_1
233    assertNull(value2);
234
235    // test timerange
236    get = new Get(ROW_2);
237    get.addFamily(COLUMN_1);
238    get.addFamily(COLUMN_2);
239    get.setTimeRange(0, TS_1 + 1);
240    result = remoteTable.get(get);
241    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
242    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
243    assertNotNull(value1);
244    assertTrue(Bytes.equals(VALUE_1, value1)); // @TS_1
245    assertNull(value2);
246
247    // test maxVersions
248    get = new Get(ROW_2);
249    get.addFamily(COLUMN_1);
250    get.setMaxVersions(2);
251    result = remoteTable.get(get);
252    int count = 0;
253    for (Cell kv : result.listCells()) {
254      if (CellUtil.matchingFamily(kv, COLUMN_1) && TS_1 == kv.getTimestamp()) {
255        assertTrue(CellUtil.matchingValue(kv, VALUE_1)); // @TS_1
256        count++;
257      }
258      if (CellUtil.matchingFamily(kv, COLUMN_1) && TS_2 == kv.getTimestamp()) {
259        assertTrue(CellUtil.matchingValue(kv, VALUE_2)); // @TS_2
260        count++;
261      }
262    }
263    assertEquals(2, count);
264  }
265
266  @Test
267  public void testMultiGet() throws Exception {
268    ArrayList<Get> gets = new ArrayList<>(2);
269    gets.add(new Get(ROW_1));
270    gets.add(new Get(ROW_2));
271    Result[] results = remoteTable.get(gets);
272    assertNotNull(results);
273    assertEquals(2, results.length);
274    assertEquals(1, results[0].size());
275    assertEquals(2, results[1].size());
276
277    // Test Versions
278    gets = new ArrayList<>(2);
279    Get g = new Get(ROW_1);
280    g.setMaxVersions(3);
281    gets.add(g);
282    gets.add(new Get(ROW_2));
283    results = remoteTable.get(gets);
284    assertNotNull(results);
285    assertEquals(2, results.length);
286    assertEquals(1, results[0].size());
287    assertEquals(3, results[1].size());
288
289    // 404
290    gets = new ArrayList<>(1);
291    gets.add(new Get(Bytes.toBytes("RESALLYREALLYNOTTHERE")));
292    results = remoteTable.get(gets);
293    assertNotNull(results);
294    assertEquals(0, results.length);
295
296    gets = new ArrayList<>(3);
297    gets.add(new Get(Bytes.toBytes("RESALLYREALLYNOTTHERE")));
298    gets.add(new Get(ROW_1));
299    gets.add(new Get(ROW_2));
300    results = remoteTable.get(gets);
301    assertNotNull(results);
302    assertEquals(2, results.length);
303  }
304
305  @Test
306  public void testPut() throws IOException {
307    Put put = new Put(ROW_3);
308    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
309    remoteTable.put(put);
310
311    Get get = new Get(ROW_3);
312    get.addFamily(COLUMN_1);
313    Result result = remoteTable.get(get);
314    byte[] value = result.getValue(COLUMN_1, QUALIFIER_1);
315    assertNotNull(value);
316    assertTrue(Bytes.equals(VALUE_1, value));
317
318    // multiput
319    List<Put> puts = new ArrayList<>(3);
320    put = new Put(ROW_3);
321    put.addColumn(COLUMN_2, QUALIFIER_2, VALUE_2);
322    puts.add(put);
323    put = new Put(ROW_4);
324    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
325    puts.add(put);
326    put = new Put(ROW_4);
327    put.addColumn(COLUMN_2, QUALIFIER_2, VALUE_2);
328    puts.add(put);
329    remoteTable.put(puts);
330
331    get = new Get(ROW_3);
332    get.addFamily(COLUMN_2);
333    result = remoteTable.get(get);
334    value = result.getValue(COLUMN_2, QUALIFIER_2);
335    assertNotNull(value);
336    assertTrue(Bytes.equals(VALUE_2, value));
337    get = new Get(ROW_4);
338    result = remoteTable.get(get);
339    value = result.getValue(COLUMN_1, QUALIFIER_1);
340    assertNotNull(value);
341    assertTrue(Bytes.equals(VALUE_1, value));
342    value = result.getValue(COLUMN_2, QUALIFIER_2);
343    assertNotNull(value);
344    assertTrue(Bytes.equals(VALUE_2, value));
345
346    assertTrue(Bytes.equals(Bytes.toBytes("TestRemoteTable" + VALID_TABLE_NAME_CHARS),
347      remoteTable.getTableName()));
348  }
349
350  @Test
351  public void testDelete() throws IOException {
352    Put put = new Put(ROW_3);
353    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
354    put.addColumn(COLUMN_2, QUALIFIER_2, VALUE_2);
355    put.addColumn(COLUMN_3, QUALIFIER_1, VALUE_1);
356    put.addColumn(COLUMN_3, QUALIFIER_2, VALUE_2);
357    remoteTable.put(put);
358
359    Get get = new Get(ROW_3);
360    get.addFamily(COLUMN_1);
361    get.addFamily(COLUMN_2);
362    get.addFamily(COLUMN_3);
363    Result result = remoteTable.get(get);
364    byte[] value1 = result.getValue(COLUMN_1, QUALIFIER_1);
365    byte[] value2 = result.getValue(COLUMN_2, QUALIFIER_2);
366    byte[] value3 = result.getValue(COLUMN_3, QUALIFIER_1);
367    byte[] value4 = result.getValue(COLUMN_3, QUALIFIER_2);
368    assertNotNull(value1);
369    assertTrue(Bytes.equals(VALUE_1, value1));
370    assertNotNull(value2);
371    assertTrue(Bytes.equals(VALUE_2, value2));
372    assertNotNull(value3);
373    assertTrue(Bytes.equals(VALUE_1, value3));
374    assertNotNull(value4);
375    assertTrue(Bytes.equals(VALUE_2, value4));
376
377    Delete delete = new Delete(ROW_3);
378    delete.addColumn(COLUMN_2, QUALIFIER_2);
379    remoteTable.delete(delete);
380
381    get = new Get(ROW_3);
382    get.addFamily(COLUMN_1);
383    get.addFamily(COLUMN_2);
384    result = remoteTable.get(get);
385    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
386    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
387    assertNotNull(value1);
388    assertTrue(Bytes.equals(VALUE_1, value1));
389    assertNull(value2);
390
391    delete = new Delete(ROW_3);
392    delete.setTimestamp(1L);
393    remoteTable.delete(delete);
394
395    get = new Get(ROW_3);
396    get.addFamily(COLUMN_1);
397    get.addFamily(COLUMN_2);
398    result = remoteTable.get(get);
399    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
400    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
401    assertNotNull(value1);
402    assertTrue(Bytes.equals(VALUE_1, value1));
403    assertNull(value2);
404
405    // Delete column family from row
406    delete = new Delete(ROW_3);
407    delete.addFamily(COLUMN_3);
408    remoteTable.delete(delete);
409
410    get = new Get(ROW_3);
411    get.addFamily(COLUMN_3);
412    result = remoteTable.get(get);
413    value3 = result.getValue(COLUMN_3, QUALIFIER_1);
414    value4 = result.getValue(COLUMN_3, QUALIFIER_2);
415    assertNull(value3);
416    assertNull(value4);
417
418    delete = new Delete(ROW_3);
419    remoteTable.delete(delete);
420
421    get = new Get(ROW_3);
422    get.addFamily(COLUMN_1);
423    get.addFamily(COLUMN_2);
424    result = remoteTable.get(get);
425    value1 = result.getValue(COLUMN_1, QUALIFIER_1);
426    value2 = result.getValue(COLUMN_2, QUALIFIER_2);
427    assertNull(value1);
428    assertNull(value2);
429  }
430
431  /**
432   * Test RemoteHTable.Scanner
433   */
434  @Test
435  public void testScanner() throws IOException {
436    List<Put> puts = new ArrayList<>(4);
437    Put put = new Put(ROW_1);
438    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
439    puts.add(put);
440    put = new Put(ROW_2);
441    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
442    puts.add(put);
443    put = new Put(ROW_3);
444    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
445    puts.add(put);
446    put = new Put(ROW_4);
447    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
448    puts.add(put);
449    remoteTable.put(puts);
450
451    ResultScanner scanner = remoteTable.getScanner(new Scan());
452
453    Result[] results = scanner.next(1);
454    assertNotNull(results);
455    assertEquals(1, results.length);
456    assertTrue(Bytes.equals(ROW_1, results[0].getRow()));
457
458    Result result = scanner.next();
459    assertNotNull(result);
460    assertTrue(Bytes.equals(ROW_2, result.getRow()));
461
462    results = scanner.next(2);
463    assertNotNull(results);
464    assertEquals(2, results.length);
465    assertTrue(Bytes.equals(ROW_3, results[0].getRow()));
466    assertTrue(Bytes.equals(ROW_4, results[1].getRow()));
467
468    results = scanner.next(1);
469    assertNull(results);
470    scanner.close();
471
472    scanner = remoteTable.getScanner(COLUMN_1);
473    results = scanner.next(4);
474    assertNotNull(results);
475    assertEquals(4, results.length);
476    assertTrue(Bytes.equals(ROW_1, results[0].getRow()));
477    assertTrue(Bytes.equals(ROW_2, results[1].getRow()));
478    assertTrue(Bytes.equals(ROW_3, results[2].getRow()));
479    assertTrue(Bytes.equals(ROW_4, results[3].getRow()));
480
481    scanner.close();
482
483    scanner = remoteTable.getScanner(COLUMN_1, QUALIFIER_1);
484    results = scanner.next(4);
485    assertNotNull(results);
486    assertEquals(4, results.length);
487    assertTrue(Bytes.equals(ROW_1, results[0].getRow()));
488    assertTrue(Bytes.equals(ROW_2, results[1].getRow()));
489    assertTrue(Bytes.equals(ROW_3, results[2].getRow()));
490    assertTrue(Bytes.equals(ROW_4, results[3].getRow()));
491    scanner.close();
492    assertTrue(remoteTable.isAutoFlush());
493  }
494
495  @Test
496  public void testCheckAndDelete() throws IOException {
497    Get get = new Get(ROW_1);
498    Result result = remoteTable.get(get);
499    byte[] value1 = result.getValue(COLUMN_1, QUALIFIER_1);
500    byte[] value2 = result.getValue(COLUMN_2, QUALIFIER_2);
501    assertNotNull(value1);
502    assertTrue(Bytes.equals(VALUE_1, value1));
503    assertNull(value2);
504    assertTrue(remoteTable.exists(get));
505    assertEquals(1, remoteTable.existsAll(Collections.singletonList(get)).length);
506    Delete delete = new Delete(ROW_1);
507
508    remoteTable.checkAndMutate(ROW_1, COLUMN_1).qualifier(QUALIFIER_1).ifEquals(VALUE_1)
509      .thenDelete(delete);
510    assertFalse(remoteTable.exists(get));
511
512    Put put = new Put(ROW_1);
513    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
514    remoteTable.put(put);
515
516    assertTrue(remoteTable.checkAndMutate(ROW_1, COLUMN_1).qualifier(QUALIFIER_1).ifEquals(VALUE_1)
517      .thenPut(put));
518    assertFalse(remoteTable.checkAndMutate(ROW_1, COLUMN_1).qualifier(QUALIFIER_1).ifEquals(VALUE_2)
519      .thenPut(put));
520  }
521
522  /**
523   * Test RemoteHable.Scanner.iterator method
524   */
525  @Test
526  public void testIteratorScaner() throws IOException {
527    List<Put> puts = new ArrayList<>(4);
528    Put put = new Put(ROW_1);
529    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
530    puts.add(put);
531    put = new Put(ROW_2);
532    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
533    puts.add(put);
534    put = new Put(ROW_3);
535    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
536    puts.add(put);
537    put = new Put(ROW_4);
538    put.addColumn(COLUMN_1, QUALIFIER_1, VALUE_1);
539    puts.add(put);
540    remoteTable.put(puts);
541
542    ResultScanner scanner = remoteTable.getScanner(new Scan());
543    Iterator<Result> iterator = scanner.iterator();
544    assertTrue(iterator.hasNext());
545    int counter = 0;
546    while (iterator.hasNext()) {
547      iterator.next();
548      counter++;
549    }
550    assertEquals(4, counter);
551  }
552
553  /**
554   * Test a some methods of class Response.
555   */
556  @Test
557  public void testResponse() {
558    Response response = new Response(200);
559    assertEquals(200, response.getCode());
560    Header[] headers = new Header[2];
561    headers[0] = new BasicHeader("header1", "value1");
562    headers[1] = new BasicHeader("header2", "value2");
563    response = new Response(200, headers);
564    assertEquals("value1", response.getHeader("header1"));
565    assertFalse(response.hasBody());
566    response.setCode(404);
567    assertEquals(404, response.getCode());
568    headers = new Header[2];
569    headers[0] = new BasicHeader("header1", "value1.1");
570    headers[1] = new BasicHeader("header2", "value2");
571    response.setHeaders(headers);
572    assertEquals("value1.1", response.getHeader("header1"));
573    response.setBody(Bytes.toBytes("body"));
574    assertTrue(response.hasBody());
575  }
576
577  /**
578   * Tests keeping a HBase scanner alive for long periods of time. Each call to next() should reset
579   * the ConnectionCache timeout for the scanner's connection.
580   * @throws Exception if starting the servlet container or disabling or truncating the table fails
581   */
582  @Test
583  public void testLongLivedScan() throws Exception {
584    int numTrials = 6;
585    int trialPause = 1000;
586    int cleanUpInterval = 100;
587
588    // Shutdown the Rest Servlet container
589    REST_TEST_UTIL.shutdownServletContainer();
590
591    // Set the ConnectionCache timeout to trigger halfway through the trials
592    TEST_UTIL.getConfiguration().setLong(RESTServlet.MAX_IDLETIME, (numTrials / 2) * trialPause);
593    TEST_UTIL.getConfiguration().setLong(RESTServlet.CLEANUP_INTERVAL, cleanUpInterval);
594
595    // Start the Rest Servlet container
596    REST_TEST_UTIL.startServletContainer(TEST_UTIL.getConfiguration());
597
598    // Truncate the test table for inserting test scenarios rows keys
599    TEST_UTIL.getHBaseAdmin().disableTable(TABLE);
600    TEST_UTIL.getHBaseAdmin().truncateTable(TABLE, false);
601
602    remoteTable =
603      new RemoteHTable(new Client(new Cluster().add("localhost", REST_TEST_UTIL.getServletPort())),
604        TEST_UTIL.getConfiguration(), TABLE.toBytes());
605
606    String row = "testrow";
607
608    try (Table table = TEST_UTIL.getConnection().getTable(TABLE)) {
609      List<Put> puts = new ArrayList<Put>();
610      Put put = null;
611      for (int i = 1; i <= numTrials; i++) {
612        put = new Put(Bytes.toBytes(row + i));
613        put.addColumn(COLUMN_1, QUALIFIER_1, TS_2, Bytes.toBytes("testvalue" + i));
614        puts.add(put);
615      }
616      table.put(puts);
617    }
618
619    Scan scan = new Scan();
620    scan.setCaching(1);
621    scan.setBatch(1);
622
623    ResultScanner scanner = remoteTable.getScanner(scan);
624    Result result = null;
625    // get scanner and rows
626    for (int i = 1; i <= numTrials; i++) {
627      // Make sure that the Scanner doesn't throw an exception after the ConnectionCache timeout
628      result = scanner.next();
629      assertEquals(row + i, Bytes.toString(result.getRow()));
630      Thread.sleep(trialPause);
631    }
632  }
633}