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