1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.rest;
21
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertNull;
25 import static org.junit.Assert.assertTrue;
26
27 import java.io.ByteArrayInputStream;
28 import java.io.IOException;
29 import java.io.StringWriter;
30 import java.util.ArrayList;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Random;
34
35 import javax.xml.bind.JAXBContext;
36 import javax.xml.bind.JAXBException;
37 import javax.xml.bind.Marshaller;
38 import javax.xml.bind.Unmarshaller;
39
40 import org.apache.commons.httpclient.Header;
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43 import org.apache.hadoop.conf.Configuration;
44 import org.apache.hadoop.hbase.HBaseTestingUtility;
45 import org.apache.hadoop.hbase.HColumnDescriptor;
46 import org.apache.hadoop.hbase.HTableDescriptor;
47 import org.apache.hadoop.hbase.KeyValue;
48 import org.apache.hadoop.hbase.TableName;
49 import org.apache.hadoop.hbase.client.Admin;
50 import org.apache.hadoop.hbase.client.Connection;
51 import org.apache.hadoop.hbase.client.ConnectionFactory;
52 import org.apache.hadoop.hbase.client.Durability;
53 import org.apache.hadoop.hbase.client.Put;
54 import org.apache.hadoop.hbase.client.Table;
55 import org.apache.hadoop.hbase.rest.client.Client;
56 import org.apache.hadoop.hbase.rest.client.Cluster;
57 import org.apache.hadoop.hbase.rest.client.Response;
58 import org.apache.hadoop.hbase.rest.model.CellModel;
59 import org.apache.hadoop.hbase.rest.model.CellSetModel;
60 import org.apache.hadoop.hbase.rest.model.RowModel;
61 import org.apache.hadoop.hbase.rest.model.ScannerModel;
62 import org.apache.hadoop.hbase.testclassification.MediumTests;
63 import org.apache.hadoop.hbase.util.Bytes;
64 import org.junit.AfterClass;
65 import org.junit.BeforeClass;
66 import org.junit.Test;
67 import org.junit.experimental.categories.Category;
68
69 @Category(MediumTests.class)
70 public class TestScannerResource {
71 private static final Log LOG = LogFactory.getLog(TestScannerResource.class);
72 private static final TableName TABLE = TableName.valueOf("TestScannerResource");
73 private static final TableName TABLE_TO_BE_DISABLED = TableName.valueOf("ScannerResourceDisable");
74 private static final String NONEXISTENT_TABLE = "ThisTableDoesNotExist";
75 private static final String CFA = "a";
76 private static final String CFB = "b";
77 private static final String COLUMN_1 = CFA + ":1";
78 private static final String COLUMN_2 = CFB + ":2";
79
80 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
81 private static final HBaseRESTTestingUtility REST_TEST_UTIL =
82 new HBaseRESTTestingUtility();
83 private static Client client;
84 private static JAXBContext context;
85 private static Marshaller marshaller;
86 private static Unmarshaller unmarshaller;
87 private static int expectedRows1;
88 private static int expectedRows2;
89 private static Configuration conf;
90
91 static int insertData(Configuration conf, TableName tableName, String column, double prob)
92 throws IOException {
93 Random rng = new Random();
94 byte[] k = new byte[3];
95 byte [][] famAndQf = KeyValue.parseColumn(Bytes.toBytes(column));
96 List<Put> puts = new ArrayList<>();
97 for (byte b1 = 'a'; b1 < 'z'; b1++) {
98 for (byte b2 = 'a'; b2 < 'z'; b2++) {
99 for (byte b3 = 'a'; b3 < 'z'; b3++) {
100 if (rng.nextDouble() < prob) {
101 k[0] = b1;
102 k[1] = b2;
103 k[2] = b3;
104 Put put = new Put(k);
105 put.setDurability(Durability.SKIP_WAL);
106 put.add(famAndQf[0], famAndQf[1], k);
107 puts.add(put);
108 }
109 }
110 }
111 }
112 try (Connection conn = ConnectionFactory.createConnection(conf);
113 Table table = conn.getTable(tableName)) {
114 table.put(puts);
115 }
116 return puts.size();
117 }
118
119 static int countCellSet(CellSetModel model) {
120 int count = 0;
121 Iterator<RowModel> rows = model.getRows().iterator();
122 while (rows.hasNext()) {
123 RowModel row = rows.next();
124 Iterator<CellModel> cells = row.getCells().iterator();
125 while (cells.hasNext()) {
126 cells.next();
127 count++;
128 }
129 }
130 return count;
131 }
132
133 private static int fullTableScan(ScannerModel model) throws IOException {
134 model.setBatch(100);
135 Response response = client.put("/" + TABLE + "/scanner",
136 Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
137 assertEquals(response.getCode(), 201);
138 String scannerURI = response.getLocation();
139 assertNotNull(scannerURI);
140 int count = 0;
141 while (true) {
142 response = client.get(scannerURI, Constants.MIMETYPE_PROTOBUF);
143 assertTrue(response.getCode() == 200 || response.getCode() == 204);
144 if (response.getCode() == 200) {
145 assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type"));
146 CellSetModel cellSet = new CellSetModel();
147 cellSet.getObjectFromMessage(response.getBody());
148 Iterator<RowModel> rows = cellSet.getRows().iterator();
149 while (rows.hasNext()) {
150 RowModel row = rows.next();
151 Iterator<CellModel> cells = row.getCells().iterator();
152 while (cells.hasNext()) {
153 cells.next();
154 count++;
155 }
156 }
157 } else {
158 break;
159 }
160 }
161
162 response = client.delete(scannerURI);
163 assertEquals(response.getCode(), 200);
164 return count;
165 }
166
167 @BeforeClass
168 public static void setUpBeforeClass() throws Exception {
169 conf = TEST_UTIL.getConfiguration();
170 TEST_UTIL.startMiniCluster();
171 REST_TEST_UTIL.startServletContainer(conf);
172 client = new Client(new Cluster().add("localhost",
173 REST_TEST_UTIL.getServletPort()));
174 context = JAXBContext.newInstance(
175 CellModel.class,
176 CellSetModel.class,
177 RowModel.class,
178 ScannerModel.class);
179 marshaller = context.createMarshaller();
180 unmarshaller = context.createUnmarshaller();
181 Admin admin = TEST_UTIL.getHBaseAdmin();
182 if (admin.tableExists(TABLE)) {
183 return;
184 }
185 HTableDescriptor htd = new HTableDescriptor(TABLE);
186 htd.addFamily(new HColumnDescriptor(CFA));
187 htd.addFamily(new HColumnDescriptor(CFB));
188 admin.createTable(htd);
189 expectedRows1 = insertData(TEST_UTIL.getConfiguration(), TABLE, COLUMN_1, 1.0);
190 expectedRows2 = insertData(TEST_UTIL.getConfiguration(), TABLE, COLUMN_2, 0.5);
191
192 htd = new HTableDescriptor(TABLE_TO_BE_DISABLED);
193 htd.addFamily(new HColumnDescriptor(CFA));
194 htd.addFamily(new HColumnDescriptor(CFB));
195 admin.createTable(htd);
196
197 }
198
199 @AfterClass
200 public static void tearDownAfterClass() throws Exception {
201 REST_TEST_UTIL.shutdownServletContainer();
202 TEST_UTIL.shutdownMiniCluster();
203 }
204
205 @Test
206 public void testSimpleScannerXML() throws IOException, JAXBException {
207 final int BATCH_SIZE = 5;
208
209 ScannerModel model = new ScannerModel();
210 model.setBatch(BATCH_SIZE);
211 model.addColumn(Bytes.toBytes(COLUMN_1));
212 StringWriter writer = new StringWriter();
213 marshaller.marshal(model, writer);
214 byte[] body = Bytes.toBytes(writer.toString());
215
216
217 conf.set("hbase.rest.readonly", "true");
218 Response response = client.put("/" + TABLE + "/scanner",
219 Constants.MIMETYPE_XML, body);
220 assertEquals(response.getCode(), 403);
221 String scannerURI = response.getLocation();
222 assertNull(scannerURI);
223
224
225 conf.set("hbase.rest.readonly", "false");
226 response = client.put("/" + TABLE + "/scanner", Constants.MIMETYPE_XML,
227 body);
228 assertEquals(response.getCode(), 201);
229 scannerURI = response.getLocation();
230 assertNotNull(scannerURI);
231
232
233 response = client.get(scannerURI, Constants.MIMETYPE_XML);
234 assertEquals(response.getCode(), 200);
235 assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
236 CellSetModel cellSet = (CellSetModel)
237 unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody()));
238
239 assertEquals(countCellSet(cellSet), BATCH_SIZE);
240
241
242 conf.set("hbase.rest.readonly", "true");
243 response = client.delete(scannerURI);
244 assertEquals(response.getCode(), 403);
245
246
247 conf.set("hbase.rest.readonly", "false");
248 response = client.delete(scannerURI);
249 assertEquals(response.getCode(), 200);
250 }
251
252 @Test
253 public void testSimpleScannerPB() throws IOException {
254 final int BATCH_SIZE = 10;
255
256 ScannerModel model = new ScannerModel();
257 model.setBatch(BATCH_SIZE);
258 model.addColumn(Bytes.toBytes(COLUMN_1));
259
260
261 conf.set("hbase.rest.readonly", "true");
262 Response response = client.put("/" + TABLE + "/scanner",
263 Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
264 assertEquals(response.getCode(), 403);
265 String scannerURI = response.getLocation();
266 assertNull(scannerURI);
267
268
269 conf.set("hbase.rest.readonly", "false");
270 response = client.put("/" + TABLE + "/scanner",
271 Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
272 assertEquals(response.getCode(), 201);
273 scannerURI = response.getLocation();
274 assertNotNull(scannerURI);
275
276
277 response = client.get(scannerURI, Constants.MIMETYPE_PROTOBUF);
278 assertEquals(response.getCode(), 200);
279 assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type"));
280 CellSetModel cellSet = new CellSetModel();
281 cellSet.getObjectFromMessage(response.getBody());
282
283 assertEquals(countCellSet(cellSet), BATCH_SIZE);
284
285
286 conf.set("hbase.rest.readonly", "true");
287 response = client.delete(scannerURI);
288 assertEquals(response.getCode(), 403);
289
290
291 conf.set("hbase.rest.readonly", "false");
292 response = client.delete(scannerURI);
293 assertEquals(response.getCode(), 200);
294 }
295
296 @Test
297 public void testSimpleScannerBinary() throws IOException {
298
299 ScannerModel model = new ScannerModel();
300 model.setBatch(1);
301 model.addColumn(Bytes.toBytes(COLUMN_1));
302
303
304 conf.set("hbase.rest.readonly", "true");
305 Response response = client.put("/" + TABLE + "/scanner",
306 Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
307 assertEquals(response.getCode(), 403);
308 String scannerURI = response.getLocation();
309 assertNull(scannerURI);
310
311
312 conf.set("hbase.rest.readonly", "false");
313 response = client.put("/" + TABLE + "/scanner",
314 Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
315 assertEquals(response.getCode(), 201);
316 scannerURI = response.getLocation();
317 assertNotNull(scannerURI);
318
319
320 response = client.get(scannerURI, Constants.MIMETYPE_BINARY);
321 assertEquals(response.getCode(), 200);
322 assertEquals(Constants.MIMETYPE_BINARY, response.getHeader("content-type"));
323
324 assertTrue(response.getBody().length > 0);
325
326 boolean foundRowHeader = false, foundColumnHeader = false,
327 foundTimestampHeader = false;
328 for (Header header: response.getHeaders()) {
329 if (header.getName().equals("X-Row")) {
330 foundRowHeader = true;
331 } else if (header.getName().equals("X-Column")) {
332 foundColumnHeader = true;
333 } else if (header.getName().equals("X-Timestamp")) {
334 foundTimestampHeader = true;
335 }
336 }
337 assertTrue(foundRowHeader);
338 assertTrue(foundColumnHeader);
339 assertTrue(foundTimestampHeader);
340
341
342 conf.set("hbase.rest.readonly", "true");
343 response = client.delete(scannerURI);
344 assertEquals(response.getCode(), 403);
345
346
347 conf.set("hbase.rest.readonly", "false");
348 response = client.delete(scannerURI);
349 assertEquals(response.getCode(), 200);
350 }
351
352 @Test
353 public void testFullTableScan() throws IOException {
354 ScannerModel model = new ScannerModel();
355 model.addColumn(Bytes.toBytes(COLUMN_1));
356 assertEquals(fullTableScan(model), expectedRows1);
357
358 model = new ScannerModel();
359 model.addColumn(Bytes.toBytes(COLUMN_2));
360 assertEquals(fullTableScan(model), expectedRows2);
361 }
362
363 @Test
364 public void testTableDoesNotExist() throws IOException, JAXBException {
365 ScannerModel model = new ScannerModel();
366 StringWriter writer = new StringWriter();
367 marshaller.marshal(model, writer);
368 byte[] body = Bytes.toBytes(writer.toString());
369 Response response = client.put("/" + NONEXISTENT_TABLE +
370 "/scanner", Constants.MIMETYPE_XML, body);
371 assertEquals(response.getCode(), 404);
372 }
373
374
375
376 @Test
377 public void testTableScanWithTableDisable() throws IOException {
378 ScannerModel model = new ScannerModel();
379 model.addColumn(Bytes.toBytes(COLUMN_1));
380 model.setCaching(1);
381 Response response = client.put("/" + TABLE_TO_BE_DISABLED + "/scanner",
382 Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
383 assertEquals(response.getCode(), 201);
384 String scannerURI = response.getLocation();
385 assertNotNull(scannerURI);
386 TEST_UTIL.getHBaseAdmin().disableTable(TABLE_TO_BE_DISABLED);
387 response = client.get(scannerURI, Constants.MIMETYPE_PROTOBUF);
388 assertTrue("got " + response.getCode(), response.getCode() == 410);
389 }
390
391 }
392