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