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