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