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 url) throws IOException {
145 Response response = client.get(url, Constants.MIMETYPE_XML);
146 return response;
147 }
148
149 private static Response getValuePB(String table, String row, String column)
150 throws IOException {
151 StringBuilder path = new StringBuilder();
152 path.append('/');
153 path.append(table);
154 path.append('/');
155 path.append(row);
156 path.append('/');
157 path.append(column);
158 return getValuePB(path.toString());
159 }
160
161 private static Response getValuePB(String url) throws IOException {
162 Response response = client.get(url, Constants.MIMETYPE_PROTOBUF);
163 return response;
164 }
165
166 private static Response putValueXML(String table, String row, String column,
167 String value) throws IOException, JAXBException {
168 StringBuilder path = new StringBuilder();
169 path.append('/');
170 path.append(table);
171 path.append('/');
172 path.append(row);
173 path.append('/');
174 path.append(column);
175 return putValueXML(path.toString(), table, row, column, value);
176 }
177
178 private static Response putValueXML(String url, String table, String row,
179 String column, String value) throws IOException, JAXBException {
180 RowModel rowModel = new RowModel(row);
181 rowModel.addCell(new CellModel(Bytes.toBytes(column),
182 Bytes.toBytes(value)));
183 CellSetModel cellSetModel = new CellSetModel();
184 cellSetModel.addRow(rowModel);
185 StringWriter writer = new StringWriter();
186 marshaller.marshal(cellSetModel, writer);
187 Response response = client.put(url, Constants.MIMETYPE_XML,
188 Bytes.toBytes(writer.toString()));
189 Thread.yield();
190 return response;
191 }
192
193 private static void checkValueXML(String table, String row, String column,
194 String value) throws IOException, JAXBException {
195 Response response = getValueXML(table, row, column);
196 assertEquals(response.getCode(), 200);
197 CellSetModel cellSet = (CellSetModel)
198 unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
199 RowModel rowModel = cellSet.getRows().get(0);
200 CellModel cell = rowModel.getCells().get(0);
201 assertEquals(Bytes.toString(cell.getColumn()), column);
202 assertEquals(Bytes.toString(cell.getValue()), value);
203 }
204
205 private static void checkValueXML(String url, String table, String row,
206 String column, String value) throws IOException, JAXBException {
207 Response response = getValueXML(url);
208 assertEquals(response.getCode(), 200);
209 CellSetModel cellSet = (CellSetModel)
210 unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
211 RowModel rowModel = cellSet.getRows().get(0);
212 CellModel cell = rowModel.getCells().get(0);
213 assertEquals(Bytes.toString(cell.getColumn()), column);
214 assertEquals(Bytes.toString(cell.getValue()), value);
215 }
216
217 private static Response putValuePB(String table, String row, String column,
218 String value) throws IOException {
219 StringBuilder path = new StringBuilder();
220 path.append('/');
221 path.append(table);
222 path.append('/');
223 path.append(row);
224 path.append('/');
225 path.append(column);
226 return putValuePB(path.toString(), table, row, column, value);
227 }
228
229 private static Response putValuePB(String url, String table, String row,
230 String column, String value) throws IOException {
231 RowModel rowModel = new RowModel(row);
232 rowModel.addCell(new CellModel(Bytes.toBytes(column),
233 Bytes.toBytes(value)));
234 CellSetModel cellSetModel = new CellSetModel();
235 cellSetModel.addRow(rowModel);
236 Response response = client.put(url, Constants.MIMETYPE_PROTOBUF,
237 cellSetModel.createProtobufOutput());
238 Thread.yield();
239 return response;
240 }
241
242 private static void checkValuePB(String table, String row, String column,
243 String value) throws IOException {
244 Response response = getValuePB(table, row, column);
245 assertEquals(response.getCode(), 200);
246 CellSetModel cellSet = new CellSetModel();
247 cellSet.getObjectFromMessage(response.getBody());
248 RowModel rowModel = cellSet.getRows().get(0);
249 CellModel cell = rowModel.getCells().get(0);
250 assertEquals(Bytes.toString(cell.getColumn()), column);
251 assertEquals(Bytes.toString(cell.getValue()), value);
252 }
253
254 @Test
255 public void testDelete() throws IOException, JAXBException {
256 Response response;
257
258 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
259 assertEquals(response.getCode(), 200);
260 response = putValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
261 assertEquals(response.getCode(), 200);
262 checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
263 checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
264
265 response = deleteValue(TABLE, ROW_1, COLUMN_1);
266 assertEquals(response.getCode(), 200);
267 response = getValueXML(TABLE, ROW_1, COLUMN_1);
268 assertEquals(response.getCode(), 404);
269 checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
270
271 response = deleteRow(TABLE, ROW_1);
272 assertEquals(response.getCode(), 200);
273 response = getValueXML(TABLE, ROW_1, COLUMN_1);
274 assertEquals(response.getCode(), 404);
275 response = getValueXML(TABLE, ROW_1, COLUMN_2);
276 assertEquals(response.getCode(), 404);
277 }
278
279 @Test
280 public void testForbidden() throws IOException, JAXBException {
281 Response response;
282
283 conf.set("hbase.rest.readonly", "true");
284
285 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
286 assertEquals(response.getCode(), 403);
287 response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
288 assertEquals(response.getCode(), 403);
289 response = deleteValue(TABLE, ROW_1, COLUMN_1);
290 assertEquals(response.getCode(), 403);
291 response = deleteRow(TABLE, ROW_1);
292 assertEquals(response.getCode(), 403);
293
294 conf.set("hbase.rest.readonly", "false");
295
296 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
297 assertEquals(response.getCode(), 200);
298 response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
299 assertEquals(response.getCode(), 200);
300 response = deleteValue(TABLE, ROW_1, COLUMN_1);
301 assertEquals(response.getCode(), 200);
302 response = deleteRow(TABLE, ROW_1);
303 assertEquals(response.getCode(), 200);
304 }
305
306 @Test
307 public void testSingleCellGetPutXML() throws IOException, JAXBException {
308 Response response = getValueXML(TABLE, ROW_1, COLUMN_1);
309 assertEquals(response.getCode(), 404);
310
311 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
312 assertEquals(response.getCode(), 200);
313 checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
314 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
315 assertEquals(response.getCode(), 200);
316 checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
317
318 response = deleteRow(TABLE, ROW_1);
319 assertEquals(response.getCode(), 200);
320 }
321
322 @Test
323 public void testSingleCellGetPutPB() throws IOException, JAXBException {
324 Response response = getValuePB(TABLE, ROW_1, COLUMN_1);
325 assertEquals(response.getCode(), 404);
326
327 response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
328 assertEquals(response.getCode(), 200);
329 checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
330
331 response = putValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
332 assertEquals(response.getCode(), 200);
333 checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_1);
334 response = putValueXML(TABLE, ROW_1, COLUMN_1, VALUE_2);
335 assertEquals(response.getCode(), 200);
336 checkValuePB(TABLE, ROW_1, COLUMN_1, VALUE_2);
337
338 response = deleteRow(TABLE, ROW_1);
339 assertEquals(response.getCode(), 200);
340 }
341
342 @Test
343 public void testSingleCellGetPutBinary() throws IOException {
344 final String path = "/" + TABLE + "/" + ROW_3 + "/" + COLUMN_1;
345 final byte[] body = Bytes.toBytes(VALUE_3);
346 Response response = client.put(path, Constants.MIMETYPE_BINARY, body);
347 assertEquals(response.getCode(), 200);
348 Thread.yield();
349
350 response = client.get(path, Constants.MIMETYPE_BINARY);
351 assertEquals(response.getCode(), 200);
352 assertTrue(Bytes.equals(response.getBody(), body));
353 boolean foundTimestampHeader = false;
354 for (Header header: response.getHeaders()) {
355 if (header.getName().equals("X-Timestamp")) {
356 foundTimestampHeader = true;
357 break;
358 }
359 }
360 assertTrue(foundTimestampHeader);
361
362 response = deleteRow(TABLE, ROW_3);
363 assertEquals(response.getCode(), 200);
364 }
365
366 @Test
367 public void testSingleCellGetJSON() throws IOException, JAXBException {
368 final String path = "/" + TABLE + "/" + ROW_4 + "/" + COLUMN_1;
369 Response response = client.put(path, Constants.MIMETYPE_BINARY,
370 Bytes.toBytes(VALUE_4));
371 assertEquals(response.getCode(), 200);
372 Thread.yield();
373 response = client.get(path, Constants.MIMETYPE_JSON);
374 assertEquals(response.getCode(), 200);
375 response = deleteRow(TABLE, ROW_4);
376 assertEquals(response.getCode(), 200);
377 }
378
379 @Test
380 public void testURLEncodedKey() throws IOException, JAXBException {
381 String urlKey = "http://example.com/foo";
382 StringBuilder path = new StringBuilder();
383 path.append('/');
384 path.append(TABLE);
385 path.append('/');
386 path.append(URLEncoder.encode(urlKey, HConstants.UTF8_ENCODING));
387 path.append('/');
388 path.append(COLUMN_1);
389 Response response;
390 response = putValueXML(path.toString(), TABLE, urlKey, COLUMN_1,
391 VALUE_1);
392 assertEquals(response.getCode(), 200);
393 checkValueXML(path.toString(), TABLE, urlKey, COLUMN_1, VALUE_1);
394 }
395
396 @Test
397 public void testNoSuchCF() throws IOException, JAXBException {
398 final String goodPath = "/" + TABLE + "/" + ROW_1 + "/" + CFA+":";
399 final String badPath = "/" + TABLE + "/" + ROW_1 + "/" + "BAD";
400 Response response = client.post(goodPath, Constants.MIMETYPE_BINARY,
401 Bytes.toBytes(VALUE_1));
402 assertEquals(response.getCode(), 200);
403 assertEquals(client.get(goodPath, Constants.MIMETYPE_BINARY).getCode(),
404 200);
405 assertEquals(client.get(badPath, Constants.MIMETYPE_BINARY).getCode(),
406 404);
407 assertEquals(client.get(goodPath, Constants.MIMETYPE_BINARY).getCode(),
408 200);
409 }
410
411 @Test
412 public void testMultiCellGetPutXML() throws IOException, JAXBException {
413 String path = "/" + TABLE + "/fakerow";
414
415 CellSetModel cellSetModel = new CellSetModel();
416 RowModel rowModel = new RowModel(ROW_1);
417 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
418 Bytes.toBytes(VALUE_1)));
419 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
420 Bytes.toBytes(VALUE_2)));
421 cellSetModel.addRow(rowModel);
422 rowModel = new RowModel(ROW_2);
423 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
424 Bytes.toBytes(VALUE_3)));
425 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
426 Bytes.toBytes(VALUE_4)));
427 cellSetModel.addRow(rowModel);
428 StringWriter writer = new StringWriter();
429 marshaller.marshal(cellSetModel, writer);
430 Response response = client.put(path, Constants.MIMETYPE_XML,
431 Bytes.toBytes(writer.toString()));
432 Thread.yield();
433
434
435 response = client.get(path, Constants.MIMETYPE_XML);
436 assertEquals(response.getCode(), 404);
437
438
439 checkValueXML(TABLE, ROW_1, COLUMN_1, VALUE_1);
440 checkValueXML(TABLE, ROW_1, COLUMN_2, VALUE_2);
441 checkValueXML(TABLE, ROW_2, COLUMN_1, VALUE_3);
442 checkValueXML(TABLE, ROW_2, COLUMN_2, VALUE_4);
443
444 response = deleteRow(TABLE, ROW_1);
445 assertEquals(response.getCode(), 200);
446 response = deleteRow(TABLE, ROW_2);
447 assertEquals(response.getCode(), 200);
448 }
449
450 @Test
451 public void testMultiCellGetPutPB() throws IOException {
452 String path = "/" + TABLE + "/fakerow";
453
454 CellSetModel cellSetModel = new CellSetModel();
455 RowModel rowModel = new RowModel(ROW_1);
456 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
457 Bytes.toBytes(VALUE_1)));
458 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
459 Bytes.toBytes(VALUE_2)));
460 cellSetModel.addRow(rowModel);
461 rowModel = new RowModel(ROW_2);
462 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_1),
463 Bytes.toBytes(VALUE_3)));
464 rowModel.addCell(new CellModel(Bytes.toBytes(COLUMN_2),
465 Bytes.toBytes(VALUE_4)));
466 cellSetModel.addRow(rowModel);
467 Response response = client.put(path, Constants.MIMETYPE_PROTOBUF,
468 cellSetModel.createProtobufOutput());
469 Thread.yield();
470
471
472 response = client.get(path, Constants.MIMETYPE_PROTOBUF);
473 assertEquals(response.getCode(), 404);
474
475
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(response.getCode(), 200);
483 response = deleteRow(TABLE, ROW_2);
484 assertEquals(response.getCode(), 200);
485 }
486 }