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