001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.rest;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertTrue;
022
023import java.io.ByteArrayInputStream;
024import java.io.IOException;
025import java.io.StringWriter;
026import java.net.URLEncoder;
027import java.util.HashMap;
028import java.util.List;
029import javax.xml.bind.JAXBException;
030import org.apache.hadoop.hbase.CompatibilityFactory;
031import org.apache.hadoop.hbase.HBaseClassTestRule;
032import org.apache.hadoop.hbase.HConstants;
033import org.apache.hadoop.hbase.rest.client.Response;
034import org.apache.hadoop.hbase.rest.model.CellModel;
035import org.apache.hadoop.hbase.rest.model.CellSetModel;
036import org.apache.hadoop.hbase.rest.model.RowModel;
037import org.apache.hadoop.hbase.security.UserProvider;
038import org.apache.hadoop.hbase.test.MetricsAssertHelper;
039import org.apache.hadoop.hbase.testclassification.MediumTests;
040import org.apache.hadoop.hbase.testclassification.RestTests;
041import org.apache.hadoop.hbase.util.Bytes;
042import org.apache.http.Header;
043import org.junit.ClassRule;
044import org.junit.Test;
045import org.junit.experimental.categories.Category;
046
047@Category({ RestTests.class, MediumTests.class })
048public class TestGetAndPutResource extends RowResourceBase {
049  @ClassRule
050  public static final HBaseClassTestRule CLASS_RULE =
051    HBaseClassTestRule.forClass(TestGetAndPutResource.class);
052
053  private static final MetricsAssertHelper METRICS_ASSERT =
054    CompatibilityFactory.getInstance(MetricsAssertHelper.class);
055
056  @Test
057  public void testForbidden() throws IOException, JAXBException {
058    conf.set("hbase.rest.readonly", "true");
059
060    Response response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
061    assertEquals(403, response.getCode());
062    response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
063    assertEquals(403, response.getCode());
064    response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
065    assertEquals(403, response.getCode());
066    response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
067    assertEquals(403, response.getCode());
068    response = deleteValue(TABLE, ROW_1, COLUMN_1);
069    assertEquals(403, response.getCode());
070    response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
071    assertEquals(403, response.getCode());
072    response = deleteRow(TABLE, ROW_1);
073    assertEquals(403, response.getCode());
074
075    conf.set("hbase.rest.readonly", "false");
076
077    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
078    assertEquals(200, response.getCode());
079    response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
080    assertEquals(200, response.getCode());
081    response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_2);
082    assertEquals(200, response.getCode());
083    response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
084    assertEquals(200, response.getCode());
085    response = deleteValue(TABLE, ROW_1, COLUMN_1);
086    assertEquals(200, response.getCode());
087    response = deleteRow(TABLE, ROW_1);
088    assertEquals(200, response.getCode());
089  }
090
091  @Test
092  public void testSingleCellGetPutXML() throws IOException, JAXBException {
093    Response response = getValueXML(TABLE, ROW_1, COLUMN_1);
094    assertEquals(404, response.getCode());
095
096    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
097    assertEquals(200, response.getCode());
098    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
099    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
100    assertEquals(200, response.getCode());
101    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
102    response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
103    assertEquals(200, response.getCode());
104    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_3);
105    response = checkAndDeleteXML(TABLE, ROW_1, COLUMN_1, VALUE_3);
106    assertEquals(200, response.getCode());
107
108    response = deleteRow(TABLE, ROW_1);
109    assertEquals(200, response.getCode());
110  }
111
112  @Test
113  public void testSingleCellGetPutPB() throws IOException, JAXBException {
114    Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
115    assertEquals(404, response.getCode());
116
117    response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
118    assertEquals(200, response.getCode());
119    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
120    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
121    assertEquals(200, response.getCode());
122    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2);
123
124    response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2, VALUE_3);
125    assertEquals(200, response.getCode());
126    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_3);
127    response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_3, VALUE_4);
128    assertEquals(200, response.getCode());
129    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_4);
130
131    response = deleteRow(TABLE, ROW_1);
132    assertEquals(200, response.getCode());
133  }
134
135  @Test
136  public void testMultipleCellCheckPutPB() throws IOException {
137    Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
138    assertEquals(404, response.getCode());
139
140    // Add 2 Columns to setup the test
141    response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
142    assertEquals(200, response.getCode());
143    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
144
145    response = putValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
146    assertEquals(200, response.getCode());
147    checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
148
149    HashMap<String, String> otherCells = new HashMap<>();
150    otherCells.put(COLUMN_2, VALUE_3);
151
152    // On Success update both the cells
153    response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_3, otherCells);
154    assertEquals(200, response.getCode());
155    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_3);
156    checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_3);
157
158    // On Failure, we dont update any cells
159    response = checkAndPutValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_4, otherCells);
160    assertEquals(304, response.getCode());
161    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_3);
162    checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_3);
163
164    response = deleteRow(TABLE, ROW_1);
165    assertEquals(200, response.getCode());
166  }
167
168  @Test
169  public void testMultipleCellCheckPutXML() throws IOException, JAXBException {
170    Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
171    assertEquals(404, response.getCode());
172
173    // Add 2 Columns to setup the test
174    response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
175    assertEquals(200, response.getCode());
176    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
177
178    response = putValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
179    assertEquals(200, response.getCode());
180    checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
181
182    HashMap<String, String> otherCells = new HashMap<>();
183    otherCells.put(COLUMN_2, VALUE_3);
184
185    // On Success update both the cells
186    response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_3, otherCells);
187    assertEquals(200, response.getCode());
188    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_3);
189    checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_3);
190
191    // On Failure, we dont update any cells
192    response = checkAndPutValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1, VALUE_4, otherCells);
193    assertEquals(304, response.getCode());
194    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_3);
195    checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_3);
196
197    response = deleteRow(TABLE, ROW_1);
198    assertEquals(200, response.getCode());
199  }
200
201  @Test
202  public void testMultipleCellCheckDeletePB() throws IOException {
203    Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
204    assertEquals(404, response.getCode());
205
206    // Add 3 Columns to setup the test
207    response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
208    assertEquals(200, response.getCode());
209    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
210
211    response = putValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
212    assertEquals(200, response.getCode());
213    checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
214
215    response = putValuePB(TABLE, ROW_1, COLUMN_3, VALUE_3);
216    assertEquals(200, response.getCode());
217    checkValuePB(TABLE, ROW_1, COLUMN_3, VALUE_3);
218
219    // Deletes the following columns based on Column1 check
220    HashMap<String, String> cellsToDelete = new HashMap<>();
221    cellsToDelete.put(COLUMN_2, VALUE_2); // Value does not matter
222    cellsToDelete.put(COLUMN_3, VALUE_3); // Value does not matter
223
224    // On Success update both the cells
225    response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_1, cellsToDelete);
226    assertEquals(200, response.getCode());
227
228    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
229
230    response = getValuePB(TABLE, ROW_1, COLUMN_2);
231    assertEquals(404, response.getCode());
232
233    response = getValuePB(TABLE, ROW_1, COLUMN_3);
234    assertEquals(404, response.getCode());
235
236    response = putValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
237    assertEquals(200, response.getCode());
238    checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
239
240    response = putValuePB(TABLE, ROW_1, COLUMN_3, VALUE_3);
241    assertEquals(200, response.getCode());
242    checkValuePB(TABLE, ROW_1, COLUMN_3, VALUE_3);
243
244    // On Failure, we dont update any cells
245    response = checkAndDeletePB(TABLE, ROW_1, COLUMN_1, VALUE_3, cellsToDelete);
246    assertEquals(304, response.getCode());
247    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
248    checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
249    checkValuePB(TABLE, ROW_1, COLUMN_3, VALUE_3);
250
251    response = deleteRow(TABLE, ROW_1);
252    assertEquals(200, response.getCode());
253  }
254
255  @Test
256  public void testSingleCellGetPutBinary() throws IOException {
257    final String path = "/" + TABLE + "/" + ROW_3 + "/" + COLUMN_1;
258    final byte[] body = Bytes.toBytes(VALUE_3);
259    Response response = client.put(path, Constants.MIMETYPE_BINARY, body);
260    assertEquals(200, response.getCode());
261    Thread.yield();
262
263    response = client.get(path, Constants.MIMETYPE_BINARY);
264    assertEquals(200, response.getCode());
265    assertEquals(Constants.MIMETYPE_BINARY, response.getHeader("content-type"));
266    assertTrue(Bytes.equals(response.getBody(), body));
267    boolean foundTimestampHeader = false;
268    for (Header header : response.getHeaders()) {
269      if (header.getName().equals("X-Timestamp")) {
270        foundTimestampHeader = true;
271        break;
272      }
273    }
274    assertTrue(foundTimestampHeader);
275
276    response = deleteRow(TABLE, ROW_3);
277    assertEquals(200, response.getCode());
278  }
279
280  @Test
281  public void testSingleCellGetJSON() throws IOException {
282    final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
283    Response response = client.put(path, Constants.MIMETYPE_BINARY, Bytes.toBytes(VALUE_4));
284    assertEquals(200, response.getCode());
285    Thread.yield();
286    response = client.get(path, Constants.MIMETYPE_JSON);
287    assertEquals(200, response.getCode());
288    assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
289    response = deleteRow(TABLE, ROW_4);
290    assertEquals(200, response.getCode());
291  }
292
293  @Test
294  public void testLatestCellGetJSON() throws IOException {
295    final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
296    CellSetModel cellSetModel = new CellSetModel();
297    RowModel rowModel = new RowModel(ROW_4);
298    CellModel cellOne = new CellModel(Bytes.toBytes(COLUMN_1), 1L, Bytes.toBytes(VALUE_1));
299    CellModel cellTwo = new CellModel(Bytes.toBytes(COLUMN_1), 2L, Bytes.toBytes(VALUE_2));
300    rowModel.addCell(cellOne);
301    rowModel.addCell(cellTwo);
302    cellSetModel.addRow(rowModel);
303    String jsonString = jsonMapper.writeValueAsString(cellSetModel);
304    Response response = client.put(path, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString));
305    assertEquals(200, response.getCode());
306    Thread.yield();
307    response = client.get(path, Constants.MIMETYPE_JSON);
308    assertEquals(200, response.getCode());
309    assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
310    CellSetModel cellSet = jsonMapper.readValue(response.getBody(), CellSetModel.class);
311    assertTrue(cellSet.getRows().size() == 1);
312    assertTrue(cellSet.getRows().get(0).getCells().size() == 1);
313    CellModel cell = cellSet.getRows().get(0).getCells().get(0);
314    assertEquals(VALUE_2, Bytes.toString(cell.getValue()));
315    assertEquals(2L, cell.getTimestamp());
316    response = deleteRow(TABLE, ROW_4);
317    assertEquals(200, response.getCode());
318  }
319
320  @Test
321  public void testURLEncodedKey() throws IOException, JAXBException {
322    String urlKey = "http://example.com/foo";
323    StringBuilder path = new StringBuilder();
324    path.append('/');
325    path.append(TABLE);
326    path.append('/');
327    path.append(URLEncoder.encode(urlKey, HConstants.UTF8_ENCODING));
328    path.append('/');
329    path.append(COLUMN_1);
330    Response response;
331    response = putValueXML(path.toString(), TABLE, urlKey, COLUMN_1, VALUE_1);
332    assertEquals(200, response.getCode());
333    checkValueXML(path.toString(), TABLE, urlKey, COLUMN_1, VALUE_1);
334  }
335
336  @Test
337  public void testNoSuchCF() throws IOException {
338    final String goodPath = "/" + TABLE + "/" + ROW_1 + "/" + CFA + ":";
339    final String badPath = "/" + TABLE + "/" + ROW_1 + "/" + "BAD";
340    Response response = client.post(goodPath, Constants.MIMETYPE_BINARY, Bytes.toBytes(VALUE_1));
341    assertEquals(200, response.getCode());
342    assertEquals(200, client.get(goodPath, Constants.MIMETYPE_BINARY).getCode());
343    assertEquals(404, client.get(badPath, Constants.MIMETYPE_BINARY).getCode());
344    assertEquals(200, client.get(goodPath, Constants.MIMETYPE_BINARY).getCode());
345  }
346
347  @Test
348  public void testMultiCellGetPutXML() throws IOException, JAXBException {
349    String path = "/" + TABLE + "/fakerow"; // deliberate nonexistent row
350
351    CellSetModel cellSetModel = new CellSetModel();
352    RowModel rowModel = new RowModel(ROW_1);
353    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
354    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
355    cellSetModel.addRow(rowModel);
356    rowModel = new RowModel(ROW_2);
357    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_3)));
358    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_4)));
359    cellSetModel.addRow(rowModel);
360    StringWriter writer = new StringWriter();
361    xmlMarshaller.marshal(cellSetModel, writer);
362    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
363    Thread.yield();
364
365    // make sure the fake row was not actually created
366    response = client.get(path, Constants.MIMETYPE_XML);
367    assertEquals(404, response.getCode());
368
369    // check that all of the values were created
370    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
371    checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
372    checkValueXML(TABLE, ROW_2, COLUMN_1, VALUE_3);
373    checkValueXML(TABLE, ROW_2, COLUMN_2, VALUE_4);
374
375    response = deleteRow(TABLE, ROW_1);
376    assertEquals(200, response.getCode());
377    response = deleteRow(TABLE, ROW_2);
378    assertEquals(200, response.getCode());
379  }
380
381  @Test
382  public void testMultiCellGetPutPB() throws IOException {
383    String path = "/" + TABLE + "/fakerow"; // deliberate nonexistent row
384
385    CellSetModel cellSetModel = new CellSetModel();
386    RowModel rowModel = new RowModel(ROW_1);
387    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
388    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
389    cellSetModel.addRow(rowModel);
390    rowModel = new RowModel(ROW_2);
391    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_3)));
392    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_4)));
393    cellSetModel.addRow(rowModel);
394    Response response =
395      client.put(path, Constants.MIMETYPE_PROTOBUF, cellSetModel.createProtobufOutput());
396    Thread.yield();
397
398    // make sure the fake row was not actually created
399    response = client.get(path, Constants.MIMETYPE_PROTOBUF);
400    assertEquals(404, response.getCode());
401
402    // check that all of the values were created
403    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
404    checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
405    checkValuePB(TABLE, ROW_2, COLUMN_1, VALUE_3);
406    checkValuePB(TABLE, ROW_2, COLUMN_2, VALUE_4);
407
408    response = deleteRow(TABLE, ROW_1);
409    assertEquals(200, response.getCode());
410    response = deleteRow(TABLE, ROW_2);
411    assertEquals(200, response.getCode());
412  }
413
414  @Test
415  public void testStartEndRowGetPutXML() throws IOException, JAXBException {
416    String[] rows = { ROW_1, ROW_2, ROW_3 };
417    String[] values = { VALUE_1, VALUE_2, VALUE_3 };
418    Response response = null;
419    for (int i = 0; i < rows.length; i++) {
420      response = putValueXML(TABLE, rows[i], COLUMN_1, values[i]);
421      assertEquals(200, response.getCode());
422      checkValueXML(TABLE, rows[i], COLUMN_1, values[i]);
423    }
424    response = getValueXML(TABLE, rows[0], rows[2], COLUMN_1);
425    assertEquals(200, response.getCode());
426    CellSetModel cellSet =
427      (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
428    assertEquals(2, cellSet.getRows().size());
429    for (int i = 0; i < cellSet.getRows().size() - 1; i++) {
430      RowModel rowModel = cellSet.getRows().get(i);
431      for (CellModel cell : rowModel.getCells()) {
432        assertEquals(COLUMN_1, Bytes.toString(cell.getColumn()));
433        assertEquals(values[i], Bytes.toString(cell.getValue()));
434      }
435    }
436    for (String row : rows) {
437      response = deleteRow(TABLE, row);
438      assertEquals(200, response.getCode());
439    }
440  }
441
442  @Test
443  public void testInvalidCheckParam() throws IOException, JAXBException {
444    CellSetModel cellSetModel = new CellSetModel();
445    RowModel rowModel = new RowModel(ROW_1);
446    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
447    cellSetModel.addRow(rowModel);
448    StringWriter writer = new StringWriter();
449    xmlMarshaller.marshal(cellSetModel, writer);
450
451    final String path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1 + "?check=blah";
452
453    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
454    assertEquals(400, response.getCode());
455  }
456
457  @Test
458  public void testInvalidColumnPut() throws IOException, JAXBException {
459    String dummyColumn = "doesnot:exist";
460    CellSetModel cellSetModel = new CellSetModel();
461    RowModel rowModel = new RowModel(ROW_1);
462    rowModel.addCell(new CellModel(Bytes.toBytes(dummyColumn), Bytes.toBytes(VALUE_1)));
463    cellSetModel.addRow(rowModel);
464    StringWriter writer = new StringWriter();
465    xmlMarshaller.marshal(cellSetModel, writer);
466
467    final String path = "/" + TABLE + "/" + ROW_1 + "/" + dummyColumn;
468
469    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
470    assertEquals(404, response.getCode());
471  }
472
473  @Test
474  public void testMultiCellGetJson() throws IOException, JAXBException {
475    String path = "/" + TABLE + "/fakerow"; // deliberate nonexistent row
476
477    CellSetModel cellSetModel = new CellSetModel();
478    RowModel rowModel = new RowModel(ROW_1);
479    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
480    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
481    cellSetModel.addRow(rowModel);
482    rowModel = new RowModel(ROW_2);
483    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_3)));
484    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_4)));
485    cellSetModel.addRow(rowModel);
486    String jsonString = jsonMapper.writeValueAsString(cellSetModel);
487
488    Response response = client.put(path, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString));
489    Thread.yield();
490
491    // make sure the fake row was not actually created
492    response = client.get(path, Constants.MIMETYPE_JSON);
493    assertEquals(404, response.getCode());
494
495    // check that all of the values were created
496    checkValueJSON(TABLE, ROW_1, COLUMN_1, VALUE_1);
497    checkValueJSON(TABLE, ROW_1, COLUMN_2, VALUE_2);
498    checkValueJSON(TABLE, ROW_2, COLUMN_1, VALUE_3);
499    checkValueJSON(TABLE, ROW_2, COLUMN_2, VALUE_4);
500
501    response = deleteRow(TABLE, ROW_1);
502    assertEquals(200, response.getCode());
503    response = deleteRow(TABLE, ROW_2);
504    assertEquals(200, response.getCode());
505  }
506
507  @Test
508  public void testMetrics() throws IOException {
509    final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
510    Response response = client.put(path, Constants.MIMETYPE_BINARY, Bytes.toBytes(VALUE_4));
511    assertEquals(200, response.getCode());
512    Thread.yield();
513    response = client.get(path, Constants.MIMETYPE_JSON);
514    assertEquals(200, response.getCode());
515    assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
516    response = deleteRow(TABLE, ROW_4);
517    assertEquals(200, response.getCode());
518
519    UserProvider userProvider = UserProvider.instantiate(conf);
520    METRICS_ASSERT.assertCounterGt("requests", 2L,
521      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
522
523    METRICS_ASSERT.assertCounterGt("successfulGet", 0L,
524      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
525
526    METRICS_ASSERT.assertCounterGt("successfulPut", 0L,
527      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
528
529    METRICS_ASSERT.assertCounterGt("successfulDelete", 0L,
530      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
531  }
532
533  @Test
534  public void testMultiColumnGetXML() throws Exception {
535    String path = "/" + TABLE + "/fakerow";
536    CellSetModel cellSetModel = new CellSetModel();
537    RowModel rowModel = new RowModel(ROW_1);
538    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
539    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
540    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_3), Bytes.toBytes(VALUE_2)));
541    cellSetModel.addRow(rowModel);
542    StringWriter writer = new StringWriter();
543    xmlMarshaller.marshal(cellSetModel, writer);
544
545    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
546    Thread.yield();
547
548    // make sure the fake row was not actually created
549    response = client.get(path, Constants.MIMETYPE_XML);
550    assertEquals(404, response.getCode());
551
552    // Try getting all the column values at once.
553    path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1 + "," + COLUMN_2 + "," + COLUMN_3;
554    response = client.get(path, Constants.MIMETYPE_XML);
555    assertEquals(200, response.getCode());
556    CellSetModel cellSet =
557      (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
558    assertTrue(cellSet.getRows().size() == 1);
559    assertTrue(cellSet.getRows().get(0).getCells().size() == 3);
560    List<CellModel> cells = cellSet.getRows().get(0).getCells();
561
562    assertTrue(containsCellModel(cells, COLUMN_1, VALUE_1));
563    assertTrue(containsCellModel(cells, COLUMN_2, VALUE_2));
564    assertTrue(containsCellModel(cells, COLUMN_3, VALUE_2));
565    response = deleteRow(TABLE, ROW_1);
566    assertEquals(200, response.getCode());
567  }
568
569  private boolean containsCellModel(List<CellModel> cells, String column, String value) {
570    boolean contains = false;
571    for (CellModel cell : cells) {
572      if (
573        Bytes.toString(cell.getColumn()).equals(column)
574          && Bytes.toString(cell.getValue()).equals(value)
575      ) {
576        contains = true;
577        return contains;
578      }
579    }
580    return contains;
581  }
582
583  @Test
584  public void testSuffixGlobbingXMLWithNewScanner() throws IOException, JAXBException {
585    String path = "/" + TABLE + "/fakerow"; // deliberate nonexistent row
586
587    CellSetModel cellSetModel = new CellSetModel();
588    RowModel rowModel = new RowModel(ROW_1);
589    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
590    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
591    cellSetModel.addRow(rowModel);
592    rowModel = new RowModel(ROW_2);
593    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_3)));
594    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_4)));
595    cellSetModel.addRow(rowModel);
596    StringWriter writer = new StringWriter();
597    xmlMarshaller.marshal(cellSetModel, writer);
598    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
599    Thread.yield();
600
601    // make sure the fake row was not actually created
602    response = client.get(path, Constants.MIMETYPE_XML);
603    assertEquals(404, response.getCode());
604
605    // check that all of the values were created
606    StringBuilder query = new StringBuilder();
607    query.append('/');
608    query.append(TABLE);
609    query.append('/');
610    query.append("testrow*");
611    response = client.get(query.toString(), Constants.MIMETYPE_XML);
612    assertEquals(200, response.getCode());
613    assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
614    CellSetModel cellSet =
615      (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
616    assertTrue(cellSet.getRows().size() == 2);
617
618    response = deleteRow(TABLE, ROW_1);
619    assertEquals(200, response.getCode());
620    response = deleteRow(TABLE, ROW_2);
621    assertEquals(200, response.getCode());
622  }
623
624  @Test
625  public void testSuffixGlobbingXML() throws IOException, JAXBException {
626    String path = "/" + TABLE + "/fakerow"; // deliberate nonexistent row
627
628    CellSetModel cellSetModel = new CellSetModel();
629    RowModel rowModel = new RowModel(ROW_1);
630    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
631    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
632    cellSetModel.addRow(rowModel);
633    rowModel = new RowModel(ROW_2);
634    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_3)));
635    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_4)));
636    cellSetModel.addRow(rowModel);
637    StringWriter writer = new StringWriter();
638    xmlMarshaller.marshal(cellSetModel, writer);
639    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
640    Thread.yield();
641
642    // make sure the fake row was not actually created
643    response = client.get(path, Constants.MIMETYPE_XML);
644    assertEquals(404, response.getCode());
645
646    // check that all of the values were created
647    StringBuilder query = new StringBuilder();
648    query.append('/');
649    query.append(TABLE);
650    query.append('/');
651    query.append("testrow*");
652    query.append('/');
653    query.append(COLUMN_1);
654    response = client.get(query.toString(), Constants.MIMETYPE_XML);
655    assertEquals(200, response.getCode());
656    assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
657    CellSetModel cellSet =
658      (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
659    List<RowModel> rows = cellSet.getRows();
660    assertTrue(rows.size() == 2);
661    for (RowModel row : rows) {
662      assertTrue(row.getCells().size() == 1);
663      assertEquals(COLUMN_1, Bytes.toString(row.getCells().get(0).getColumn()));
664    }
665    response = deleteRow(TABLE, ROW_1);
666    assertEquals(200, response.getCode());
667    response = deleteRow(TABLE, ROW_2);
668    assertEquals(200, response.getCode());
669  }
670
671  @Test
672  public void testAppendXML() throws IOException, JAXBException {
673    Response response = getValueXML(TABLE, ROW_1, COLUMN_1);
674    assertEquals(404, response.getCode());
675
676    // append cell
677    response = appendValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
678    assertEquals(200, response.getCode());
679    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
680    response = appendValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
681    assertEquals(200, response.getCode());
682    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1 + VALUE_2);
683
684    response = deleteRow(TABLE, ROW_1);
685    assertEquals(200, response.getCode());
686  }
687
688  @Test
689  public void testAppendPB() throws IOException, JAXBException {
690    Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
691    assertEquals(404, response.getCode());
692
693    // append cell
694    response = appendValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
695    assertEquals(200, response.getCode());
696    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
697    response = appendValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2);
698    assertEquals(200, response.getCode());
699    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1 + VALUE_2);
700
701    response = deleteRow(TABLE, ROW_1);
702    assertEquals(200, response.getCode());
703  }
704
705  @Test
706  public void testAppendJSON() throws IOException, JAXBException {
707    Response response = getValueJson(TABLE, ROW_1, COLUMN_1);
708    assertEquals(404, response.getCode());
709
710    // append cell
711    response = appendValueJson(TABLE, ROW_1, COLUMN_1, VALUE_1);
712    assertEquals(200, response.getCode());
713    putValueJson(TABLE, ROW_1, COLUMN_1, VALUE_1);
714    response = appendValueJson(TABLE, ROW_1, COLUMN_1, VALUE_2);
715    assertEquals(200, response.getCode());
716    putValueJson(TABLE, ROW_1, COLUMN_1, VALUE_1 + VALUE_2);
717
718    response = deleteRow(TABLE, ROW_1);
719    assertEquals(200, response.getCode());
720  }
721
722  @Test
723  public void testIncrementXML() throws IOException, JAXBException {
724    Response response = getValueXML(TABLE, ROW_1, COLUMN_1);
725    assertEquals(404, response.getCode());
726
727    // append single cell
728    response = incrementValueXML(TABLE, ROW_1, COLUMN_1, VALUE_5);
729    assertEquals(200, response.getCode());
730    checkIncrementValueXML(TABLE, ROW_1, COLUMN_1, Long.parseLong(VALUE_5));
731    response = incrementValueXML(TABLE, ROW_1, COLUMN_1, VALUE_6);
732    assertEquals(200, response.getCode());
733    checkIncrementValueXML(TABLE, ROW_1, COLUMN_1,
734      Long.parseLong(VALUE_5) + Long.parseLong(VALUE_6));
735
736    response = deleteRow(TABLE, ROW_1);
737    assertEquals(200, response.getCode());
738  }
739
740  @Test
741  public void testIncrementPB() throws IOException, JAXBException {
742    Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
743    assertEquals(404, response.getCode());
744
745    // append cell
746    response = incrementValuePB(TABLE, ROW_1, COLUMN_1, VALUE_5);
747    assertEquals(200, response.getCode());
748    checkIncrementValuePB(TABLE, ROW_1, COLUMN_1, Long.parseLong(VALUE_5));
749    response = incrementValuePB(TABLE, ROW_1, COLUMN_1, VALUE_6);
750    assertEquals(200, response.getCode());
751    checkIncrementValuePB(TABLE, ROW_1, COLUMN_1,
752      Long.parseLong(VALUE_5) + Long.parseLong(VALUE_6));
753
754    response = deleteRow(TABLE, ROW_1);
755    assertEquals(200, response.getCode());
756  }
757
758  @Test
759  public void testIncrementJSON() throws IOException, JAXBException {
760    Response response = getValueJson(TABLE, ROW_1, COLUMN_1);
761    assertEquals(404, response.getCode());
762
763    // append cell
764    response = incrementValueJson(TABLE, ROW_1, COLUMN_1, VALUE_5);
765    assertEquals(200, response.getCode());
766    checkIncrementValueJSON(TABLE, ROW_1, COLUMN_1, Long.parseLong(VALUE_5));
767    response = incrementValueJson(TABLE, ROW_1, COLUMN_1, VALUE_6);
768    assertEquals(200, response.getCode());
769    checkIncrementValueJSON(TABLE, ROW_1, COLUMN_1,
770      Long.parseLong(VALUE_5) + Long.parseLong(VALUE_6));
771
772    response = deleteRow(TABLE, ROW_1);
773    assertEquals(200, response.getCode());
774  }
775}