View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.rest;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.io.ByteArrayInputStream;
25  import java.io.IOException;
26  import java.io.StringWriter;
27  import java.net.URLEncoder;
28  import java.util.List;
29  
30  import javax.xml.bind.JAXBException;
31  
32  import org.apache.commons.httpclient.Header;
33  import org.apache.hadoop.hbase.CompatibilityFactory;
34  import org.apache.hadoop.hbase.HConstants;
35  import org.apache.hadoop.hbase.testclassification.MediumTests;
36  import org.apache.hadoop.hbase.rest.client.Response;
37  import org.apache.hadoop.hbase.rest.model.CellModel;
38  import org.apache.hadoop.hbase.rest.model.CellSetModel;
39  import org.apache.hadoop.hbase.rest.model.RowModel;
40  import org.apache.hadoop.hbase.security.UserProvider;
41  import org.apache.hadoop.hbase.test.MetricsAssertHelper;
42  import org.apache.hadoop.hbase.util.Bytes;
43  import org.junit.Test;
44  import org.junit.experimental.categories.Category;
45  
46  @Category(MediumTests.class)
47  public class TestGetAndPutResource extends RowResourceBase {
48  
49    private static final MetricsAssertHelper METRICS_ASSERT =
50        CompatibilityFactory.getInstance(MetricsAssertHelper.class);
51  
52    @Test
53    public void testForbidden() throws IOException, JAXBException {
54      conf.set("hbase.rest.readonly", "true");
55  
56      Response response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
57      assertEquals(response.getCode(), 403);
58      response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
59      assertEquals(response.getCode(), 403);
60      response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
61      assertEquals(response.getCode(), 403);
62      response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
63      assertEquals(response.getCode(), 403);
64      response = deleteValue(TABLE, ROW_1, COLUMN_1);
65      assertEquals(response.getCode(), 403);
66      response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
67      assertEquals(response.getCode(), 403);
68      response = deleteRow(TABLE, ROW_1);
69      assertEquals(response.getCode(), 403);
70  
71      conf.set("hbase.rest.readonly", "false");
72  
73      response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
74      assertEquals(response.getCode(), 200);
75      response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
76      assertEquals(response.getCode(), 200);
77      response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
78      assertEquals(response.getCode(), 200);
79      response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
80      assertEquals(response.getCode(), 200);
81      response = deleteValue(TABLE, ROW_1, COLUMN_1);
82      assertEquals(response.getCode(), 200);
83      response = deleteRow(TABLE, ROW_1);
84      assertEquals(response.getCode(), 200);
85    }
86  
87    @Test
88    public void testSingleCellGetPutXML() throws IOException, JAXBException {
89      Response response = getValueXML(TABLE, ROW_1, COLUMN_1);
90      assertEquals(response.getCode(), 404);
91  
92      response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
93      assertEquals(response.getCode(), 200);
94      checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
95      response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
96      assertEquals(response.getCode(), 200);
97      checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
98      response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
99      assertEquals(response.getCode(), 200);
100     checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_3);
101     response = checkAndDeleteXML(TABLE, ROW_1, COLUMN_1, VALUE_3);
102     assertEquals(response.getCode(), 200);
103 
104     response = deleteRow(TABLE, ROW_1);
105     assertEquals(response.getCode(), 200);
106   }
107 
108   @Test
109   public void testSingleCellGetPutPB() throws IOException, JAXBException {
110     Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
111     assertEquals(response.getCode(), 404);
112     
113     response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
114     assertEquals(response.getCode(), 200);
115     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
116     response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
117     assertEquals(response.getCode(), 200);
118     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2);
119 
120     response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
121     assertEquals(response.getCode(), 200);
122     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_3);
123     response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_3, VALUE_4);
124     assertEquals(response.getCode(), 200);
125     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_4);
126 
127     response = deleteRow(TABLE, ROW_1);
128     assertEquals(response.getCode(), 200);
129   }
130 
131   @Test
132   public void testSingleCellGetPutBinary() throws IOException {
133     final String path = "/" + TABLE + "/" + ROW_3 + "/" + COLUMN_1;
134     final byte[] body = Bytes.toBytes(VALUE_3);
135     Response response = client.put(path, Constants.MIMETYPE_BINARY, body);
136     assertEquals(response.getCode(), 200);
137     Thread.yield();
138 
139     response = client.get(path, Constants.MIMETYPE_BINARY);
140     assertEquals(response.getCode(), 200);
141     assertEquals(Constants.MIMETYPE_BINARY, response.getHeader("content-type"));
142     assertTrue(Bytes.equals(response.getBody(), body));
143     boolean foundTimestampHeader = false;
144     for (Header header: response.getHeaders()) {
145       if (header.getName().equals("X-Timestamp")) {
146         foundTimestampHeader = true;
147         break;
148       }
149     }
150     assertTrue(foundTimestampHeader);
151 
152     response = deleteRow(TABLE, ROW_3);
153     assertEquals(response.getCode(), 200);
154   }
155 
156   @Test
157   public void testSingleCellGetJSON() throws IOException, JAXBException {
158     final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
159     Response response = client.put(path, Constants.MIMETYPE_BINARY,
160       Bytes.toBytes(VALUE_4));
161     assertEquals(response.getCode(), 200);
162     Thread.yield();
163     response = client.get(path, Constants.MIMETYPE_JSON);
164     assertEquals(response.getCode(), 200);
165     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
166     response = deleteRow(TABLE, ROW_4);
167     assertEquals(response.getCode(), 200);
168   }
169 
170   @Test
171   public void testLatestCellGetJSON() throws IOException, JAXBException {
172     final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
173     CellSetModel cellSetModel = new CellSetModel();
174     RowModel rowModel = new RowModel(ROW_4);
175     CellModel cellOne = new CellModel(Bytes.toBytes(COLUMN_1), 1L,
176       Bytes.toBytes(VALUE_1));
177     CellModel cellTwo = new CellModel(Bytes.toBytes(COLUMN_1), 2L,
178       Bytes.toBytes(VALUE_2));
179     rowModel.addCell(cellOne);
180     rowModel.addCell(cellTwo);
181     cellSetModel.addRow(rowModel);
182     String jsonString = jsonMapper.writeValueAsString(cellSetModel);
183     Response response = client.put(path, Constants.MIMETYPE_JSON,
184       Bytes.toBytes(jsonString));
185     assertEquals(response.getCode(), 200);
186     Thread.yield();
187     response = client.get(path, Constants.MIMETYPE_JSON);
188     assertEquals(response.getCode(), 200);
189     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
190     CellSetModel cellSet = jsonMapper.readValue(response.getBody(), CellSetModel.class);
191     assertTrue(cellSet.getRows().size() == 1);
192     assertTrue(cellSet.getRows().get(0).getCells().size() == 1);
193     CellModel cell = cellSet.getRows().get(0).getCells().get(0);
194     assertEquals(VALUE_2 , Bytes.toString(cell.getValue()));
195     assertEquals(2L , cell.getTimestamp());
196     response = deleteRow(TABLE, ROW_4);
197     assertEquals(response.getCode(), 200);
198   }
199 
200   @Test
201   public void testURLEncodedKey() throws IOException, JAXBException {
202     String urlKey = "http://example.com/foo";
203     StringBuilder path = new StringBuilder();
204     path.append('/');
205     path.append(TABLE);
206     path.append('/');
207     path.append(URLEncoder.encode(urlKey, HConstants.UTF8_ENCODING));
208     path.append('/');
209     path.append(COLUMN_1);
210     Response response;
211     response = putValueXML(path.toString(), TABLE, urlKey, COLUMN_1,
212       VALUE_1);
213     assertEquals(response.getCode(), 200);
214     checkValueXML(path.toString(), TABLE, urlKey, COLUMN_1, VALUE_1);
215   }
216 
217   @Test
218   public void testNoSuchCF() throws IOException, JAXBException {
219     final String goodPath = "/" + TABLE + "/" + ROW_1 + "/" + CFA+":";
220     final String badPath = "/" + TABLE + "/" + ROW_1 + "/" + "BAD";
221     Response response = client.post(goodPath, Constants.MIMETYPE_BINARY,
222       Bytes.toBytes(VALUE_1));
223     assertEquals(response.getCode(), 200);
224     assertEquals(client.get(goodPath, Constants.MIMETYPE_BINARY).getCode(),
225       200);
226     assertEquals(client.get(badPath, Constants.MIMETYPE_BINARY).getCode(),
227       404);
228     assertEquals(client.get(goodPath, Constants.MIMETYPE_BINARY).getCode(),
229       200);
230   }
231 
232   @Test
233   public void testMultiCellGetPutXML() throws IOException, JAXBException {
234     String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
235 
236     CellSetModel cellSetModel = new CellSetModel();
237     RowModel rowModel = new RowModel(ROW_1);
238     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
239       Bytes.toBytes(VALUE_1)));
240     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
241       Bytes.toBytes(VALUE_2)));
242     cellSetModel.addRow(rowModel);
243     rowModel = new RowModel(ROW_2);
244     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
245       Bytes.toBytes(VALUE_3)));
246     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
247       Bytes.toBytes(VALUE_4)));
248     cellSetModel.addRow(rowModel);
249     StringWriter writer = new StringWriter();
250     xmlMarshaller.marshal(cellSetModel, writer);
251     Response response = client.put(path, Constants.MIMETYPE_XML,
252       Bytes.toBytes(writer.toString()));
253     Thread.yield();
254 
255     // make sure the fake row was not actually created
256     response = client.get(path, Constants.MIMETYPE_XML);
257     assertEquals(response.getCode(), 404);
258 
259     // check that all of the values were created
260     checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
261     checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
262     checkValueXML(TABLE, ROW_2, COLUMN_1, VALUE_3);
263     checkValueXML(TABLE, ROW_2, COLUMN_2, VALUE_4);
264 
265     response = deleteRow(TABLE, ROW_1);
266     assertEquals(response.getCode(), 200);
267     response = deleteRow(TABLE, ROW_2);
268     assertEquals(response.getCode(), 200);
269   }
270 
271   @Test
272   public void testMultiCellGetPutPB() throws IOException {
273     String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
274 
275     CellSetModel cellSetModel = new CellSetModel();
276     RowModel rowModel = new RowModel(ROW_1);
277     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
278       Bytes.toBytes(VALUE_1)));
279     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
280       Bytes.toBytes(VALUE_2)));
281     cellSetModel.addRow(rowModel);
282     rowModel = new RowModel(ROW_2);
283     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
284       Bytes.toBytes(VALUE_3)));
285     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
286       Bytes.toBytes(VALUE_4)));
287     cellSetModel.addRow(rowModel);
288     Response response = client.put(path, Constants.MIMETYPE_PROTOBUF,
289       cellSetModel.createProtobufOutput());
290     Thread.yield();
291 
292     // make sure the fake row was not actually created
293     response = client.get(path, Constants.MIMETYPE_PROTOBUF);
294     assertEquals(response.getCode(), 404);
295 
296     // check that all of the values were created
297     checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
298     checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
299     checkValuePB(TABLE, ROW_2, COLUMN_1, VALUE_3);
300     checkValuePB(TABLE, ROW_2, COLUMN_2, VALUE_4);
301 
302     response = deleteRow(TABLE, ROW_1);
303     assertEquals(response.getCode(), 200);
304     response = deleteRow(TABLE, ROW_2);
305     assertEquals(response.getCode(), 200);
306   }
307 
308   @Test
309   public void testStartEndRowGetPutXML() throws IOException, JAXBException {
310     String[] rows = { ROW_1, ROW_2, ROW_3 };
311     String[] values = { VALUE_1, VALUE_2, VALUE_3 };
312     Response response = null;
313     for (int i = 0; i < rows.length; i++) {
314       response = putValueXML(TABLE, rows[i], COLUMN_1, values[i]);
315       assertEquals(200, response.getCode());
316       checkValueXML(TABLE, rows[i], COLUMN_1, values[i]);
317     }
318     response = getValueXML(TABLE, rows[0], rows[2], COLUMN_1);
319     assertEquals(200, response.getCode());
320     CellSetModel cellSet = (CellSetModel)
321       xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
322     assertEquals(2, cellSet.getRows().size());
323     for (int i = 0; i < cellSet.getRows().size()-1; i++) {
324       RowModel rowModel = cellSet.getRows().get(i);
325       for (CellModel cell: rowModel.getCells()) {
326         assertEquals(COLUMN_1, Bytes.toString(cell.getColumn()));
327         assertEquals(values[i], Bytes.toString(cell.getValue()));
328       }
329     }
330     for (String row : rows) {
331       response = deleteRow(TABLE, row);
332       assertEquals(200, response.getCode());
333     }
334   }
335 
336   @Test
337   public void testInvalidCheckParam() throws IOException, JAXBException {
338     CellSetModel cellSetModel = new CellSetModel();
339     RowModel rowModel = new RowModel(ROW_1);
340     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
341       Bytes.toBytes(VALUE_1)));
342     cellSetModel.addRow(rowModel);
343     StringWriter writer = new StringWriter();
344     xmlMarshaller.marshal(cellSetModel, writer);
345 
346     final String path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1 + "?check=blah";
347 
348     Response response = client.put(path, Constants.MIMETYPE_XML,
349       Bytes.toBytes(writer.toString()));
350     assertEquals(response.getCode(), 400);
351   }
352 
353   @Test
354   public void testInvalidColumnPut() throws IOException, JAXBException {
355     String dummyColumn = "doesnot:exist";
356     CellSetModel cellSetModel = new CellSetModel();
357     RowModel rowModel = new RowModel(ROW_1);
358     rowModel.addCell(new CellModel(Bytes.toBytes(dummyColumn),
359       Bytes.toBytes(VALUE_1)));
360     cellSetModel.addRow(rowModel);
361     StringWriter writer = new StringWriter();
362     xmlMarshaller.marshal(cellSetModel, writer);
363 
364     final String path = "/" + TABLE + "/" + ROW_1 + "/" + dummyColumn;
365 
366     Response response = client.put(path, Constants.MIMETYPE_XML,
367       Bytes.toBytes(writer.toString()));
368     assertEquals(response.getCode(), 404);
369   }
370 
371   @Test
372   public void testMultiCellGetJson() throws IOException, JAXBException {
373     String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
374 
375     CellSetModel cellSetModel = new CellSetModel();
376     RowModel rowModel = new RowModel(ROW_1);
377     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
378       Bytes.toBytes(VALUE_1)));
379     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
380       Bytes.toBytes(VALUE_2)));
381     cellSetModel.addRow(rowModel);
382     rowModel = new RowModel(ROW_2);
383     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
384       Bytes.toBytes(VALUE_3)));
385     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
386       Bytes.toBytes(VALUE_4)));
387     cellSetModel.addRow(rowModel);
388     String jsonString = jsonMapper.writeValueAsString(cellSetModel);
389 
390     Response response = client.put(path, Constants.MIMETYPE_JSON,
391       Bytes.toBytes(jsonString));
392     Thread.yield();
393 
394     // make sure the fake row was not actually created
395     response = client.get(path, Constants.MIMETYPE_JSON);
396     assertEquals(response.getCode(), 404);
397 
398     // check that all of the values were created
399     checkValueJSON(TABLE, ROW_1, COLUMN_1, VALUE_1);
400     checkValueJSON(TABLE, ROW_1, COLUMN_2, VALUE_2);
401     checkValueJSON(TABLE, ROW_2, COLUMN_1, VALUE_3);
402     checkValueJSON(TABLE, ROW_2, COLUMN_2, VALUE_4);
403 
404     response = deleteRow(TABLE, ROW_1);
405     assertEquals(response.getCode(), 200);
406     response = deleteRow(TABLE, ROW_2);
407     assertEquals(response.getCode(), 200);
408   }
409   
410   @Test
411   public void testMetrics() throws IOException, JAXBException {
412     final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
413     Response response = client.put(path, Constants.MIMETYPE_BINARY,
414         Bytes.toBytes(VALUE_4));
415     assertEquals(response.getCode(), 200);
416     Thread.yield();
417     response = client.get(path, Constants.MIMETYPE_JSON);
418     assertEquals(response.getCode(), 200);
419     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
420     response = deleteRow(TABLE, ROW_4);
421     assertEquals(response.getCode(), 200);
422 
423     UserProvider userProvider = UserProvider.instantiate(conf);
424     METRICS_ASSERT.assertCounterGt("requests", 2l,
425       RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
426 
427     METRICS_ASSERT.assertCounterGt("successfulGet", 0l,
428       RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
429 
430     METRICS_ASSERT.assertCounterGt("successfulPut", 0l,
431       RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
432 
433     METRICS_ASSERT.assertCounterGt("successfulDelete", 0l,
434       RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
435   }
436   
437   @Test
438   public void testMultiColumnGetXML() throws Exception {
439     String path = "/" + TABLE + "/fakerow";
440     CellSetModel cellSetModel = new CellSetModel();
441     RowModel rowModel = new RowModel(ROW_1);
442     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
443     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
444     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_3), Bytes.toBytes(VALUE_2)));
445     cellSetModel.addRow(rowModel);
446     StringWriter writer = new StringWriter();
447     xmlMarshaller.marshal(cellSetModel, writer);
448 
449     Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
450     Thread.yield();
451 
452     // make sure the fake row was not actually created
453     response = client.get(path, Constants.MIMETYPE_XML);
454     assertEquals(response.getCode(), 404);
455 
456     // Try getting all the column values at once.
457     path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1 + "," + COLUMN_2 + "," + COLUMN_3;
458     response = client.get(path, Constants.MIMETYPE_XML);
459     assertEquals(200, response.getCode());
460     CellSetModel cellSet = (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response
461         .getBody()));
462     assertTrue(cellSet.getRows().size() == 1);
463     assertTrue(cellSet.getRows().get(0).getCells().size() == 3);
464     List<CellModel> cells = cellSet.getRows().get(0).getCells();
465 
466     assertTrue(containsCellModel(cells, COLUMN_1, VALUE_1));
467     assertTrue(containsCellModel(cells, COLUMN_2, VALUE_2));
468     assertTrue(containsCellModel(cells, COLUMN_3, VALUE_2));
469     response = deleteRow(TABLE, ROW_1);
470     assertEquals(response.getCode(), 200);
471   }
472 
473   private boolean containsCellModel(List<CellModel> cells, String column, String value) {
474     boolean contains = false;
475     for (CellModel cell : cells) {
476       if (Bytes.toString(cell.getColumn()).equals(column)
477           && Bytes.toString(cell.getValue()).equals(value)) {
478         contains = true;
479         return contains;
480       }
481     }
482     return contains;
483   }
484 
485   @Test
486   public void testSuffixGlobbingXMLWithNewScanner() throws IOException, JAXBException {
487     String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
488 
489     CellSetModel cellSetModel = new CellSetModel();
490     RowModel rowModel = new RowModel(ROW_1);
491     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
492       Bytes.toBytes(VALUE_1)));
493     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
494       Bytes.toBytes(VALUE_2)));
495     cellSetModel.addRow(rowModel);
496     rowModel = new RowModel(ROW_2);
497     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
498       Bytes.toBytes(VALUE_3)));
499     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
500       Bytes.toBytes(VALUE_4)));
501     cellSetModel.addRow(rowModel);
502     StringWriter writer = new StringWriter();
503     xmlMarshaller.marshal(cellSetModel, writer);
504     Response response = client.put(path, Constants.MIMETYPE_XML,
505       Bytes.toBytes(writer.toString()));
506     Thread.yield();
507 
508     // make sure the fake row was not actually created
509     response = client.get(path, Constants.MIMETYPE_XML);
510     assertEquals(response.getCode(), 404);
511 
512     // check that all of the values were created
513     StringBuilder query = new StringBuilder();
514     query.append('/');
515     query.append(TABLE);
516     query.append('/');
517     query.append("testrow*");
518     response = client.get(query.toString(), Constants.MIMETYPE_XML);
519     assertEquals(response.getCode(), 200);
520     assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
521     CellSetModel cellSet = (CellSetModel)
522       xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
523     assertTrue(cellSet.getRows().size() == 2);
524 
525     response = deleteRow(TABLE, ROW_1);
526     assertEquals(response.getCode(), 200);
527     response = deleteRow(TABLE, ROW_2);
528     assertEquals(response.getCode(), 200);
529   }
530 
531   @Test
532   public void testSuffixGlobbingXML() throws IOException, JAXBException {
533     String path = "/" + TABLE + "/fakerow";  // deliberate nonexistent row
534 
535     CellSetModel cellSetModel = new CellSetModel();
536     RowModel rowModel = new RowModel(ROW_1);
537     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
538       Bytes.toBytes(VALUE_1)));
539     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
540       Bytes.toBytes(VALUE_2)));
541     cellSetModel.addRow(rowModel);
542     rowModel = new RowModel(ROW_2);
543     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
544       Bytes.toBytes(VALUE_3)));
545     rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
546       Bytes.toBytes(VALUE_4)));
547     cellSetModel.addRow(rowModel);
548     StringWriter writer = new StringWriter();
549     xmlMarshaller.marshal(cellSetModel, writer);
550     Response response = client.put(path, Constants.MIMETYPE_XML,
551       Bytes.toBytes(writer.toString()));
552     Thread.yield();
553 
554     // make sure the fake row was not actually created
555     response = client.get(path, Constants.MIMETYPE_XML);
556     assertEquals(response.getCode(), 404);
557 
558     // check that all of the values were created
559     StringBuilder query = new StringBuilder();
560     query.append('/');
561     query.append(TABLE);
562     query.append('/');
563     query.append("testrow*");
564     query.append('/');
565     query.append(COLUMN_1);
566     response = client.get(query.toString(), Constants.MIMETYPE_XML);
567     assertEquals(response.getCode(), 200);
568     assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
569     CellSetModel cellSet = (CellSetModel)
570       xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
571     List<RowModel> rows = cellSet.getRows();
572     assertTrue(rows.size() == 2);
573     for (RowModel row : rows) {
574       assertTrue(row.getCells().size() == 1);
575       assertEquals(COLUMN_1, Bytes.toString(row.getCells().get(0).getColumn()));
576     }
577     response = deleteRow(TABLE, ROW_1);
578     assertEquals(response.getCode(), 200);
579     response = deleteRow(TABLE, ROW_2);
580     assertEquals(response.getCode(), 200);
581   }
582 }
583