View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.rest;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertTrue;
24  
25  import java.io.ByteArrayInputStream;
26  import java.io.IOException;
27  import java.io.StringWriter;
28  import java.net.URLEncoder;
29  import java.util.List;
30  
31  import javax.xml.bind.JAXBContext;
32  import javax.xml.bind.JAXBException;
33  import javax.xml.bind.Marshaller;
34  import javax.xml.bind.Unmarshaller;
35  
36  import org.apache.commons.httpclient.Header;
37  import org.apache.hadoop.conf.Configuration;
38  import org.apache.hadoop.hbase.CompatibilityFactory;
39  import org.apache.hadoop.hbase.HBaseTestingUtility;
40  import org.apache.hadoop.hbase.HColumnDescriptor;
41  import org.apache.hadoop.hbase.HConstants;
42  import org.apache.hadoop.hbase.HTableDescriptor;
43  import org.apache.hadoop.hbase.MediumTests;
44  import org.apache.hadoop.hbase.TableName;
45  import org.apache.hadoop.hbase.client.HBaseAdmin;
46  import org.apache.hadoop.hbase.rest.client.Client;
47  import org.apache.hadoop.hbase.rest.client.Cluster;
48  import org.apache.hadoop.hbase.rest.client.Response;
49  import org.apache.hadoop.hbase.rest.model.CellModel;
50  import org.apache.hadoop.hbase.rest.model.CellSetModel;
51  import org.apache.hadoop.hbase.rest.model.RowModel;
52  import org.apache.hadoop.hbase.security.User;
53  import org.apache.hadoop.hbase.test.MetricsAssertHelper;
54  import org.apache.hadoop.hbase.util.Bytes;
55  import org.apache.hadoop.security.UserGroupInformation;
56  import org.junit.After;
57  import org.junit.AfterClass;
58  import org.junit.Before;
59  import org.junit.BeforeClass;
60  import org.junit.Test;
61  import org.junit.experimental.categories.Category;
62  
63  @Category(MediumTests.class)
64  public class TestRowResource {
65    private static final String TABLE = "TestRowResource";
66    private static final String CFA = "a";
67    private static final String CFB = "b";
68    private static final String COLUMN_1 = CFA + ":1";
69    private static final String COLUMN_2 = CFB + ":2";
70    private static final String COLUMN_3 = CFA + ":";
71    private static final String ROW_1 = "testrow1";
72    private static final String VALUE_1 = "testvalue1";
73    private static final String ROW_2 = "testrow2";
74    private static final String VALUE_2 = "testvalue2";
75    private static final String ROW_3 = "testrow3";
76    private static final String VALUE_3 = "testvalue3";
77    private static final String ROW_4 = "testrow4";
78    private static final String VALUE_4 = "testvalue4";
79  
80    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
81    private static final HBaseRESTTestingUtility REST_TEST_UTIL =
82      new HBaseRESTTestingUtility();
83    private static final MetricsAssertHelper METRICS_ASSERT =
84        CompatibilityFactory.getInstance(MetricsAssertHelper.class);
85    private static Client client;
86    private static JAXBContext context;
87    private static Marshaller marshaller;
88    private static Unmarshaller unmarshaller;
89    private static Configuration conf;
90  
91    @BeforeClass
92    public static void setUpBeforeClass() throws Exception {
93      conf = TEST_UTIL.getConfiguration();
94      TEST_UTIL.startMiniCluster(3);
95      REST_TEST_UTIL.startServletContainer(conf);
96      context = JAXBContext.newInstance(
97          CellModel.class,
98          CellSetModel.class,
99          RowModel.class);
100     marshaller = context.createMarshaller();
101     unmarshaller = context.createUnmarshaller();
102     client = new Client(new Cluster().add("localhost", 
103       REST_TEST_UTIL.getServletPort()));
104   }
105 
106   @Before
107   public void beforeMethod() throws Exception {
108     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
109     if (admin.tableExists(TABLE)) {
110       return;
111     }
112     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(TABLE));
113     htd.addFamily(new HColumnDescriptor(CFA));
114     htd.addFamily(new HColumnDescriptor(CFB));
115     admin.createTable(htd);
116   }
117 
118   @After
119   public void afterMethod() throws Exception {
120     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
121     if (admin.tableExists(TABLE)) {
122       admin.disableTable(TABLE);
123       admin.deleteTable(TABLE);
124     }
125   }
126 
127   @AfterClass
128   public static void tearDownAfterClass() throws Exception {
129     REST_TEST_UTIL.shutdownServletContainer();
130     TEST_UTIL.shutdownMiniCluster();
131   }
132 
133   private static Response deleteRow(String table, String row)
134       throws IOException {
135     StringBuilder path = new StringBuilder();
136     path.append('/');
137     path.append(table);
138     path.append('/');
139     path.append(row);
140     Response response = client.delete(path.toString());
141     Thread.yield();
142     return response;
143   }
144 
145   private static Response deleteValue(String table, String row, String column)
146       throws IOException {
147     StringBuilder path = new StringBuilder();
148     path.append('/');
149     path.append(table);
150     path.append('/');
151     path.append(row);
152     path.append('/');
153     path.append(column);
154     Response response = client.delete(path.toString());
155     Thread.yield();
156     return response;
157   }
158 
159   private static Response getValueXML(String table, String row, String column)
160       throws IOException {
161     StringBuilder path = new StringBuilder();
162     path.append('/');
163     path.append(table);
164     path.append('/');
165     path.append(row);
166     path.append('/');
167     path.append(column);
168     return getValueXML(path.toString());
169   }
170 
171   private static Response getValueXML(String table, String startRow,
172       String endRow, String column) throws IOException {
173     StringBuilder path = new StringBuilder();
174     path.append('/');
175     path.append(table);
176     path.append('/');
177     path.append(startRow);
178     path.append(",");
179     path.append(endRow);
180     path.append('/');
181     path.append(column);
182     return getValueXML(path.toString());
183   }
184 
185   private static Response getValueXML(String url) throws IOException {
186     Response response = client.get(url, Constants.MIMETYPE_XML);
187     return response;
188   }
189 
190   private static Response getValuePB(String table, String row, String column) 
191       throws IOException {
192     StringBuilder path = new StringBuilder();
193     path.append('/');
194     path.append(table);
195     path.append('/');
196     path.append(row);
197     path.append('/');
198     path.append(column);
199     return getValuePB(path.toString());
200   }
201 
202   private static Response getValuePB(String url) throws IOException {
203     Response response = client.get(url, Constants.MIMETYPE_PROTOBUF);
204     return response;
205   }
206 
207   private static Response putValueXML(String table, String row, String column,
208       String value) throws IOException, JAXBException {
209     StringBuilder path = new StringBuilder();
210     path.append('/');
211     path.append(table);
212     path.append('/');
213     path.append(row);
214     path.append('/');
215     path.append(column);
216     return putValueXML(path.toString(), table, row, column, value);
217   }
218 
219   private static Response putValueXML(String url, String table, String row,
220       String column, String value) throws IOException, JAXBException {
221     RowModel rowModel = new RowModel(row);
222     rowModel.addCell(new CellModel(Bytes.toBytes(column),
223       Bytes.toBytes(value)));
224     CellSetModel cellSetModel = new CellSetModel();
225     cellSetModel.addRow(rowModel);
226     StringWriter writer = new StringWriter();
227     marshaller.marshal(cellSetModel, writer);
228     Response response = client.put(url, Constants.MIMETYPE_XML,
229       Bytes.toBytes(writer.toString()));
230     Thread.yield();
231     return response;
232   }
233 
234   private static void checkValueXML(String table, String row, String column,
235       String value) throws IOException, JAXBException {
236     Response response = getValueXML(table, row, column);
237     assertEquals(response.getCode(), 200);
238     assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
239     CellSetModel cellSet = (CellSetModel)
240       unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
241     RowModel rowModel = cellSet.getRows().get(0);
242     CellModel cell = rowModel.getCells().get(0);
243     assertEquals(Bytes.toString(cell.getColumn()), column);
244     assertEquals(Bytes.toString(cell.getValue()), value);
245   }
246 
247   private static void checkValueXML(String url, String table, String row,
248       String column, String value) throws IOException, JAXBException {
249     Response response = getValueXML(url);
250     assertEquals(response.getCode(), 200);
251     assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
252     CellSetModel cellSet = (CellSetModel)
253       unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
254     RowModel rowModel = cellSet.getRows().get(0);
255     CellModel cell = rowModel.getCells().get(0);
256     assertEquals(Bytes.toString(cell.getColumn()), column);
257     assertEquals(Bytes.toString(cell.getValue()), value);
258   }
259 
260   private static Response putValuePB(String table, String row, String column,
261       String value) throws IOException {
262     StringBuilder path = new StringBuilder();
263     path.append('/');
264     path.append(table);
265     path.append('/');
266     path.append(row);
267     path.append('/');
268     path.append(column);
269     return putValuePB(path.toString(), table, row, column, value);
270   }
271 
272   private static Response putValuePB(String url, String table, String row,
273       String column, String value) throws IOException {
274     RowModel rowModel = new RowModel(row);
275     rowModel.addCell(new CellModel(Bytes.toBytes(column),
276       Bytes.toBytes(value)));
277     CellSetModel cellSetModel = new CellSetModel();
278     cellSetModel.addRow(rowModel);
279     Response response = client.put(url, Constants.MIMETYPE_PROTOBUF,
280       cellSetModel.createProtobufOutput());
281     Thread.yield();
282     return response;
283   }
284 
285   private static void checkValuePB(String table, String row, String column, 
286       String value) throws IOException {
287     Response response = getValuePB(table, row, column);
288     assertEquals(response.getCode(), 200);
289     assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type"));
290     CellSetModel cellSet = new CellSetModel();
291     cellSet.getObjectFromMessage(response.getBody());
292     RowModel rowModel = cellSet.getRows().get(0);
293     CellModel cell = rowModel.getCells().get(0);
294     assertEquals(Bytes.toString(cell.getColumn()), column);
295     assertEquals(Bytes.toString(cell.getValue()), value);
296   }
297 
298   private static Response checkAndPutValuePB(String url, String table,
299       String row, String column, String valueToCheck, String valueToPut)
300         throws IOException {
301     RowModel rowModel = new RowModel(row);
302     rowModel.addCell(new CellModel(Bytes.toBytes(column),
303       Bytes.toBytes(valueToPut)));
304     rowModel.addCell(new CellModel(Bytes.toBytes(column),
305       Bytes.toBytes(valueToCheck)));
306     CellSetModel cellSetModel = new CellSetModel();
307     cellSetModel.addRow(rowModel);
308     Response response = client.put(url, Constants.MIMETYPE_PROTOBUF,
309       cellSetModel.createProtobufOutput());
310     Thread.yield();
311     return response;
312   }
313 
314   private static Response checkAndPutValuePB(String table, String row,
315       String column, String valueToCheck, String valueToPut) throws IOException {
316     StringBuilder path = new StringBuilder();
317     path.append('/');
318     path.append(table);
319     path.append('/');
320     path.append(row);
321     path.append("?check=put");
322     return checkAndPutValuePB(path.toString(), table, row, column,
323       valueToCheck, valueToPut);
324   }
325 
326   private static Response checkAndPutValueXML(String url, String table,
327       String row, String column, String valueToCheck, String valueToPut)
328         throws IOException, JAXBException {
329     RowModel rowModel = new RowModel(row);
330     rowModel.addCell(new CellModel(Bytes.toBytes(column),
331       Bytes.toBytes(valueToPut)));
332     rowModel.addCell(new CellModel(Bytes.toBytes(column),
333       Bytes.toBytes(valueToCheck)));
334     CellSetModel cellSetModel = new CellSetModel();
335     cellSetModel.addRow(rowModel);
336     StringWriter writer = new StringWriter();
337     marshaller.marshal(cellSetModel, writer);
338     Response response = client.put(url, Constants.MIMETYPE_XML,
339       Bytes.toBytes(writer.toString()));
340     Thread.yield();
341     return response;
342   }
343 
344   private static Response checkAndPutValueXML(String table, String row,
345       String column, String valueToCheck, String valueToPut)
346         throws IOException, JAXBException {
347     StringBuilder path = new StringBuilder();
348     path.append('/');
349     path.append(table);
350     path.append('/');
351     path.append(row);
352     path.append("?check=put");
353     return checkAndPutValueXML(path.toString(), table, row, column,
354       valueToCheck, valueToPut);
355   }
356 
357   private static Response checkAndDeleteXML(String url, String table,
358       String row, String column, String valueToCheck)
359         throws IOException, JAXBException {
360     RowModel rowModel = new RowModel(row);
361     rowModel.addCell(new CellModel(Bytes.toBytes(column),
362       Bytes.toBytes(valueToCheck)));
363     CellSetModel cellSetModel = new CellSetModel();
364     cellSetModel.addRow(rowModel);
365     StringWriter writer = new StringWriter();
366     marshaller.marshal(cellSetModel, writer);
367     Response response = client.put(url, Constants.MIMETYPE_XML,
368       Bytes.toBytes(writer.toString()));
369     Thread.yield();
370     return response;
371   }
372 
373   private static Response checkAndDeleteXML(String table, String row,
374       String column, String valueToCheck) throws IOException, JAXBException {
375     StringBuilder path = new StringBuilder();
376     path.append('/');
377     path.append(table);
378     path.append('/');
379     path.append(row);
380     path.append("?check=delete");
381     return checkAndDeleteXML(path.toString(), table, row, column, valueToCheck);
382   }
383 
384   private static Response checkAndDeletePB(String table, String row,
385       String column, String value) throws IOException {
386     StringBuilder path = new StringBuilder();
387     path.append('/');
388     path.append(table);
389     path.append('/');
390     path.append(row);
391     path.append("?check=delete");
392     return checkAndDeleteValuePB(path.toString(), table, row, column, value);
393   }
394 
395   private static Response checkAndDeleteValuePB(String url, String table,
396       String row, String column, String valueToCheck)
397       throws IOException {
398     RowModel rowModel = new RowModel(row);
399     rowModel.addCell(new CellModel(Bytes.toBytes(column), Bytes
400         .toBytes(valueToCheck)));
401     CellSetModel cellSetModel = new CellSetModel();
402     cellSetModel.addRow(rowModel);
403     Response response = client.put(url, Constants.MIMETYPE_PROTOBUF,
404         cellSetModel.createProtobufOutput());
405     Thread.yield();
406     return response;
407   }
408 
409   @Test
410   public void testDelete() throws IOException, JAXBException {
411     Response response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
412     assertEquals(response.getCode(), 200);
413     response = putValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
414     assertEquals(response.getCode(), 200);
415     checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
416     checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
417 
418     response = deleteValue(TABLE, ROW_1, COLUMN_1);
419     assertEquals(response.getCode(), 200);
420     response = getValueXML(TABLE, ROW_1, COLUMN_1);
421     assertEquals(response.getCode(), 404);
422     checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
423 
424     response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
425     assertEquals(response.getCode(), 200);
426     response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
427     assertEquals(response.getCode(), 200);
428     response = getValueXML(TABLE, ROW_1, COLUMN_1);
429     assertEquals(response.getCode(), 404);
430 
431     response = deleteRow(TABLE, ROW_1);
432     assertEquals(response.getCode(), 200);    
433     response = getValueXML(TABLE, ROW_1, COLUMN_1);
434     assertEquals(response.getCode(), 404);
435     response = getValueXML(TABLE, ROW_1, COLUMN_2);
436     assertEquals(response.getCode(), 404);
437   }
438 
439   @Test
440   public void testForbidden() throws IOException, JAXBException {
441     conf.set("hbase.rest.readonly", "true");
442 
443     Response response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
444     assertEquals(response.getCode(), 403);
445     response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
446     assertEquals(response.getCode(), 403);
447     response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
448     assertEquals(response.getCode(), 403);
449     response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
450     assertEquals(response.getCode(), 403);
451     response = deleteValue(TABLE, ROW_1, COLUMN_1);
452     assertEquals(response.getCode(), 403);
453     response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
454     assertEquals(response.getCode(), 403);
455     response = deleteRow(TABLE, ROW_1);
456     assertEquals(response.getCode(), 403);
457 
458     conf.set("hbase.rest.readonly", "false");
459 
460     response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
461     assertEquals(response.getCode(), 200);
462     response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
463     assertEquals(response.getCode(), 200);
464     response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
465     assertEquals(response.getCode(), 200);
466     response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
467     assertEquals(response.getCode(), 200);
468     response = deleteValue(TABLE, ROW_1, COLUMN_1);
469     assertEquals(response.getCode(), 200);
470     response = deleteRow(TABLE, ROW_1);
471     assertEquals(response.getCode(), 200);
472   }
473 
474   @Test
475   public void testSingleCellGetPutXML() throws IOException, JAXBException {
476     Response response = getValueXML(TABLE, ROW_1, COLUMN_1);
477     assertEquals(response.getCode(), 404);
478 
479     response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
480     assertEquals(response.getCode(), 200);
481     checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
482     response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
483     assertEquals(response.getCode(), 200);
484     checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
485     response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
486     assertEquals(response.getCode(), 200);
487     checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_3);
488     response = checkAndDeleteXML(TABLE, ROW_1, COLUMN_1, VALUE_3);
489     assertEquals(response.getCode(), 200);
490 
491     response = deleteRow(TABLE, ROW_1);
492     assertEquals(response.getCode(), 200);
493   }
494 
495   @Test
496   public void testSingleCellGetPutPB() throws IOException, JAXBException {
497     Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
498     assertEquals(response.getCode(), 404);
499 
500     response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
501     assertEquals(response.getCode(), 200);
502     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
503 
504     response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
505     assertEquals(response.getCode(), 200);
506     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
507     response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
508     assertEquals(response.getCode(), 200);
509     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2);
510 
511     response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
512     assertEquals(response.getCode(), 200);
513     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_3);
514     response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_3, VALUE_4);
515     assertEquals(response.getCode(), 200);
516     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_4);
517 
518     response = deleteRow(TABLE, ROW_1);
519     assertEquals(response.getCode(), 200);
520   }
521 
522   @Test
523   public void testSingleCellGetPutBinary() throws IOException {
524     final String path = "/" + TABLE + "/" + ROW_3 + "/" + COLUMN_1;
525     final byte[] body = Bytes.toBytes(VALUE_3);
526     Response response = client.put(path, Constants.MIMETYPE_BINARY, body);
527     assertEquals(response.getCode(), 200);
528     Thread.yield();
529 
530     response = client.get(path, Constants.MIMETYPE_BINARY);
531     assertEquals(response.getCode(), 200);
532     assertEquals(Constants.MIMETYPE_BINARY, response.getHeader("content-type"));
533     assertTrue(Bytes.equals(response.getBody(), body));
534     boolean foundTimestampHeader = false;
535     for (Header header: response.getHeaders()) {
536       if (header.getName().equals("X-Timestamp")) {
537         foundTimestampHeader = true;
538         break;
539       }
540     }
541     assertTrue(foundTimestampHeader);
542 
543     response = deleteRow(TABLE, ROW_3);
544     assertEquals(response.getCode(), 200);
545   }
546 
547   @Test
548   public void testSingleCellGetJSON() throws IOException, JAXBException {
549     final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
550     Response response = client.put(path, Constants.MIMETYPE_BINARY,
551       Bytes.toBytes(VALUE_4));
552     assertEquals(response.getCode(), 200);
553     Thread.yield();
554     response = client.get(path, Constants.MIMETYPE_JSON);
555     assertEquals(response.getCode(), 200);
556     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
557     response = deleteRow(TABLE, ROW_4);
558     assertEquals(response.getCode(), 200);
559   }
560 
561   @Test
562   public void testMetrics() throws IOException, JAXBException {
563     final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
564     Response response = client.put(path, Constants.MIMETYPE_BINARY,
565         Bytes.toBytes(VALUE_4));
566     assertEquals(response.getCode(), 200);
567     Thread.yield();
568     response = client.get(path, Constants.MIMETYPE_JSON);
569     assertEquals(response.getCode(), 200);
570     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
571     response = deleteRow(TABLE, ROW_4);
572     assertEquals(response.getCode(), 200);
573 
574     UserGroupInformation ugi = User.getCurrent().getUGI();
575     METRICS_ASSERT.assertCounterGt("requests", 2l,
576       RESTServlet.getInstance(conf, ugi).getMetrics().getSource());
577 
578     METRICS_ASSERT.assertCounterGt("successfulGet", 0l,
579       RESTServlet.getInstance(conf, ugi).getMetrics().getSource());
580 
581     METRICS_ASSERT.assertCounterGt("successfulPut", 0l,
582       RESTServlet.getInstance(conf, ugi).getMetrics().getSource());
583 
584     METRICS_ASSERT.assertCounterGt("successfulDelete", 0l,
585       RESTServlet.getInstance(conf, ugi).getMetrics().getSource());
586   }
587 
588   @Test
589   public void testURLEncodedKey() throws IOException, JAXBException {
590     String urlKey = "http://example.com/foo";
591     StringBuilder path = new StringBuilder();
592     path.append('/');
593     path.append(TABLE);
594     path.append('/');
595     path.append(URLEncoder.encode(urlKey, HConstants.UTF8_ENCODING));
596     path.append('/');
597     path.append(COLUMN_1);
598     Response response;
599     response = putValueXML(path.toString(), TABLE, urlKey, COLUMN_1,
600       VALUE_1);
601     assertEquals(response.getCode(), 200);
602     checkValueXML(path.toString(), TABLE, urlKey, COLUMN_1, VALUE_1);
603   }
604 
605   @Test
606   public void testNoSuchCF() throws IOException, JAXBException {
607     final String goodPath = "/" + TABLE + "/" + ROW_1 + "/" + CFA+":";
608     final String badPath = "/" + TABLE + "/" + ROW_1 + "/" + "BAD";
609     Response response = client.post(goodPath, Constants.MIMETYPE_BINARY,
610       Bytes.toBytes(VALUE_1));
611     assertEquals(response.getCode(), 200);
612     assertEquals(client.get(goodPath, Constants.MIMETYPE_BINARY).getCode(),
613       200);
614     assertEquals(client.get(badPath, Constants.MIMETYPE_BINARY).getCode(),
615       404);
616     assertEquals(client.get(goodPath, Constants.MIMETYPE_BINARY).getCode(),
617       200);
618   }
619 
620   @Test
621   public void testMultiCellGetPutXML() throws IOException, JAXBException {
622     String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
623 
624     CellSetModel cellSetModel = new CellSetModel();
625     RowModel rowModel = new RowModel(ROW_1);
626     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
627       Bytes.toBytes(VALUE_1)));
628     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
629       Bytes.toBytes(VALUE_2)));
630     cellSetModel.addRow(rowModel);
631     rowModel = new RowModel(ROW_2);
632     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
633       Bytes.toBytes(VALUE_3)));
634     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
635       Bytes.toBytes(VALUE_4)));
636     cellSetModel.addRow(rowModel);
637     StringWriter writer = new StringWriter();
638     marshaller.marshal(cellSetModel, writer);
639     Response response = client.put(path, Constants.MIMETYPE_XML,
640       Bytes.toBytes(writer.toString()));
641     Thread.yield();
642 
643     // make sure the fake row was not actually created
644     response = client.get(path, Constants.MIMETYPE_XML);
645     assertEquals(response.getCode(), 404);
646 
647     // check that all of the values were created
648     checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
649     checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
650     checkValueXML(TABLE, ROW_2, COLUMN_1, VALUE_3);
651     checkValueXML(TABLE, ROW_2, COLUMN_2, VALUE_4);
652 
653     response = deleteRow(TABLE, ROW_1);
654     assertEquals(response.getCode(), 200);
655     response = deleteRow(TABLE, ROW_2);
656     assertEquals(response.getCode(), 200);
657   }
658 
659   @Test
660   public void testMultiCellGetPutPB() throws IOException {
661     String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
662 
663     CellSetModel cellSetModel = new CellSetModel();
664     RowModel rowModel = new RowModel(ROW_1);
665     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
666       Bytes.toBytes(VALUE_1)));
667     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
668       Bytes.toBytes(VALUE_2)));
669     cellSetModel.addRow(rowModel);
670     rowModel = new RowModel(ROW_2);
671     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
672       Bytes.toBytes(VALUE_3)));
673     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
674       Bytes.toBytes(VALUE_4)));
675     cellSetModel.addRow(rowModel);
676     Response response = client.put(path, Constants.MIMETYPE_PROTOBUF,
677       cellSetModel.createProtobufOutput());
678     Thread.yield();
679 
680     // make sure the fake row was not actually created
681     response = client.get(path, Constants.MIMETYPE_PROTOBUF);
682     assertEquals(response.getCode(), 404);
683 
684     // check that all of the values were created
685     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
686     checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
687     checkValuePB(TABLE, ROW_2, COLUMN_1, VALUE_3);
688     checkValuePB(TABLE, ROW_2, COLUMN_2, VALUE_4);
689 
690     response = deleteRow(TABLE, ROW_1);
691     assertEquals(response.getCode(), 200);
692     response = deleteRow(TABLE, ROW_2);
693     assertEquals(response.getCode(), 200);
694   }
695 
696   @Test
697   public void testStartEndRowGetPutXML() throws IOException, JAXBException {
698     String[] rows = { ROW_1, ROW_2, ROW_3 };
699     String[] values = { VALUE_1, VALUE_2, VALUE_3 }; 
700     Response response = null;
701     for (int i = 0; i < rows.length; i++) {
702       response = putValueXML(TABLE, rows[i], COLUMN_1, values[i]);
703       assertEquals(200, response.getCode());
704       checkValueXML(TABLE, rows[i], COLUMN_1, values[i]);
705     }
706     response = getValueXML(TABLE, rows[0], rows[2], COLUMN_1);
707     assertEquals(200, response.getCode());
708     CellSetModel cellSet = (CellSetModel)
709       unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
710     assertEquals(2, cellSet.getRows().size());
711     for (int i = 0; i < cellSet.getRows().size()-1; i++) {
712       RowModel rowModel = cellSet.getRows().get(i);
713       for (CellModel cell: rowModel.getCells()) {
714         assertEquals(COLUMN_1, Bytes.toString(cell.getColumn()));
715         assertEquals(values[i], Bytes.toString(cell.getValue()));
716       }
717     }
718     for (String row : rows) {
719       response = deleteRow(TABLE, row);
720       assertEquals(200, response.getCode());
721     }
722   }
723 
724   @Test
725   public void testInvalidCheckParam() throws IOException, JAXBException {
726     CellSetModel cellSetModel = new CellSetModel();
727     RowModel rowModel = new RowModel(ROW_1);
728     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
729       Bytes.toBytes(VALUE_1)));
730     cellSetModel.addRow(rowModel);
731     StringWriter writer = new StringWriter();
732     marshaller.marshal(cellSetModel, writer);
733 
734     final String path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1 + "?check=blah";
735 
736     Response response = client.put(path, Constants.MIMETYPE_XML,
737       Bytes.toBytes(writer.toString()));
738     assertEquals(response.getCode(), 400);
739   }
740 
741   @Test
742   public void testLatestCellGetXML() throws IOException, JAXBException {
743     final String path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1;
744     CellSetModel cellSetModel = new CellSetModel();
745     RowModel rowModel = new RowModel(ROW_1);
746     CellModel cellOne = new CellModel(Bytes.toBytes(COLUMN_1), 1L, Bytes.toBytes(VALUE_1));
747     CellModel cellTwo = new CellModel(Bytes.toBytes(COLUMN_1), 2L, Bytes.toBytes(VALUE_2));
748     rowModel.addCell(cellOne);
749     rowModel.addCell(cellTwo);
750     cellSetModel.addRow(rowModel);
751     StringWriter writer = new StringWriter();
752     marshaller.marshal(cellSetModel, writer);
753     Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
754     assertEquals(response.getCode(), 200);
755     response = getValueXML(TABLE, ROW_1, COLUMN_1);
756     assertEquals(response.getCode(), 200);
757     assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
758     CellSetModel cellSet = (CellSetModel) unmarshaller.unmarshal(new ByteArrayInputStream(response
759         .getBody()));
760     assertTrue(cellSet.getRows().size() == 1);
761     assertTrue(cellSet.getRows().get(0).getCells().size() == 1);
762     CellModel cell = cellSet.getRows().get(0).getCells().get(0);
763     assertEquals(VALUE_2, Bytes.toString(cell.getValue()));
764     assertEquals(2L, cell.getTimestamp());
765     response = deleteRow(TABLE, ROW_1);
766     assertEquals(response.getCode(), 200);
767   }
768 
769   @Test
770   public void testMultiColumnGetXML() throws Exception {
771     String path = "/" + TABLE + "/fakerow";
772     CellSetModel cellSetModel = new CellSetModel();
773     RowModel rowModel = new RowModel(ROW_1);
774     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
775     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
776     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_3), Bytes.toBytes(VALUE_2)));
777     cellSetModel.addRow(rowModel);
778     StringWriter writer = new StringWriter();
779     marshaller.marshal(cellSetModel, writer);
780 
781     Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
782     Thread.yield();
783 
784     // make sure the fake row was not actually created
785     response = client.get(path, Constants.MIMETYPE_XML);
786     assertEquals(response.getCode(), 404);
787 
788     // Try getting all the column values at once.
789     path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1 + "," + COLUMN_2 + "," + COLUMN_3;
790     response = client.get(path, Constants.MIMETYPE_XML);
791     assertEquals(200, response.getCode());
792     CellSetModel cellSet = (CellSetModel) unmarshaller.unmarshal(new ByteArrayInputStream(response
793         .getBody()));
794     assertTrue(cellSet.getRows().size() == 1);
795     assertTrue(cellSet.getRows().get(0).getCells().size() == 3);
796     List<CellModel> cells = cellSet.getRows().get(0).getCells();
797 
798     assertTrue(containsCellModel(cells, COLUMN_1, VALUE_1));
799     assertTrue(containsCellModel(cells, COLUMN_2, VALUE_2));
800     assertTrue(containsCellModel(cells, COLUMN_3, VALUE_2));
801     response = deleteRow(TABLE, ROW_1);
802     assertEquals(response.getCode(), 200);
803   }
804 
805   private boolean containsCellModel(List<CellModel> cells, String column, String value) {
806     boolean contains = false;
807     for (CellModel cell : cells) {
808       if (Bytes.toString(cell.getColumn()).equals(column)
809           && Bytes.toString(cell.getValue()).equals(value)) {
810         contains = true;
811         return contains;
812       }
813     }
814     return contains;
815   }
816 }
817