1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.rest;
22
23 import java.io.ByteArrayInputStream;
24 import java.io.IOException;
25 import java.io.StringWriter;
26 import java.net.URLEncoder;
27
28 import javax.xml.bind.JAXBContext;
29 import javax.xml.bind.JAXBException;
30 import javax.xml.bind.Marshaller;
31 import javax.xml.bind.Unmarshaller;
32
33 import org.apache.commons.httpclient.Header;
34 import org.apache.hadoop.conf.Configuration;
35 import org.apache.hadoop.hbase.HBaseTestingUtility;
36 import org.apache.hadoop.hbase.HColumnDescriptor;
37 import org.apache.hadoop.hbase.HConstants;
38 import org.apache.hadoop.hbase.HTableDescriptor;
39 import org.apache.hadoop.hbase.client.HBaseAdmin;
40 import org.apache.hadoop.hbase.rest.client.Client;
41 import org.apache.hadoop.hbase.rest.client.Cluster;
42 import org.apache.hadoop.hbase.rest.client.Response;
43 import org.apache.hadoop.hbase.rest.model.CellModel;
44 import org.apache.hadoop.hbase.rest.model.CellSetModel;
45 import org.apache.hadoop.hbase.rest.model.RowModel;
46 import org.apache.hadoop.hbase.util.Bytes;
47
48 import static org.junit.Assert.*;
49
50 import org.junit.AfterClass;
51 import org.junit.BeforeClass;
52 import org.junit.Test;
53
54 public class TestRowResource {
55 private static final String TABLE = "TestRowResource";
56 private static final String CFA = "a";
57 private static final String CFB = "b";
58 private static final String COLUMN_1 = CFA + ":1";
59 private static final String COLUMN_2 = CFB + ":2";
60 private static final String ROW_1 = "testrow1";
61 private static final String VALUE_1 = "testvalue1";
62 private static final String ROW_2 = "testrow2";
63 private static final String VALUE_2 = "testvalue2";
64 private static final String ROW_3 = "testrow3";
65 private static final String VALUE_3 = "testvalue3";
66 private static final String ROW_4 = "testrow4";
67 private static final String VALUE_4 = "testvalue4";
68
69 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
70 private static final HBaseRESTTestingUtility REST_TEST_UTIL =
71 new HBaseRESTTestingUtility();
72 private static Client client;
73 private static JAXBContext context;
74 private static Marshaller marshaller;
75 private static Unmarshaller unmarshaller;
76 private static Configuration conf;
77
78 @BeforeClass
79 public static void setUpBeforeClass() throws Exception {
80 conf = TEST_UTIL.getConfiguration();
81 TEST_UTIL.startMiniCluster(3);
82 REST_TEST_UTIL.startServletContainer(conf);
83 context = JAXBContext.newInstance(
84 CellModel.class,
85 CellSetModel.class,
86 RowModel.class);
87 marshaller = context.createMarshaller();
88 unmarshaller = context.createUnmarshaller();
89 client = new Client(new Cluster().add("localhost",
90 REST_TEST_UTIL.getServletPort()));
91 HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
92 if (admin.tableExists(TABLE)) {
93 return;
94 }
95 HTableDescriptor htd = new HTableDescriptor(TABLE);
96 htd.addFamily(new HColumnDescriptor(CFA));
97 htd.addFamily(new HColumnDescriptor(CFB));
98 admin.createTable(htd);
99 }
100
101 @AfterClass
102 public static void tearDownAfterClass() throws Exception {
103 REST_TEST_UTIL.shutdownServletContainer();
104 TEST_UTIL.shutdownMiniCluster();
105 }
106
107 private static Response deleteRow(String table, String row)
108 throws IOException {
109 StringBuilder path = new StringBuilder();
110 path.append('/');
111 path.append(table);
112 path.append('/');
113 path.append(row);
114 Response response = client.delete(path.toString());
115 Thread.yield();
116 return response;
117 }
118
119 private static Response deleteValue(String table, String row, String column)
120 throws IOException {
121 StringBuilder path = new StringBuilder();
122 path.append('/');
123 path.append(table);
124 path.append('/');
125 path.append(row);
126 path.append('/');
127 path.append(column);
128 Response response = client.delete(path.toString());
129 Thread.yield();
130 return response;
131 }
132
133 private static Response getValueXML(String table, String row, String column)
134 throws IOException {
135 StringBuilder path = new StringBuilder();
136 path.append('/');
137 path.append(table);
138 path.append('/');
139 path.append(row);
140 path.append('/');
141 path.append(column);
142 return getValueXML(path.toString());
143 }
144
145 private static Response getValueXML(String table, String startRow,
146 String endRow, String column) throws IOException {
147 StringBuilder path = new StringBuilder();
148 path.append('/');
149 path.append(table);
150 path.append('/');
151 path.append(startRow);
152 path.append(",");
153 path.append(endRow);
154 path.append('/');
155 path.append(column);
156 return getValueXML(path.toString());
157 }
158
159 private static Response getValueXML(String url) throws IOException {
160 Response response = client.get(url, Constants.MIMETYPE_XML);
161 return response;
162 }
163
164 private static Response getValuePB(String table, String row, String column)
165 throws IOException {
166 StringBuilder path = new StringBuilder();
167 path.append('/');
168 path.append(table);
169 path.append('/');
170 path.append(row);
171 path.append('/');
172 path.append(column);
173 return getValuePB(path.toString());
174 }
175
176 private static Response getValuePB(String url) throws IOException {
177 Response response = client.get(url, Constants.MIMETYPE_PROTOBUF);
178 return response;
179 }
180
181 private static Response putValueXML(String table, String row, String column,
182 String value) throws IOException, JAXBException {
183 StringBuilder path = new StringBuilder();
184 path.append('/');
185 path.append(table);
186 path.append('/');
187 path.append(row);
188 path.append('/');
189 path.append(column);
190 return putValueXML(path.toString(), table, row, column, value);
191 }
192
193 private static Response putValueXML(String url, String table, String row,
194 String column, String value) throws IOException, JAXBException {
195 RowModel rowModel = new RowModel(row);
196 rowModel.addCell(new CellModel(Bytes.toBytes(column),
197 Bytes.toBytes(value)));
198 CellSetModel cellSetModel = new CellSetModel();
199 cellSetModel.addRow(rowModel);
200 StringWriter writer = new StringWriter();
201 marshaller.marshal(cellSetModel, writer);
202 Response response = client.put(url, Constants.MIMETYPE_XML,
203 Bytes.toBytes(writer.toString()));
204 Thread.yield();
205 return response;
206 }
207
208 private static void checkValueXML(String table, String row, String column,
209 String value) throws IOException, JAXBException {
210 Response response = getValueXML(table, row, column);
211 assertEquals(response.getCode(), 200);
212 CellSetModel cellSet = (CellSetModel)
213 unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
214 RowModel rowModel = cellSet.getRows().get(0);
215 CellModel cell = rowModel.getCells().get(0);
216 assertEquals(Bytes.toString(cell.getColumn()), column);
217 assertEquals(Bytes.toString(cell.getValue()), value);
218 }
219
220 private static void checkValueXML(String url, String table, String row,
221 String column, String value) throws IOException, JAXBException {
222 Response response = getValueXML(url);
223 assertEquals(response.getCode(), 200);
224 CellSetModel cellSet = (CellSetModel)
225 unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
226 RowModel rowModel = cellSet.getRows().get(0);
227 CellModel cell = rowModel.getCells().get(0);
228 assertEquals(Bytes.toString(cell.getColumn()), column);
229 assertEquals(Bytes.toString(cell.getValue()), value);
230 }
231
232 private static Response putValuePB(String table, String row, String column,
233 String value) throws IOException {
234 StringBuilder path = new StringBuilder();
235 path.append('/');
236 path.append(table);
237 path.append('/');
238 path.append(row);
239 path.append('/');
240 path.append(column);
241 return putValuePB(path.toString(), table, row, column, value);
242 }
243
244 private static Response putValuePB(String url, String table, String row,
245 String column, String value) throws IOException {
246 RowModel rowModel = new RowModel(row);
247 rowModel.addCell(new CellModel(Bytes.toBytes(column),
248 Bytes.toBytes(value)));
249 CellSetModel cellSetModel = new CellSetModel();
250 cellSetModel.addRow(rowModel);
251 Response response = client.put(url, Constants.MIMETYPE_PROTOBUF,
252 cellSetModel.createProtobufOutput());
253 Thread.yield();
254 return response;
255 }
256
257 private static void checkValuePB(String table, String row, String column,
258 String value) throws IOException {
259 Response response = getValuePB(table, row, column);
260 assertEquals(response.getCode(), 200);
261 CellSetModel cellSet = new CellSetModel();
262 cellSet.getObjectFromMessage(response.getBody());
263 RowModel rowModel = cellSet.getRows().get(0);
264 CellModel cell = rowModel.getCells().get(0);
265 assertEquals(Bytes.toString(cell.getColumn()), column);
266 assertEquals(Bytes.toString(cell.getValue()), value);
267 }
268
269 @Test
270 public void testDelete() throws IOException, JAXBException {
271 Response response;
272
273 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
274 assertEquals(response.getCode(), 200);
275 response = putValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
276 assertEquals(response.getCode(), 200);
277 checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
278 checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
279
280 response = deleteValue(TABLE, ROW_1, COLUMN_1);
281 assertEquals(response.getCode(), 200);
282 response = getValueXML(TABLE, ROW_1, COLUMN_1);
283 assertEquals(response.getCode(), 404);
284 checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
285
286 response = deleteRow(TABLE, ROW_1);
287 assertEquals(response.getCode(), 200);
288 response = getValueXML(TABLE, ROW_1, COLUMN_1);
289 assertEquals(response.getCode(), 404);
290 response = getValueXML(TABLE, ROW_1, COLUMN_2);
291 assertEquals(response.getCode(), 404);
292 }
293
294 @Test
295 public void testForbidden() throws IOException, JAXBException {
296 Response response;
297
298 conf.set("hbase.rest.readonly", "true");
299
300 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
301 assertEquals(response.getCode(), 403);
302 response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
303 assertEquals(response.getCode(), 403);
304 response = deleteValue(TABLE, ROW_1, COLUMN_1);
305 assertEquals(response.getCode(), 403);
306 response = deleteRow(TABLE, ROW_1);
307 assertEquals(response.getCode(), 403);
308
309 conf.set("hbase.rest.readonly", "false");
310
311 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
312 assertEquals(response.getCode(), 200);
313 response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
314 assertEquals(response.getCode(), 200);
315 response = deleteValue(TABLE, ROW_1, COLUMN_1);
316 assertEquals(response.getCode(), 200);
317 response = deleteRow(TABLE, ROW_1);
318 assertEquals(response.getCode(), 200);
319 }
320
321 @Test
322 public void testSingleCellGetPutXML() throws IOException, JAXBException {
323 Response response = getValueXML(TABLE, ROW_1, COLUMN_1);
324 assertEquals(response.getCode(), 404);
325
326 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
327 assertEquals(response.getCode(), 200);
328 checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
329 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
330 assertEquals(response.getCode(), 200);
331 checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
332
333 response = deleteRow(TABLE, ROW_1);
334 assertEquals(response.getCode(), 200);
335 }
336
337 @Test
338 public void testSingleCellGetPutPB() throws IOException, JAXBException {
339 Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
340 assertEquals(response.getCode(), 404);
341
342 response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
343 assertEquals(response.getCode(), 200);
344 checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
345
346 response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
347 assertEquals(response.getCode(), 200);
348 checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
349 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
350 assertEquals(response.getCode(), 200);
351 checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2);
352
353 response = deleteRow(TABLE, ROW_1);
354 assertEquals(response.getCode(), 200);
355 }
356
357 @Test
358 public void testSingleCellGetPutBinary() throws IOException {
359 final String path = "/" + TABLE + "/" + ROW_3 + "/" + COLUMN_1;
360 final byte[] body = Bytes.toBytes(VALUE_3);
361 Response response = client.put(path, Constants.MIMETYPE_BINARY, body);
362 assertEquals(response.getCode(), 200);
363 Thread.yield();
364
365 response = client.get(path, Constants.MIMETYPE_BINARY);
366 assertEquals(response.getCode(), 200);
367 assertTrue(Bytes.equals(response.getBody(), body));
368 boolean foundTimestampHeader = false;
369 for (Header header: response.getHeaders()) {
370 if (header.getName().equals("X-Timestamp")) {
371 foundTimestampHeader = true;
372 break;
373 }
374 }
375 assertTrue(foundTimestampHeader);
376
377 response = deleteRow(TABLE, ROW_3);
378 assertEquals(response.getCode(), 200);
379 }
380
381 @Test
382 public void testSingleCellGetJSON() throws IOException, JAXBException {
383 final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
384 Response response = client.put(path, Constants.MIMETYPE_BINARY,
385 Bytes.toBytes(VALUE_4));
386 assertEquals(response.getCode(), 200);
387 Thread.yield();
388 response = client.get(path, Constants.MIMETYPE_JSON);
389 assertEquals(response.getCode(), 200);
390 response = deleteRow(TABLE, ROW_4);
391 assertEquals(response.getCode(), 200);
392 }
393
394 @Test
395 public void testURLEncodedKey() throws IOException, JAXBException {
396 String urlKey = "http://example.com/foo";
397 StringBuilder path = new StringBuilder();
398 path.append('/');
399 path.append(TABLE);
400 path.append('/');
401 path.append(URLEncoder.encode(urlKey, HConstants.UTF8_ENCODING));
402 path.append('/');
403 path.append(COLUMN_1);
404 Response response;
405 response = putValueXML(path.toString(), TABLE, urlKey, COLUMN_1,
406 VALUE_1);
407 assertEquals(response.getCode(), 200);
408 checkValueXML(path.toString(), TABLE, urlKey, COLUMN_1, VALUE_1);
409 }
410
411 @Test
412 public void testNoSuchCF() throws IOException, JAXBException {
413 final String goodPath = "/" + TABLE + "/" + ROW_1 + "/" + CFA+":";
414 final String badPath = "/" + TABLE + "/" + ROW_1 + "/" + "BAD";
415 Response response = client.post(goodPath, Constants.MIMETYPE_BINARY,
416 Bytes.toBytes(VALUE_1));
417 assertEquals(response.getCode(), 200);
418 assertEquals(client.get(goodPath, Constants.MIMETYPE_BINARY).getCode(),
419 200);
420 assertEquals(client.get(badPath, Constants.MIMETYPE_BINARY).getCode(),
421 404);
422 assertEquals(client.get(goodPath, Constants.MIMETYPE_BINARY).getCode(),
423 200);
424 }
425
426 @Test
427 public void testMultiCellGetPutXML() throws IOException, JAXBException {
428 String path = "/" + TABLE + "/fakerow";
429
430 CellSetModel cellSetModel = new CellSetModel();
431 RowModel rowModel = new RowModel(ROW_1);
432 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
433 Bytes.toBytes(VALUE_1)));
434 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
435 Bytes.toBytes(VALUE_2)));
436 cellSetModel.addRow(rowModel);
437 rowModel = new RowModel(ROW_2);
438 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
439 Bytes.toBytes(VALUE_3)));
440 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
441 Bytes.toBytes(VALUE_4)));
442 cellSetModel.addRow(rowModel);
443 StringWriter writer = new StringWriter();
444 marshaller.marshal(cellSetModel, writer);
445 Response response = client.put(path, Constants.MIMETYPE_XML,
446 Bytes.toBytes(writer.toString()));
447 Thread.yield();
448
449
450 response = client.get(path, Constants.MIMETYPE_XML);
451 assertEquals(response.getCode(), 404);
452
453
454 checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
455 checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
456 checkValueXML(TABLE, ROW_2, COLUMN_1, VALUE_3);
457 checkValueXML(TABLE, ROW_2, COLUMN_2, VALUE_4);
458
459 response = deleteRow(TABLE, ROW_1);
460 assertEquals(response.getCode(), 200);
461 response = deleteRow(TABLE, ROW_2);
462 assertEquals(response.getCode(), 200);
463 }
464
465 @Test
466 public void testMultiCellGetPutPB() throws IOException {
467 String path = "/" + TABLE + "/fakerow";
468
469 CellSetModel cellSetModel = new CellSetModel();
470 RowModel rowModel = new RowModel(ROW_1);
471 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
472 Bytes.toBytes(VALUE_1)));
473 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
474 Bytes.toBytes(VALUE_2)));
475 cellSetModel.addRow(rowModel);
476 rowModel = new RowModel(ROW_2);
477 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
478 Bytes.toBytes(VALUE_3)));
479 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
480 Bytes.toBytes(VALUE_4)));
481 cellSetModel.addRow(rowModel);
482 Response response = client.put(path, Constants.MIMETYPE_PROTOBUF,
483 cellSetModel.createProtobufOutput());
484 Thread.yield();
485
486
487 response = client.get(path, Constants.MIMETYPE_PROTOBUF);
488 assertEquals(response.getCode(), 404);
489
490
491 checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
492 checkValuePB(TABLE, ROW_1, COLUMN_2, VALUE_2);
493 checkValuePB(TABLE, ROW_2, COLUMN_1, VALUE_3);
494 checkValuePB(TABLE, ROW_2, COLUMN_2, VALUE_4);
495
496 response = deleteRow(TABLE, ROW_1);
497 assertEquals(response.getCode(), 200);
498 response = deleteRow(TABLE, ROW_2);
499 assertEquals(response.getCode(), 200);
500 }
501
502 @Test
503 public void testStartEndRowGetPutXML() throws IOException, JAXBException {
504 String[] rows = { ROW_1, ROW_2, ROW_3 };
505 String[] values = { VALUE_1, VALUE_2, VALUE_3 };
506 Response response = null;
507 for (int i = 0; i < rows.length; i++) {
508 response = putValueXML(TABLE, rows[i], COLUMN_1, values[i]);
509 assertEquals(200, response.getCode());
510 checkValueXML(TABLE, rows[i], COLUMN_1, values[i]);
511 }
512 response = getValueXML(TABLE, rows[0], rows[2], COLUMN_1);
513 assertEquals(200, response.getCode());
514 CellSetModel cellSet = (CellSetModel)
515 unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
516 assertEquals(2, cellSet.getRows().size());
517 for (int i = 0; i < cellSet.getRows().size()-1; i++) {
518 RowModel rowModel = cellSet.getRows().get(i);
519 for (CellModel cell: rowModel.getCells()) {
520 assertEquals(COLUMN_1, Bytes.toString(cell.getColumn()));
521 assertEquals(values[i], Bytes.toString(cell.getValue()));
522 }
523 }
524 for (String row : rows) {
525 response = deleteRow(TABLE, row);
526 assertEquals(200, response.getCode());
527 }
528 }
529 }