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.assertFalse; 022import static org.junit.Assert.assertNotNull; 023import static org.junit.Assert.assertTrue; 024 025import com.fasterxml.jackson.core.JsonFactory; 026import com.fasterxml.jackson.core.JsonParser; 027import com.fasterxml.jackson.core.JsonToken; 028import com.fasterxml.jackson.databind.ObjectMapper; 029import java.io.DataInputStream; 030import java.io.EOFException; 031import java.io.IOException; 032import java.io.InputStream; 033import java.io.Serializable; 034import java.net.URLEncoder; 035import java.nio.charset.StandardCharsets; 036import java.util.ArrayList; 037import java.util.Collections; 038import java.util.List; 039import javax.xml.bind.JAXBContext; 040import javax.xml.bind.JAXBException; 041import javax.xml.bind.Unmarshaller; 042import javax.xml.bind.annotation.XmlAccessType; 043import javax.xml.bind.annotation.XmlAccessorType; 044import javax.xml.bind.annotation.XmlElement; 045import javax.xml.bind.annotation.XmlRootElement; 046import javax.xml.parsers.SAXParserFactory; 047import org.apache.hadoop.conf.Configuration; 048import org.apache.hadoop.hbase.HBaseClassTestRule; 049import org.apache.hadoop.hbase.HBaseTestingUtility; 050import org.apache.hadoop.hbase.HColumnDescriptor; 051import org.apache.hadoop.hbase.HTableDescriptor; 052import org.apache.hadoop.hbase.TableName; 053import org.apache.hadoop.hbase.client.Admin; 054import org.apache.hadoop.hbase.filter.Filter; 055import org.apache.hadoop.hbase.filter.ParseFilter; 056import org.apache.hadoop.hbase.filter.PrefixFilter; 057import org.apache.hadoop.hbase.rest.client.Client; 058import org.apache.hadoop.hbase.rest.client.Cluster; 059import org.apache.hadoop.hbase.rest.client.Response; 060import org.apache.hadoop.hbase.rest.model.CellModel; 061import org.apache.hadoop.hbase.rest.model.CellSetModel; 062import org.apache.hadoop.hbase.rest.model.RowModel; 063import org.apache.hadoop.hbase.testclassification.MediumTests; 064import org.apache.hadoop.hbase.testclassification.RestTests; 065import org.apache.hadoop.hbase.util.Bytes; 066import org.junit.AfterClass; 067import org.junit.BeforeClass; 068import org.junit.ClassRule; 069import org.junit.Test; 070import org.junit.experimental.categories.Category; 071import org.xml.sax.InputSource; 072import org.xml.sax.XMLReader; 073 074import org.apache.hbase.thirdparty.com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; 075import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType; 076 077@Category({ RestTests.class, MediumTests.class }) 078public class TestTableScan { 079 @ClassRule 080 public static final HBaseClassTestRule CLASS_RULE = 081 HBaseClassTestRule.forClass(TestTableScan.class); 082 083 private static final TableName TABLE = TableName.valueOf("TestScanResource"); 084 private static final String CFA = "a"; 085 private static final String CFB = "b"; 086 private static final String COLUMN_1 = CFA + ":1"; 087 private static final String COLUMN_2 = CFB + ":2"; 088 private static final String COLUMN_EMPTY = CFA + ":"; 089 private static Client client; 090 private static int expectedRows1; 091 private static int expectedRows2; 092 private static int expectedRows3; 093 private static Configuration conf; 094 095 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 096 private static final HBaseRESTTestingUtility REST_TEST_UTIL = new HBaseRESTTestingUtility(); 097 098 @BeforeClass 099 public static void setUpBeforeClass() throws Exception { 100 conf = TEST_UTIL.getConfiguration(); 101 conf.set(Constants.CUSTOM_FILTERS, "CustomFilter:" + CustomFilter.class.getName()); 102 TEST_UTIL.startMiniCluster(); 103 REST_TEST_UTIL.startServletContainer(conf); 104 client = new Client(new Cluster().add("localhost", REST_TEST_UTIL.getServletPort())); 105 Admin admin = TEST_UTIL.getAdmin(); 106 if (!admin.tableExists(TABLE)) { 107 HTableDescriptor htd = new HTableDescriptor(TABLE); 108 htd.addFamily(new HColumnDescriptor(CFA)); 109 htd.addFamily(new HColumnDescriptor(CFB)); 110 admin.createTable(htd); 111 expectedRows1 = TestScannerResource.insertData(conf, TABLE, COLUMN_1, 1.0); 112 expectedRows2 = TestScannerResource.insertData(conf, TABLE, COLUMN_2, 0.5); 113 expectedRows3 = TestScannerResource.insertData(conf, TABLE, COLUMN_EMPTY, 1.0); 114 } 115 } 116 117 @AfterClass 118 public static void tearDownAfterClass() throws Exception { 119 TEST_UTIL.getAdmin().disableTable(TABLE); 120 TEST_UTIL.getAdmin().deleteTable(TABLE); 121 REST_TEST_UTIL.shutdownServletContainer(); 122 TEST_UTIL.shutdownMiniCluster(); 123 } 124 125 @Test 126 public void testSimpleScannerXML() throws IOException, JAXBException { 127 // Test scanning particular columns 128 StringBuilder builder = new StringBuilder(); 129 builder.append("/*"); 130 builder.append("?"); 131 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 132 builder.append("&"); 133 builder.append(Constants.SCAN_LIMIT + "=10"); 134 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 135 assertEquals(200, response.getCode()); 136 assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); 137 JAXBContext ctx = JAXBContext.newInstance(CellSetModel.class); 138 Unmarshaller ush = ctx.createUnmarshaller(); 139 CellSetModel model = (CellSetModel) ush.unmarshal(response.getStream()); 140 int count = TestScannerResource.countCellSet(model); 141 assertEquals(10, count); 142 checkRowsNotNull(model); 143 144 // Test with no limit. 145 builder = new StringBuilder(); 146 builder.append("/*"); 147 builder.append("?"); 148 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 149 response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 150 assertEquals(200, response.getCode()); 151 assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); 152 model = (CellSetModel) ush.unmarshal(response.getStream()); 153 count = TestScannerResource.countCellSet(model); 154 assertEquals(expectedRows1, count); 155 checkRowsNotNull(model); 156 157 // Test with start and end row. 158 builder = new StringBuilder(); 159 builder.append("/*"); 160 builder.append("?"); 161 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 162 builder.append("&"); 163 builder.append(Constants.SCAN_START_ROW + "=aaa"); 164 builder.append("&"); 165 builder.append(Constants.SCAN_END_ROW + "=aay"); 166 response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 167 assertEquals(200, response.getCode()); 168 model = (CellSetModel) ush.unmarshal(response.getStream()); 169 count = TestScannerResource.countCellSet(model); 170 RowModel startRow = model.getRows().get(0); 171 assertEquals("aaa", Bytes.toString(startRow.getKey())); 172 RowModel endRow = model.getRows().get(model.getRows().size() - 1); 173 assertEquals("aax", Bytes.toString(endRow.getKey())); 174 assertEquals(24, count); 175 checkRowsNotNull(model); 176 177 // Test with start row and limit. 178 builder = new StringBuilder(); 179 builder.append("/*"); 180 builder.append("?"); 181 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 182 builder.append("&"); 183 builder.append(Constants.SCAN_START_ROW + "=aaa"); 184 builder.append("&"); 185 builder.append(Constants.SCAN_LIMIT + "=15"); 186 response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 187 assertEquals(200, response.getCode()); 188 assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); 189 model = (CellSetModel) ush.unmarshal(response.getStream()); 190 startRow = model.getRows().get(0); 191 assertEquals("aaa", Bytes.toString(startRow.getKey())); 192 count = TestScannerResource.countCellSet(model); 193 assertEquals(15, count); 194 checkRowsNotNull(model); 195 } 196 197 @Test 198 public void testSimpleScannerJson() throws IOException { 199 // Test scanning particular columns with limit. 200 StringBuilder builder = new StringBuilder(); 201 builder.append("/*"); 202 builder.append("?"); 203 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 204 builder.append("&"); 205 builder.append(Constants.SCAN_LIMIT + "=2"); 206 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_JSON); 207 assertEquals(200, response.getCode()); 208 assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type")); 209 ObjectMapper mapper = new JacksonJaxbJsonProvider().locateMapper(CellSetModel.class, 210 MediaType.APPLICATION_JSON_TYPE); 211 CellSetModel model = mapper.readValue(response.getStream(), CellSetModel.class); 212 int count = TestScannerResource.countCellSet(model); 213 assertEquals(2, count); 214 checkRowsNotNull(model); 215 216 // Test scanning with no limit. 217 builder = new StringBuilder(); 218 builder.append("/*"); 219 builder.append("?"); 220 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_2); 221 response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_JSON); 222 assertEquals(200, response.getCode()); 223 assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type")); 224 model = mapper.readValue(response.getStream(), CellSetModel.class); 225 count = TestScannerResource.countCellSet(model); 226 assertEquals(expectedRows2, count); 227 checkRowsNotNull(model); 228 229 // Test with start row and end row. 230 builder = new StringBuilder(); 231 builder.append("/*"); 232 builder.append("?"); 233 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 234 builder.append("&"); 235 builder.append(Constants.SCAN_START_ROW + "=aaa"); 236 builder.append("&"); 237 builder.append(Constants.SCAN_END_ROW + "=aay"); 238 response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_JSON); 239 assertEquals(200, response.getCode()); 240 model = mapper.readValue(response.getStream(), CellSetModel.class); 241 RowModel startRow = model.getRows().get(0); 242 assertEquals("aaa", Bytes.toString(startRow.getKey())); 243 RowModel endRow = model.getRows().get(model.getRows().size() - 1); 244 assertEquals("aax", Bytes.toString(endRow.getKey())); 245 count = TestScannerResource.countCellSet(model); 246 assertEquals(24, count); 247 checkRowsNotNull(model); 248 } 249 250 /** 251 * An example to scan using listener in unmarshaller for XML. 252 * @throws Exception the exception 253 */ 254 @Test 255 public void testScanUsingListenerUnmarshallerXML() throws Exception { 256 StringBuilder builder = new StringBuilder(); 257 builder.append("/*"); 258 builder.append("?"); 259 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 260 builder.append("&"); 261 builder.append(Constants.SCAN_LIMIT + "=10"); 262 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 263 assertEquals(200, response.getCode()); 264 assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); 265 JAXBContext context = 266 JAXBContext.newInstance(ClientSideCellSetModel.class, RowModel.class, CellModel.class); 267 Unmarshaller unmarshaller = context.createUnmarshaller(); 268 269 final ClientSideCellSetModel.Listener listener = new ClientSideCellSetModel.Listener() { 270 @Override 271 public void handleRowModel(ClientSideCellSetModel helper, RowModel row) { 272 assertTrue(row.getKey() != null); 273 assertTrue(row.getCells().size() > 0); 274 } 275 }; 276 277 // install the callback on all ClientSideCellSetModel instances 278 unmarshaller.setListener(new Unmarshaller.Listener() { 279 @Override 280 public void beforeUnmarshal(Object target, Object parent) { 281 if (target instanceof ClientSideCellSetModel) { 282 ((ClientSideCellSetModel) target).setCellSetModelListener(listener); 283 } 284 } 285 286 @Override 287 public void afterUnmarshal(Object target, Object parent) { 288 if (target instanceof ClientSideCellSetModel) { 289 ((ClientSideCellSetModel) target).setCellSetModelListener(null); 290 } 291 } 292 }); 293 294 // create a new XML parser 295 SAXParserFactory factory = SAXParserFactory.newInstance(); 296 factory.setNamespaceAware(true); 297 XMLReader reader = factory.newSAXParser().getXMLReader(); 298 reader.setContentHandler(unmarshaller.getUnmarshallerHandler()); 299 assertFalse(ClientSideCellSetModel.listenerInvoked); 300 reader.parse(new InputSource(response.getStream())); 301 assertTrue(ClientSideCellSetModel.listenerInvoked); 302 303 } 304 305 @Test 306 public void testStreamingJSON() throws Exception { 307 // Test with start row and end row. 308 StringBuilder builder = new StringBuilder(); 309 builder.append("/*"); 310 builder.append("?"); 311 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 312 builder.append("&"); 313 builder.append(Constants.SCAN_START_ROW + "=aaa"); 314 builder.append("&"); 315 builder.append(Constants.SCAN_END_ROW + "=aay"); 316 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_JSON); 317 assertEquals(200, response.getCode()); 318 319 int count = 0; 320 ObjectMapper mapper = new JacksonJaxbJsonProvider().locateMapper(CellSetModel.class, 321 MediaType.APPLICATION_JSON_TYPE); 322 JsonFactory jfactory = new JsonFactory(mapper); 323 JsonParser jParser = jfactory.createJsonParser(response.getStream()); 324 boolean found = false; 325 while (jParser.nextToken() != JsonToken.END_OBJECT) { 326 if (jParser.getCurrentToken() == JsonToken.START_OBJECT && found) { 327 RowModel row = jParser.readValueAs(RowModel.class); 328 assertNotNull(row.getKey()); 329 for (int i = 0; i < row.getCells().size(); i++) { 330 if (count == 0) { 331 assertEquals("aaa", Bytes.toString(row.getKey())); 332 } 333 if (count == 23) { 334 assertEquals("aax", Bytes.toString(row.getKey())); 335 } 336 count++; 337 } 338 jParser.skipChildren(); 339 } else { 340 found = jParser.getCurrentToken() == JsonToken.START_ARRAY; 341 } 342 } 343 assertEquals(24, count); 344 } 345 346 @Test 347 public void testSimpleScannerProtobuf() throws Exception { 348 StringBuilder builder = new StringBuilder(); 349 builder.append("/*"); 350 builder.append("?"); 351 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 352 builder.append("&"); 353 builder.append(Constants.SCAN_LIMIT + "=15"); 354 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_PROTOBUF); 355 assertEquals(200, response.getCode()); 356 assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type")); 357 int rowCount = readProtobufStream(response.getStream()); 358 assertEquals(15, rowCount); 359 360 // Test with start row and end row. 361 builder = new StringBuilder(); 362 builder.append("/*"); 363 builder.append("?"); 364 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 365 builder.append("&"); 366 builder.append(Constants.SCAN_START_ROW + "=aaa"); 367 builder.append("&"); 368 builder.append(Constants.SCAN_END_ROW + "=aay"); 369 response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_PROTOBUF); 370 assertEquals(200, response.getCode()); 371 assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type")); 372 rowCount = readProtobufStream(response.getStream()); 373 assertEquals(24, rowCount); 374 } 375 376 private void checkRowsNotNull(CellSetModel model) { 377 for (RowModel row : model.getRows()) { 378 assertTrue(row.getKey() != null); 379 assertTrue(row.getCells().size() > 0); 380 } 381 } 382 383 /** 384 * Read protobuf stream. 385 * @param inputStream the input stream 386 * @return The number of rows in the cell set model. 387 * @throws IOException Signals that an I/O exception has occurred. 388 */ 389 public int readProtobufStream(InputStream inputStream) throws IOException { 390 DataInputStream stream = new DataInputStream(inputStream); 391 CellSetModel model = null; 392 int rowCount = 0; 393 try { 394 while (true) { 395 byte[] lengthBytes = new byte[2]; 396 int readBytes = stream.read(lengthBytes); 397 if (readBytes == -1) { 398 break; 399 } 400 assertEquals(2, readBytes); 401 int length = Bytes.toShort(lengthBytes); 402 byte[] cellset = new byte[length]; 403 stream.read(cellset); 404 model = new CellSetModel(); 405 model.getObjectFromMessage(cellset); 406 checkRowsNotNull(model); 407 rowCount = rowCount + TestScannerResource.countCellSet(model); 408 } 409 } catch (EOFException exp) { 410 exp.printStackTrace(); 411 } finally { 412 stream.close(); 413 } 414 return rowCount; 415 } 416 417 @Test 418 public void testScanningUnknownColumnJson() throws IOException { 419 // Test scanning particular columns with limit. 420 StringBuilder builder = new StringBuilder(); 421 builder.append("/*"); 422 builder.append("?"); 423 builder.append(Constants.SCAN_COLUMN + "=a:test"); 424 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_JSON); 425 assertEquals(200, response.getCode()); 426 assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type")); 427 ObjectMapper mapper = new JacksonJaxbJsonProvider().locateMapper(CellSetModel.class, 428 MediaType.APPLICATION_JSON_TYPE); 429 CellSetModel model = mapper.readValue(response.getStream(), CellSetModel.class); 430 int count = TestScannerResource.countCellSet(model); 431 assertEquals(0, count); 432 } 433 434 @Test 435 public void testSimpleFilter() throws IOException, JAXBException { 436 StringBuilder builder = new StringBuilder(); 437 builder.append("/*"); 438 builder.append("?"); 439 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 440 builder.append("&"); 441 builder.append(Constants.SCAN_START_ROW + "=aaa"); 442 builder.append("&"); 443 builder.append(Constants.SCAN_END_ROW + "=aay"); 444 builder.append("&"); 445 builder.append(Constants.SCAN_FILTER + "=" + URLEncoder.encode("PrefixFilter('aab')", "UTF-8")); 446 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 447 assertEquals(200, response.getCode()); 448 JAXBContext ctx = JAXBContext.newInstance(CellSetModel.class); 449 Unmarshaller ush = ctx.createUnmarshaller(); 450 CellSetModel model = (CellSetModel) ush.unmarshal(response.getStream()); 451 int count = TestScannerResource.countCellSet(model); 452 assertEquals(1, count); 453 assertEquals("aab", 454 new String(model.getRows().get(0).getCells().get(0).getValue(), StandardCharsets.UTF_8)); 455 } 456 457 @Test 458 public void testQualifierAndPrefixFilters() throws IOException, JAXBException { 459 StringBuilder builder = new StringBuilder(); 460 builder.append("/abc*"); 461 builder.append("?"); 462 builder.append( 463 Constants.SCAN_FILTER + "=" + URLEncoder.encode("QualifierFilter(=,'binary:1')", "UTF-8")); 464 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 465 assertEquals(200, response.getCode()); 466 JAXBContext ctx = JAXBContext.newInstance(CellSetModel.class); 467 Unmarshaller ush = ctx.createUnmarshaller(); 468 CellSetModel model = (CellSetModel) ush.unmarshal(response.getStream()); 469 int count = TestScannerResource.countCellSet(model); 470 assertEquals(1, count); 471 assertEquals("abc", 472 new String(model.getRows().get(0).getCells().get(0).getValue(), StandardCharsets.UTF_8)); 473 } 474 475 @Test 476 public void testCompoundFilter() throws IOException, JAXBException { 477 StringBuilder builder = new StringBuilder(); 478 builder.append("/*"); 479 builder.append("?"); 480 builder.append(Constants.SCAN_FILTER + "=" 481 + URLEncoder.encode("PrefixFilter('abc') AND QualifierFilter(=,'binary:1')", "UTF-8")); 482 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 483 assertEquals(200, response.getCode()); 484 JAXBContext ctx = JAXBContext.newInstance(CellSetModel.class); 485 Unmarshaller ush = ctx.createUnmarshaller(); 486 CellSetModel model = (CellSetModel) ush.unmarshal(response.getStream()); 487 int count = TestScannerResource.countCellSet(model); 488 assertEquals(1, count); 489 assertEquals("abc", 490 new String(model.getRows().get(0).getCells().get(0).getValue(), StandardCharsets.UTF_8)); 491 } 492 493 @Test 494 public void testCustomFilter() throws IOException, JAXBException { 495 StringBuilder builder = new StringBuilder(); 496 builder.append("/a*"); 497 builder.append("?"); 498 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 499 builder.append("&"); 500 builder.append(Constants.SCAN_FILTER + "=" + URLEncoder.encode("CustomFilter('abc')", "UTF-8")); 501 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 502 assertEquals(200, response.getCode()); 503 JAXBContext ctx = JAXBContext.newInstance(CellSetModel.class); 504 Unmarshaller ush = ctx.createUnmarshaller(); 505 CellSetModel model = (CellSetModel) ush.unmarshal(response.getStream()); 506 int count = TestScannerResource.countCellSet(model); 507 assertEquals(1, count); 508 assertEquals("abc", 509 new String(model.getRows().get(0).getCells().get(0).getValue(), StandardCharsets.UTF_8)); 510 } 511 512 @Test 513 public void testNegativeCustomFilter() throws IOException, JAXBException { 514 StringBuilder builder = new StringBuilder(); 515 builder.append("/b*"); 516 builder.append("?"); 517 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 518 builder.append("&"); 519 builder.append(Constants.SCAN_FILTER + "=" + URLEncoder.encode("CustomFilter('abc')", "UTF-8")); 520 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 521 assertEquals(200, response.getCode()); 522 JAXBContext ctx = JAXBContext.newInstance(CellSetModel.class); 523 Unmarshaller ush = ctx.createUnmarshaller(); 524 CellSetModel model = (CellSetModel) ush.unmarshal(response.getStream()); 525 int count = TestScannerResource.countCellSet(model); 526 // Should return no rows as the filters conflict 527 assertEquals(0, count); 528 } 529 530 @Test 531 public void testReversed() throws IOException, JAXBException { 532 StringBuilder builder = new StringBuilder(); 533 builder.append("/*"); 534 builder.append("?"); 535 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 536 builder.append("&"); 537 builder.append(Constants.SCAN_START_ROW + "=aaa"); 538 builder.append("&"); 539 builder.append(Constants.SCAN_END_ROW + "=aay"); 540 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 541 assertEquals(200, response.getCode()); 542 JAXBContext ctx = JAXBContext.newInstance(CellSetModel.class); 543 Unmarshaller ush = ctx.createUnmarshaller(); 544 CellSetModel model = (CellSetModel) ush.unmarshal(response.getStream()); 545 int count = TestScannerResource.countCellSet(model); 546 assertEquals(24, count); 547 List<RowModel> rowModels = model.getRows().subList(1, count); 548 549 // reversed 550 builder = new StringBuilder(); 551 builder.append("/*"); 552 builder.append("?"); 553 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 554 builder.append("&"); 555 builder.append(Constants.SCAN_START_ROW + "=aay"); 556 builder.append("&"); 557 builder.append(Constants.SCAN_END_ROW + "=aaa"); 558 builder.append("&"); 559 builder.append(Constants.SCAN_REVERSED + "=true"); 560 response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML); 561 assertEquals(200, response.getCode()); 562 model = (CellSetModel) ush.unmarshal(response.getStream()); 563 count = TestScannerResource.countCellSet(model); 564 assertEquals(24, count); 565 List<RowModel> reversedRowModels = model.getRows().subList(1, count); 566 567 Collections.reverse(reversedRowModels); 568 assertEquals(rowModels.size(), reversedRowModels.size()); 569 for (int i = 0; i < rowModels.size(); i++) { 570 RowModel rowModel = rowModels.get(i); 571 RowModel reversedRowModel = reversedRowModels.get(i); 572 573 assertEquals(new String(rowModel.getKey(), "UTF-8"), 574 new String(reversedRowModel.getKey(), "UTF-8")); 575 assertEquals(new String(rowModel.getCells().get(0).getValue(), "UTF-8"), 576 new String(reversedRowModel.getCells().get(0).getValue(), "UTF-8")); 577 } 578 } 579 580 @Test 581 public void testColumnWithEmptyQualifier() throws IOException { 582 // Test scanning with empty qualifier 583 StringBuilder builder = new StringBuilder(); 584 builder.append("/*"); 585 builder.append("?"); 586 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_EMPTY); 587 Response response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_JSON); 588 assertEquals(200, response.getCode()); 589 assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type")); 590 ObjectMapper mapper = new JacksonJaxbJsonProvider().locateMapper(CellSetModel.class, 591 MediaType.APPLICATION_JSON_TYPE); 592 CellSetModel model = mapper.readValue(response.getStream(), CellSetModel.class); 593 int count = TestScannerResource.countCellSet(model); 594 assertEquals(expectedRows3, count); 595 checkRowsNotNull(model); 596 RowModel startRow = model.getRows().get(0); 597 assertEquals("aaa", Bytes.toString(startRow.getKey())); 598 assertEquals(1, startRow.getCells().size()); 599 600 // Test scanning with empty qualifier and normal qualifier 601 builder = new StringBuilder(); 602 builder.append("/*"); 603 builder.append("?"); 604 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1); 605 builder.append("&"); 606 builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_EMPTY); 607 response = client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_JSON); 608 assertEquals(200, response.getCode()); 609 assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type")); 610 mapper = new JacksonJaxbJsonProvider().locateMapper(CellSetModel.class, 611 MediaType.APPLICATION_JSON_TYPE); 612 model = mapper.readValue(response.getStream(), CellSetModel.class); 613 count = TestScannerResource.countCellSet(model); 614 assertEquals(expectedRows1 + expectedRows3, count); 615 checkRowsNotNull(model); 616 } 617 618 public static class CustomFilter extends PrefixFilter { 619 private byte[] key = null; 620 621 public CustomFilter(byte[] key) { 622 super(key); 623 } 624 625 @Override 626 public boolean filterRowKey(byte[] buffer, int offset, int length) { 627 int cmp = Bytes.compareTo(buffer, offset, length, this.key, 0, this.key.length); 628 return cmp != 0; 629 } 630 631 public static Filter createFilterFromArguments(ArrayList<byte[]> filterArguments) { 632 byte[] prefix = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0)); 633 return new CustomFilter(prefix); 634 } 635 } 636 637 /** 638 * The Class ClientSideCellSetModel which mimics cell set model, and contains listener to perform 639 * user defined operations on the row model. 640 */ 641 @XmlRootElement(name = "CellSet") 642 @XmlAccessorType(XmlAccessType.FIELD) 643 public static class ClientSideCellSetModel implements Serializable { 644 private static final long serialVersionUID = 1L; 645 646 /** 647 * This list is not a real list; instead it will notify a listener whenever JAXB has 648 * unmarshalled the next row. 649 */ 650 @XmlElement(name = "Row") 651 private List<RowModel> row; 652 653 static boolean listenerInvoked = false; 654 655 /** 656 * Install a listener for row model on this object. If l is null, the listener is removed again. 657 */ 658 public void setCellSetModelListener(final Listener l) { 659 row = (l == null) ? null : new ArrayList<RowModel>() { 660 private static final long serialVersionUID = 1L; 661 662 @Override 663 public boolean add(RowModel o) { 664 l.handleRowModel(ClientSideCellSetModel.this, o); 665 listenerInvoked = true; 666 return false; 667 } 668 }; 669 } 670 671 /** 672 * This listener is invoked every time a new row model is unmarshalled. 673 */ 674 public interface Listener { 675 void handleRowModel(ClientSideCellSetModel helper, RowModel rowModel); 676 } 677 } 678}