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