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