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    String urlKey = "http://example.com/foo";
325    StringBuilder path = new StringBuilder();
326    path.append('/');
327    path.append(TABLE);
328    path.append('/');
329    path.append(URLEncoder.encode(urlKey, HConstants.UTF8_ENCODING));
330    path.append('/');
331    path.append(COLUMN_1);
332    Response response;
333    response = putValueXML(path.toString(), TABLE, urlKey, COLUMN_1, VALUE_1);
334    assertEquals(200, response.getCode());
335    checkValueXML(path.toString(), TABLE, urlKey, COLUMN_1, VALUE_1);
336  }
337
338  private void setupValue1() throws IOException, JAXBException {
339    StringBuilder path = new StringBuilder();
340    path.append('/');
341    path.append(TABLE);
342    path.append('/');
343    path.append(ROW_1);
344    path.append('/');
345    path.append(COLUMN_1);
346    Response response = putValueXML(path.toString(), TABLE, ROW_1, COLUMN_1, VALUE_1);
347    assertEquals(200, response.getCode());
348  }
349
350  private void checkValue1(Response getResponse) throws JAXBException {
351    assertEquals(Constants.MIMETYPE_XML, getResponse.getHeader("content-type"));
352
353    CellSetModel cellSet =
354      (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(getResponse.getBody()));
355    assertEquals(1, cellSet.getRows().size());
356    RowModel rowModel = cellSet.getRows().get(0);
357    assertEquals(ROW_1, new String(rowModel.getKey()));
358    assertEquals(1, rowModel.getCells().size());
359    CellModel cell = rowModel.getCells().get(0);
360    assertEquals(COLUMN_1, new String(cell.getColumn()));
361    assertEquals(VALUE_1, new String(cell.getValue()));
362  }
363
364  // See https://issues.apache.org/jira/browse/HBASE-28174
365  @Test
366  public void testUrlB64EncodedKeyQueryParam() throws IOException, JAXBException {
367    setupValue1();
368
369    StringBuilder path = new StringBuilder();
370    Base64.Encoder encoder = Base64.getUrlEncoder();
371    path.append('/');
372    path.append(TABLE);
373    path.append('/');
374    path.append(encoder.encodeToString(ROW_1.getBytes("UTF-8")));
375    path.append('/');
376    path.append(encoder.encodeToString(COLUMN_1.getBytes("UTF-8")));
377    path.append("?e=b64");
378    Response response = getValueXML(path.toString());
379    assertEquals(200, response.getCode());
380
381    checkValue1(response);
382  }
383
384  // See https://issues.apache.org/jira/browse/HBASE-28174
385  @Test
386  public void testUrlB64EncodedKeyHeader() throws IOException, JAXBException {
387    setupValue1();
388
389    StringBuilder path = new StringBuilder();
390    Base64.Encoder encoder = Base64.getUrlEncoder();
391    path.append('/');
392    path.append(TABLE);
393    path.append('/');
394    path.append(encoder.encodeToString(ROW_1.getBytes("UTF-8")));
395    path.append('/');
396    path.append(encoder.encodeToString(COLUMN_1.getBytes("UTF-8")));
397    Response response =
398      getValueXML(path.toString(), new Header[] { new BasicHeader("Encoding", "b64") });
399    assertEquals(200, response.getCode());
400
401    checkValue1(response);
402  }
403
404  @Test
405  public void testNoSuchCF() throws IOException {
406    final String goodPath = "/" + TABLE + "/" + ROW_1 + "/" + CFA + ":";
407    final String badPath = "/" + TABLE + "/" + ROW_1 + "/" + "BAD";
408    Response response = client.post(goodPath, Constants.MIMETYPE_BINARY, Bytes.toBytes(VALUE_1));
409    assertEquals(200, response.getCode());
410    assertEquals(200, client.get(goodPath, Constants.MIMETYPE_BINARY).getCode());
411    assertEquals(404, client.get(badPath, Constants.MIMETYPE_BINARY).getCode());
412    assertEquals(200, client.get(goodPath, Constants.MIMETYPE_BINARY).getCode());
413  }
414
415  @Test
416  public void testMultiCellGetPutXML() throws IOException, JAXBException {
417    String path = "/" + TABLE + "/fakerow"; // deliberate nonexistent row
418
419    CellSetModel cellSetModel = new CellSetModel();
420    RowModel rowModel = new RowModel(ROW_1);
421    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
422    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
423    cellSetModel.addRow(rowModel);
424    rowModel = new RowModel(ROW_2);
425    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_3)));
426    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_4)));
427    cellSetModel.addRow(rowModel);
428    StringWriter writer = new StringWriter();
429    xmlMarshaller.marshal(cellSetModel, writer);
430    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
431    Thread.yield();
432
433    // make sure the fake row was not actually created
434    response = client.get(path, Constants.MIMETYPE_XML);
435    assertEquals(404, response.getCode());
436
437    // check that all of the values were created
438    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
439    checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
440    checkValueXML(TABLE, ROW_2, COLUMN_1, VALUE_3);
441    checkValueXML(TABLE, ROW_2, COLUMN_2, VALUE_4);
442
443    response = deleteRow(TABLE, ROW_1);
444    assertEquals(200, response.getCode());
445    response = deleteRow(TABLE, ROW_2);
446    assertEquals(200, response.getCode());
447  }
448
449  @Test
450  public void testMultiCellGetPutPB() throws IOException {
451    String path = "/" + TABLE + "/fakerow"; // deliberate nonexistent row
452
453    CellSetModel cellSetModel = new CellSetModel();
454    RowModel rowModel = new RowModel(ROW_1);
455    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
456    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
457    cellSetModel.addRow(rowModel);
458    rowModel = new RowModel(ROW_2);
459    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_3)));
460    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_4)));
461    cellSetModel.addRow(rowModel);
462    Response response =
463      client.put(path, Constants.MIMETYPE_PROTOBUF, cellSetModel.createProtobufOutput());
464    Thread.yield();
465
466    // make sure the fake row was not actually created
467    response = client.get(path, Constants.MIMETYPE_PROTOBUF);
468    assertEquals(404, response.getCode());
469
470    // check that all of the values were created
471    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
472    checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
473    checkValuePB(TABLE, ROW_2, COLUMN_1, VALUE_3);
474    checkValuePB(TABLE, ROW_2, COLUMN_2, VALUE_4);
475
476    response = deleteRow(TABLE, ROW_1);
477    assertEquals(200, response.getCode());
478    response = deleteRow(TABLE, ROW_2);
479    assertEquals(200, response.getCode());
480  }
481
482  @Test
483  public void testStartEndRowGetPutXML() throws IOException, JAXBException {
484    String[] rows = { ROW_1, ROW_2, ROW_3 };
485    String[] values = { VALUE_1, VALUE_2, VALUE_3 };
486    Response response = null;
487    for (int i = 0; i < rows.length; i++) {
488      response = putValueXML(TABLE, rows[i], COLUMN_1, values[i]);
489      assertEquals(200, response.getCode());
490      checkValueXML(TABLE, rows[i], COLUMN_1, values[i]);
491    }
492    response = getValueXML(TABLE, rows[0], rows[2], COLUMN_1);
493    assertEquals(200, response.getCode());
494    CellSetModel cellSet =
495      (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
496    assertEquals(2, cellSet.getRows().size());
497    for (int i = 0; i < cellSet.getRows().size() - 1; i++) {
498      RowModel rowModel = cellSet.getRows().get(i);
499      for (CellModel cell : rowModel.getCells()) {
500        assertEquals(COLUMN_1, Bytes.toString(cell.getColumn()));
501        assertEquals(values[i], Bytes.toString(cell.getValue()));
502      }
503    }
504    for (String row : rows) {
505      response = deleteRow(TABLE, row);
506      assertEquals(200, response.getCode());
507    }
508  }
509
510  @Test
511  public void testInvalidCheckParam() throws IOException, JAXBException {
512    CellSetModel cellSetModel = new CellSetModel();
513    RowModel rowModel = new RowModel(ROW_1);
514    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
515    cellSetModel.addRow(rowModel);
516    StringWriter writer = new StringWriter();
517    xmlMarshaller.marshal(cellSetModel, writer);
518
519    final String path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1 + "?check=blah";
520
521    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
522    assertEquals(400, response.getCode());
523  }
524
525  @Test
526  public void testInvalidColumnPut() throws IOException, JAXBException {
527    String dummyColumn = "doesnot:exist";
528    CellSetModel cellSetModel = new CellSetModel();
529    RowModel rowModel = new RowModel(ROW_1);
530    rowModel.addCell(new CellModel(Bytes.toBytes(dummyColumn), Bytes.toBytes(VALUE_1)));
531    cellSetModel.addRow(rowModel);
532    StringWriter writer = new StringWriter();
533    xmlMarshaller.marshal(cellSetModel, writer);
534
535    final String path = "/" + TABLE + "/" + ROW_1 + "/" + dummyColumn;
536
537    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
538    assertEquals(404, response.getCode());
539  }
540
541  @Test
542  public void testMultiCellGetJson() throws IOException, JAXBException {
543    String path = "/" + TABLE + "/fakerow"; // deliberate nonexistent row
544
545    CellSetModel cellSetModel = new CellSetModel();
546    RowModel rowModel = new RowModel(ROW_1);
547    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
548    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
549    cellSetModel.addRow(rowModel);
550    rowModel = new RowModel(ROW_2);
551    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_3)));
552    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_4)));
553    cellSetModel.addRow(rowModel);
554    String jsonString = jsonMapper.writeValueAsString(cellSetModel);
555
556    Response response = client.put(path, Constants.MIMETYPE_JSON, Bytes.toBytes(jsonString));
557    Thread.yield();
558
559    // make sure the fake row was not actually created
560    response = client.get(path, Constants.MIMETYPE_JSON);
561    assertEquals(404, response.getCode());
562
563    // check that all of the values were created
564    checkValueJSON(TABLE, ROW_1, COLUMN_1, VALUE_1);
565    checkValueJSON(TABLE, ROW_1, COLUMN_2, VALUE_2);
566    checkValueJSON(TABLE, ROW_2, COLUMN_1, VALUE_3);
567    checkValueJSON(TABLE, ROW_2, COLUMN_2, VALUE_4);
568
569    response = deleteRow(TABLE, ROW_1);
570    assertEquals(200, response.getCode());
571    response = deleteRow(TABLE, ROW_2);
572    assertEquals(200, response.getCode());
573  }
574
575  @Test
576  public void testMetrics() throws IOException {
577    final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
578    Response response = client.put(path, Constants.MIMETYPE_BINARY, Bytes.toBytes(VALUE_4));
579    assertEquals(200, response.getCode());
580    Thread.yield();
581    response = client.get(path, Constants.MIMETYPE_JSON);
582    assertEquals(200, response.getCode());
583    assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
584    response = deleteRow(TABLE, ROW_4);
585    assertEquals(200, response.getCode());
586
587    UserProvider userProvider = UserProvider.instantiate(conf);
588    METRICS_ASSERT.assertCounterGt("requests", 2L,
589      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
590
591    METRICS_ASSERT.assertCounterGt("successfulGet", 0L,
592      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
593
594    METRICS_ASSERT.assertCounterGt("successfulPut", 0L,
595      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
596
597    METRICS_ASSERT.assertCounterGt("successfulDelete", 0L,
598      RESTServlet.getInstance(conf, userProvider).getMetrics().getSource());
599  }
600
601  @Test
602  public void testMultiColumnGetXML() throws Exception {
603    String path = "/" + TABLE + "/fakerow";
604    CellSetModel cellSetModel = new CellSetModel();
605    RowModel rowModel = new RowModel(ROW_1);
606    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
607    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
608    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_3), Bytes.toBytes(VALUE_2)));
609    cellSetModel.addRow(rowModel);
610    StringWriter writer = new StringWriter();
611    xmlMarshaller.marshal(cellSetModel, writer);
612
613    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
614    Thread.yield();
615
616    // make sure the fake row was not actually created
617    response = client.get(path, Constants.MIMETYPE_XML);
618    assertEquals(404, response.getCode());
619
620    // Try getting all the column values at once.
621    path = "/" + TABLE + "/" + ROW_1 + "/" + COLUMN_1 + "," + COLUMN_2 + "," + COLUMN_3;
622    response = client.get(path, Constants.MIMETYPE_XML);
623    assertEquals(200, response.getCode());
624    CellSetModel cellSet =
625      (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
626    assertTrue(cellSet.getRows().size() == 1);
627    assertTrue(cellSet.getRows().get(0).getCells().size() == 3);
628    List<CellModel> cells = cellSet.getRows().get(0).getCells();
629
630    assertTrue(containsCellModel(cells, COLUMN_1, VALUE_1));
631    assertTrue(containsCellModel(cells, COLUMN_2, VALUE_2));
632    assertTrue(containsCellModel(cells, COLUMN_3, VALUE_2));
633    response = deleteRow(TABLE, ROW_1);
634    assertEquals(200, response.getCode());
635  }
636
637  private boolean containsCellModel(List<CellModel> cells, String column, String value) {
638    boolean contains = false;
639    for (CellModel cell : cells) {
640      if (
641        Bytes.toString(cell.getColumn()).equals(column)
642          && Bytes.toString(cell.getValue()).equals(value)
643      ) {
644        contains = true;
645        return contains;
646      }
647    }
648    return contains;
649  }
650
651  @Test
652  public void testSuffixGlobbingXMLWithNewScanner() throws IOException, JAXBException {
653    String path = "/" + TABLE + "/fakerow"; // deliberate nonexistent row
654
655    CellSetModel cellSetModel = new CellSetModel();
656    RowModel rowModel = new RowModel(ROW_1);
657    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
658    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
659    cellSetModel.addRow(rowModel);
660    rowModel = new RowModel(ROW_2);
661    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_3)));
662    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_4)));
663    cellSetModel.addRow(rowModel);
664    StringWriter writer = new StringWriter();
665    xmlMarshaller.marshal(cellSetModel, writer);
666    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
667    Thread.yield();
668
669    // make sure the fake row was not actually created
670    response = client.get(path, Constants.MIMETYPE_XML);
671    assertEquals(404, response.getCode());
672
673    // check that all of the values were created
674    StringBuilder query = new StringBuilder();
675    query.append('/');
676    query.append(TABLE);
677    query.append('/');
678    query.append("testrow*");
679    response = client.get(query.toString(), Constants.MIMETYPE_XML);
680    assertEquals(200, response.getCode());
681    assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
682    CellSetModel cellSet =
683      (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
684    assertTrue(cellSet.getRows().size() == 2);
685
686    response = deleteRow(TABLE, ROW_1);
687    assertEquals(200, response.getCode());
688    response = deleteRow(TABLE, ROW_2);
689    assertEquals(200, response.getCode());
690  }
691
692  @Test
693  public void testSuffixGlobbingXML() throws IOException, JAXBException {
694    String path = "/" + TABLE + "/fakerow"; // deliberate nonexistent row
695
696    CellSetModel cellSetModel = new CellSetModel();
697    RowModel rowModel = new RowModel(ROW_1);
698    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_1)));
699    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_2)));
700    cellSetModel.addRow(rowModel);
701    rowModel = new RowModel(ROW_2);
702    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1), Bytes.toBytes(VALUE_3)));
703    rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2), Bytes.toBytes(VALUE_4)));
704    cellSetModel.addRow(rowModel);
705    StringWriter writer = new StringWriter();
706    xmlMarshaller.marshal(cellSetModel, writer);
707    Response response = client.put(path, Constants.MIMETYPE_XML, Bytes.toBytes(writer.toString()));
708    Thread.yield();
709
710    // make sure the fake row was not actually created
711    response = client.get(path, Constants.MIMETYPE_XML);
712    assertEquals(404, response.getCode());
713
714    // check that all of the values were created
715    StringBuilder query = new StringBuilder();
716    query.append('/');
717    query.append(TABLE);
718    query.append('/');
719    query.append("testrow*");
720    query.append('/');
721    query.append(COLUMN_1);
722    response = client.get(query.toString(), Constants.MIMETYPE_XML);
723    assertEquals(200, response.getCode());
724    assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
725    CellSetModel cellSet =
726      (CellSetModel) xmlUnmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
727    List<RowModel> rows = cellSet.getRows();
728    assertTrue(rows.size() == 2);
729    for (RowModel row : rows) {
730      assertTrue(row.getCells().size() == 1);
731      assertEquals(COLUMN_1, Bytes.toString(row.getCells().get(0).getColumn()));
732    }
733    response = deleteRow(TABLE, ROW_1);
734    assertEquals(200, response.getCode());
735    response = deleteRow(TABLE, ROW_2);
736    assertEquals(200, response.getCode());
737  }
738
739  @Test
740  public void testAppendXML() throws IOException, JAXBException {
741    Response response = getValueXML(TABLE, ROW_1, COLUMN_1);
742    assertEquals(404, response.getCode());
743
744    // append cell
745    response = appendValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
746    assertEquals(200, response.getCode());
747    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
748    response = appendValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
749    assertEquals(200, response.getCode());
750    checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1 + VALUE_2);
751
752    response = deleteRow(TABLE, ROW_1);
753    assertEquals(200, response.getCode());
754  }
755
756  @Test
757  public void testAppendPB() throws IOException, JAXBException {
758    Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
759    assertEquals(404, response.getCode());
760
761    // append cell
762    response = appendValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
763    assertEquals(200, response.getCode());
764    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
765    response = appendValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2);
766    assertEquals(200, response.getCode());
767    checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1 + VALUE_2);
768
769    response = deleteRow(TABLE, ROW_1);
770    assertEquals(200, response.getCode());
771  }
772
773  @Test
774  public void testAppendJSON() throws IOException, JAXBException {
775    Response response = getValueJson(TABLE, ROW_1, COLUMN_1);
776    assertEquals(404, response.getCode());
777
778    // append cell
779    response = appendValueJson(TABLE, ROW_1, COLUMN_1, VALUE_1);
780    assertEquals(200, response.getCode());
781    putValueJson(TABLE, ROW_1, COLUMN_1, VALUE_1);
782    response = appendValueJson(TABLE, ROW_1, COLUMN_1, VALUE_2);
783    assertEquals(200, response.getCode());
784    putValueJson(TABLE, ROW_1, COLUMN_1, VALUE_1 + VALUE_2);
785
786    response = deleteRow(TABLE, ROW_1);
787    assertEquals(200, response.getCode());
788  }
789
790  @Test
791  public void testIncrementXML() throws IOException, JAXBException {
792    Response response = getValueXML(TABLE, ROW_1, COLUMN_1);
793    assertEquals(404, response.getCode());
794
795    // append single cell
796    response = incrementValueXML(TABLE, ROW_1, COLUMN_1, VALUE_5);
797    assertEquals(200, response.getCode());
798    checkIncrementValueXML(TABLE, ROW_1, COLUMN_1, Long.parseLong(VALUE_5));
799    response = incrementValueXML(TABLE, ROW_1, COLUMN_1, VALUE_6);
800    assertEquals(200, response.getCode());
801    checkIncrementValueXML(TABLE, ROW_1, COLUMN_1,
802      Long.parseLong(VALUE_5) + Long.parseLong(VALUE_6));
803
804    response = deleteRow(TABLE, ROW_1);
805    assertEquals(200, response.getCode());
806  }
807
808  @Test
809  public void testIncrementPB() throws IOException, JAXBException {
810    Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
811    assertEquals(404, response.getCode());
812
813    // append cell
814    response = incrementValuePB(TABLE, ROW_1, COLUMN_1, VALUE_5);
815    assertEquals(200, response.getCode());
816    checkIncrementValuePB(TABLE, ROW_1, COLUMN_1, Long.parseLong(VALUE_5));
817    response = incrementValuePB(TABLE, ROW_1, COLUMN_1, VALUE_6);
818    assertEquals(200, response.getCode());
819    checkIncrementValuePB(TABLE, ROW_1, COLUMN_1,
820      Long.parseLong(VALUE_5) + Long.parseLong(VALUE_6));
821
822    response = deleteRow(TABLE, ROW_1);
823    assertEquals(200, response.getCode());
824  }
825
826  @Test
827  public void testIncrementJSON() throws IOException, JAXBException {
828    Response response = getValueJson(TABLE, ROW_1, COLUMN_1);
829    assertEquals(404, response.getCode());
830
831    // append cell
832    response = incrementValueJson(TABLE, ROW_1, COLUMN_1, VALUE_5);
833    assertEquals(200, response.getCode());
834    checkIncrementValueJSON(TABLE, ROW_1, COLUMN_1, Long.parseLong(VALUE_5));
835    response = incrementValueJson(TABLE, ROW_1, COLUMN_1, VALUE_6);
836    assertEquals(200, response.getCode());
837    checkIncrementValueJSON(TABLE, ROW_1, COLUMN_1,
838      Long.parseLong(VALUE_5) + Long.parseLong(VALUE_6));
839
840    response = deleteRow(TABLE, ROW_1);
841    assertEquals(200, response.getCode());
842  }
843}