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