View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.rest;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertFalse;
22  import static org.junit.Assert.assertNotNull;
23  import static org.junit.Assert.assertTrue;
24  
25  import java.io.DataInputStream;
26  import java.io.EOFException;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.Serializable;
30  import java.net.URLEncoder;
31  import java.util.ArrayList;
32  import java.util.List;
33  
34  import javax.ws.rs.core.MediaType;
35  import javax.xml.bind.JAXBContext;
36  import javax.xml.bind.JAXBException;
37  import javax.xml.bind.Unmarshaller;
38  import javax.xml.bind.annotation.XmlAccessType;
39  import javax.xml.bind.annotation.XmlAccessorType;
40  import javax.xml.bind.annotation.XmlElement;
41  import javax.xml.bind.annotation.XmlRootElement;
42  import javax.xml.parsers.SAXParserFactory;
43  import javax.xml.stream.XMLStreamException;
44  
45  import org.apache.hadoop.conf.Configuration;
46  import org.apache.hadoop.hbase.HBaseTestingUtility;
47  import org.apache.hadoop.hbase.HColumnDescriptor;
48  import org.apache.hadoop.hbase.HTableDescriptor;
49  import org.apache.hadoop.hbase.testclassification.MediumTests;
50  import org.apache.hadoop.hbase.TableName;
51  import org.apache.hadoop.hbase.client.Admin;
52  import org.apache.hadoop.hbase.filter.Filter;
53  import org.apache.hadoop.hbase.filter.ParseFilter;
54  import org.apache.hadoop.hbase.filter.PrefixFilter;
55  import org.apache.hadoop.hbase.rest.client.Client;
56  import org.apache.hadoop.hbase.rest.client.Cluster;
57  import org.apache.hadoop.hbase.rest.client.Response;
58  import org.apache.hadoop.hbase.rest.model.CellModel;
59  import org.apache.hadoop.hbase.rest.model.CellSetModel;
60  import org.apache.hadoop.hbase.rest.model.RowModel;
61  import org.apache.hadoop.hbase.rest.provider.JacksonProvider;
62  import org.apache.hadoop.hbase.util.Bytes;
63  import org.codehaus.jackson.JsonFactory;
64  import org.codehaus.jackson.JsonParser;
65  import org.codehaus.jackson.JsonToken;
66  import org.codehaus.jackson.map.ObjectMapper;
67  import org.junit.AfterClass;
68  import org.junit.BeforeClass;
69  import org.junit.Test;
70  import org.junit.experimental.categories.Category;
71  import org.xml.sax.InputSource;
72  import org.xml.sax.XMLReader;
73  
74  @Category(MediumTests.class)
75  public class TestTableScan {
76  
77    private static final TableName TABLE = TableName.valueOf("TestScanResource");
78    private static final String CFA = "a";
79    private static final String CFB = "b";
80    private static final String COLUMN_1 = CFA + ":1";
81    private static final String COLUMN_2 = CFB + ":2";
82    private static Client client;
83    private static int expectedRows1;
84    private static int expectedRows2;
85    private static Configuration conf;
86  
87    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
88    private static final HBaseRESTTestingUtility REST_TEST_UTIL =
89      new HBaseRESTTestingUtility();
90  
91    @BeforeClass
92    public static void setUpBeforeClass() throws Exception {
93      conf = TEST_UTIL.getConfiguration();
94      conf.set(Constants.CUSTOM_FILTERS, "CustomFilter:" + CustomFilter.class.getName()); 
95      TEST_UTIL.startMiniCluster();
96      REST_TEST_UTIL.startServletContainer(conf);
97      client = new Client(new Cluster().add("localhost",
98        REST_TEST_UTIL.getServletPort()));
99      Admin admin = TEST_UTIL.getHBaseAdmin();
100     if (!admin.tableExists(TABLE)) {
101     HTableDescriptor htd = new HTableDescriptor(TABLE);
102     htd.addFamily(new HColumnDescriptor(CFA));
103     htd.addFamily(new HColumnDescriptor(CFB));
104     admin.createTable(htd);
105     expectedRows1 = TestScannerResource.insertData(conf, TABLE, COLUMN_1, 1.0);
106     expectedRows2 = TestScannerResource.insertData(conf, TABLE, COLUMN_2, 0.5);
107     }
108   }
109 
110   @AfterClass
111   public static void tearDownAfterClass() throws Exception {
112     TEST_UTIL.getHBaseAdmin().disableTable(TABLE);
113     TEST_UTIL.getHBaseAdmin().deleteTable(TABLE);
114     REST_TEST_UTIL.shutdownServletContainer();
115     TEST_UTIL.shutdownMiniCluster();
116   }
117 
118   @Test
119   public void testSimpleScannerXML() throws IOException, JAXBException, XMLStreamException {
120     // Test scanning particular columns
121     StringBuilder builder = new StringBuilder();
122     builder.append("/*");
123     builder.append("?");
124     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
125     builder.append("&");
126     builder.append(Constants.SCAN_LIMIT + "=10");
127     Response response = client.get("/" + TABLE + builder.toString(),
128       Constants.MIMETYPE_XML);
129     assertEquals(200, response.getCode());
130     assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
131     JAXBContext ctx = JAXBContext.newInstance(CellSetModel.class);
132     Unmarshaller ush = ctx.createUnmarshaller();
133     CellSetModel model = (CellSetModel) ush.unmarshal(response.getStream());
134     int count = TestScannerResource.countCellSet(model);
135     assertEquals(10, count);
136     checkRowsNotNull(model);
137 
138     //Test with no limit.
139     builder = new StringBuilder();
140     builder.append("/*");
141     builder.append("?");
142     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
143     response = client.get("/" + TABLE + builder.toString(),
144       Constants.MIMETYPE_XML);
145     assertEquals(200, response.getCode());
146     assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type")); 
147     model = (CellSetModel) ush.unmarshal(response.getStream());
148     count = TestScannerResource.countCellSet(model);
149     assertEquals(expectedRows1, count);
150     checkRowsNotNull(model);
151 
152     //Test with start and end row.
153     builder = new StringBuilder();
154     builder.append("/*");
155     builder.append("?");
156     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
157     builder.append("&");
158     builder.append(Constants.SCAN_START_ROW + "=aaa");
159     builder.append("&");
160     builder.append(Constants.SCAN_END_ROW + "=aay");
161     response = client.get("/" + TABLE + builder.toString(),
162       Constants.MIMETYPE_XML);
163     assertEquals(200, response.getCode());
164     model = (CellSetModel) ush.unmarshal(response.getStream());
165     count = TestScannerResource.countCellSet(model);
166     RowModel startRow = model.getRows().get(0);
167     assertEquals("aaa", Bytes.toString(startRow.getKey()));
168     RowModel endRow = model.getRows().get(model.getRows().size() - 1);
169     assertEquals("aax", Bytes.toString(endRow.getKey()));
170     assertEquals(24, count);
171     checkRowsNotNull(model);
172 
173     //Test with start row and limit.
174     builder = new StringBuilder();
175     builder.append("/*");
176     builder.append("?");
177     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
178     builder.append("&");
179     builder.append(Constants.SCAN_START_ROW + "=aaa");
180     builder.append("&");
181     builder.append(Constants.SCAN_LIMIT + "=15");
182     response = client.get("/" + TABLE + builder.toString(),
183       Constants.MIMETYPE_XML);
184     assertEquals(200, response.getCode());
185     assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
186     model = (CellSetModel) ush.unmarshal(response.getStream());
187     startRow = model.getRows().get(0);
188     assertEquals("aaa", Bytes.toString(startRow.getKey()));
189     count = TestScannerResource.countCellSet(model);
190     assertEquals(15, count);
191     checkRowsNotNull(model);
192   }
193 
194   @Test
195   public void testSimpleScannerJson() throws IOException, JAXBException {
196     // Test scanning particular columns with limit.
197     StringBuilder builder = new StringBuilder();
198     builder.append("/*");
199     builder.append("?");
200     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
201     builder.append("&");
202     builder.append(Constants.SCAN_LIMIT + "=20");
203     Response response = client.get("/" + TABLE + builder.toString(),
204       Constants.MIMETYPE_JSON);
205     assertEquals(200, response.getCode());
206     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
207     ObjectMapper mapper = new JacksonProvider()
208         .locateMapper(CellSetModel.class, MediaType.APPLICATION_JSON_TYPE);
209     CellSetModel model = mapper.readValue(response.getStream(), CellSetModel.class);
210     int count = TestScannerResource.countCellSet(model);
211     assertEquals(20, count);
212     checkRowsNotNull(model);
213 
214     //Test scanning with no limit.
215     builder = new StringBuilder();
216     builder.append("/*");
217     builder.append("?");
218     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_2);
219     response = client.get("/" + TABLE + builder.toString(),
220       Constants.MIMETYPE_JSON);
221     assertEquals(200, response.getCode());
222     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
223     model = mapper.readValue(response.getStream(), CellSetModel.class);
224     count = TestScannerResource.countCellSet(model);
225     assertEquals(expectedRows2, count);
226     checkRowsNotNull(model);
227 
228     //Test with start row and end row.
229     builder = new StringBuilder();
230     builder.append("/*");
231     builder.append("?");
232     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
233     builder.append("&");
234     builder.append(Constants.SCAN_START_ROW + "=aaa");
235     builder.append("&");
236     builder.append(Constants.SCAN_END_ROW + "=aay");
237     response = client.get("/" + TABLE + builder.toString(),
238       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(),
263       Constants.MIMETYPE_XML);
264     assertEquals(200, response.getCode());
265     assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
266     JAXBContext context = JAXBContext.newInstance(ClientSideCellSetModel.class, RowModel.class,
267       CellModel.class);
268     Unmarshaller unmarshaller = context.createUnmarshaller();
269 
270     final ClientSideCellSetModel.Listener listener = new ClientSideCellSetModel.Listener() {
271       @Override
272       public void handleRowModel(ClientSideCellSetModel helper, RowModel row) {
273         assertTrue(row.getKey() != null);
274         assertTrue(row.getCells().size() > 0);
275       }
276     };
277 
278     // install the callback on all ClientSideCellSetModel instances
279     unmarshaller.setListener(new Unmarshaller.Listener() {
280         public void beforeUnmarshal(Object target, Object parent) {
281             if (target instanceof ClientSideCellSetModel) {
282                 ((ClientSideCellSetModel) target).setCellSetModelListener(listener);
283             }
284         }
285 
286         public void afterUnmarshal(Object target, Object parent) {
287             if (target instanceof ClientSideCellSetModel) {
288                 ((ClientSideCellSetModel) target).setCellSetModelListener(null);
289             }
290         }
291     });
292 
293     // create a new XML parser
294     SAXParserFactory factory = SAXParserFactory.newInstance();
295     factory.setNamespaceAware(true);
296     XMLReader reader = factory.newSAXParser().getXMLReader();
297     reader.setContentHandler(unmarshaller.getUnmarshallerHandler());
298     assertFalse(ClientSideCellSetModel.listenerInvoked);
299     reader.parse(new InputSource(response.getStream()));
300     assertTrue(ClientSideCellSetModel.listenerInvoked);
301 
302   }
303 
304   @Test
305   public void testStreamingJSON() throws Exception {
306     // Test scanning particular columns with limit.
307     StringBuilder builder = new StringBuilder();
308     builder.append("/*");
309     builder.append("?");
310     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
311     builder.append("&");
312     builder.append(Constants.SCAN_LIMIT + "=20");
313     Response response = client.get("/" + TABLE + builder.toString(),
314       Constants.MIMETYPE_JSON);
315     assertEquals(200, response.getCode());
316     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
317     ObjectMapper mapper = new JacksonProvider()
318         .locateMapper(CellSetModel.class, MediaType.APPLICATION_JSON_TYPE);
319     CellSetModel model = mapper.readValue(response.getStream(), CellSetModel.class);
320     int count = TestScannerResource.countCellSet(model);
321     assertEquals(20, count);
322     checkRowsNotNull(model);
323 
324     //Test scanning with no limit.
325     builder = new StringBuilder();
326     builder.append("/*");
327     builder.append("?");
328     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_2);
329     response = client.get("/" + TABLE + builder.toString(),
330       Constants.MIMETYPE_JSON);
331     assertEquals(200, response.getCode());
332     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
333     model = mapper.readValue(response.getStream(), CellSetModel.class);
334     count = TestScannerResource.countCellSet(model);
335     assertEquals(expectedRows2, count);
336     checkRowsNotNull(model);
337 
338     //Test with start row and end row.
339     builder = new StringBuilder();
340     builder.append("/*");
341     builder.append("?");
342     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
343     builder.append("&");
344     builder.append(Constants.SCAN_START_ROW + "=aaa");
345     builder.append("&");
346     builder.append(Constants.SCAN_END_ROW + "=aay");
347     response = client.get("/" + TABLE + builder.toString(),
348       Constants.MIMETYPE_JSON);
349     assertEquals(200, response.getCode());
350 
351     count = 0;
352     JsonFactory jfactory = new JsonFactory(mapper);
353     JsonParser jParser = jfactory.createJsonParser(response.getStream());
354     boolean found = false;
355     while (jParser.nextToken() != JsonToken.END_OBJECT) {
356       if(jParser.getCurrentToken() == JsonToken.START_OBJECT && found) {
357         RowModel row = jParser.readValueAs(RowModel.class);
358         assertNotNull(row.getKey());
359         for (int i = 0; i < row.getCells().size(); i++) {
360           if (count == 0) {
361             assertEquals("aaa", Bytes.toString(row.getKey()));
362           }
363           if (count == 23) {
364             assertEquals("aax", Bytes.toString(row.getKey()));
365           }
366           count++;
367         }
368         jParser.skipChildren();
369       } else {
370         found = jParser.getCurrentToken() == JsonToken.START_ARRAY;
371       }
372     }
373     assertEquals(24, count);
374   }
375 
376   @Test
377   public void testSimpleScannerProtobuf() throws Exception {
378     StringBuilder 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_LIMIT + "=15");
384     Response response = client.get("/" + TABLE + builder.toString(),
385       Constants.MIMETYPE_PROTOBUF);
386     assertEquals(200, response.getCode());
387     assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type"));
388     int rowCount = readProtobufStream(response.getStream());
389     assertEquals(15, rowCount);
390 
391   //Test with start row and end row.
392     builder = new StringBuilder();
393     builder.append("/*");
394     builder.append("?");
395     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
396     builder.append("&");
397     builder.append(Constants.SCAN_START_ROW + "=aaa");
398     builder.append("&");
399     builder.append(Constants.SCAN_END_ROW + "=aay");
400     response = client.get("/" + TABLE + builder.toString(),
401       Constants.MIMETYPE_PROTOBUF);
402     assertEquals(200, response.getCode());
403     assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type"));
404     rowCount = readProtobufStream(response.getStream());
405     assertEquals(24, rowCount);
406   }
407 
408   private void checkRowsNotNull(CellSetModel model) {
409     for (RowModel row: model.getRows()) {
410       assertTrue(row.getKey() != null);
411       assertTrue(row.getCells().size() > 0);
412     }
413   }
414 
415   /**
416    * Read protobuf stream.
417    * @param inputStream the input stream
418    * @return The number of rows in the cell set model.
419    * @throws IOException Signals that an I/O exception has occurred.
420    */
421   public int readProtobufStream(InputStream inputStream) throws IOException{
422     DataInputStream stream = new DataInputStream(inputStream);
423     CellSetModel model = null;
424     int rowCount = 0;
425     try {
426       while (true) {
427         byte[] lengthBytes = new byte[2];
428         int readBytes = stream.read(lengthBytes);
429         if (readBytes == -1) {
430           break;
431         }
432         assertEquals(2, readBytes);
433         int length = Bytes.toShort(lengthBytes);
434         byte[] cellset = new byte[length];
435         stream.read(cellset);
436         model = new CellSetModel();
437         model.getObjectFromMessage(cellset);
438         checkRowsNotNull(model);
439         rowCount = rowCount + TestScannerResource.countCellSet(model);
440       }
441     } catch (EOFException exp) {
442       exp.printStackTrace();
443     } finally {
444       stream.close();
445     }
446     return rowCount;
447   }
448 
449   @Test
450   public void testScanningUnknownColumnJson() throws IOException, JAXBException {
451     // Test scanning particular columns with limit.
452     StringBuilder builder = new StringBuilder();
453     builder.append("/*");
454     builder.append("?");
455     builder.append(Constants.SCAN_COLUMN + "=a:test");
456     Response response = client.get("/" + TABLE  + builder.toString(),
457       Constants.MIMETYPE_JSON);
458     assertEquals(200, response.getCode());
459     assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
460     ObjectMapper mapper = new JacksonProvider().locateMapper(CellSetModel.class,
461       MediaType.APPLICATION_JSON_TYPE);
462     CellSetModel model = mapper.readValue(response.getStream(), CellSetModel.class);
463     int count = TestScannerResource.countCellSet(model);
464     assertEquals(0, count);
465   }
466   
467   @Test
468   public void testSimpleFilter() throws IOException, JAXBException {
469     StringBuilder builder = new StringBuilder();
470     builder = new StringBuilder();
471     builder.append("/*");
472     builder.append("?");
473     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
474     builder.append("&");
475     builder.append(Constants.SCAN_START_ROW + "=aaa");
476     builder.append("&");
477     builder.append(Constants.SCAN_END_ROW + "=aay");
478     builder.append("&");
479     builder.append(Constants.SCAN_FILTER + "=" + URLEncoder.encode("PrefixFilter('aab')", "UTF-8"));
480     Response response =
481         client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML);
482     assertEquals(200, response.getCode());
483     JAXBContext ctx = JAXBContext.newInstance(CellSetModel.class);
484     Unmarshaller ush = ctx.createUnmarshaller();
485     CellSetModel model = (CellSetModel) ush.unmarshal(response.getStream());
486     int count = TestScannerResource.countCellSet(model);
487     assertEquals(1, count);
488     assertEquals("aab", new String(model.getRows().get(0).getCells().get(0).getValue()));
489   }
490 
491   @Test
492   public void testCompoundFilter() throws IOException, JAXBException {
493     StringBuilder builder = new StringBuilder();
494     builder = new StringBuilder();
495     builder.append("/*");
496     builder.append("?");
497     builder.append(Constants.SCAN_FILTER + "="
498         + URLEncoder.encode("PrefixFilter('abc') AND QualifierFilter(=,'binary:1')", "UTF-8"));
499     Response response =
500         client.get("/" + TABLE + builder.toString(), Constants.MIMETYPE_XML);
501     assertEquals(200, response.getCode());
502     JAXBContext ctx = JAXBContext.newInstance(CellSetModel.class);
503     Unmarshaller ush = ctx.createUnmarshaller();
504     CellSetModel model = (CellSetModel) ush.unmarshal(response.getStream());
505     int count = TestScannerResource.countCellSet(model);
506     assertEquals(1, count);
507     assertEquals("abc", new String(model.getRows().get(0).getCells().get(0).getValue()));
508   }
509 
510   @Test
511   public void testCustomFilter() throws IOException, JAXBException {
512     StringBuilder builder = new StringBuilder();
513     builder = new StringBuilder();
514     builder.append("/a*");
515     builder.append("?");
516     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
517     builder.append("&");
518     builder.append(Constants.SCAN_FILTER + "=" + URLEncoder.encode("CustomFilter('abc')", "UTF-8"));
519     Response response =
520         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     assertEquals(1, count);
527     assertEquals("abc", new String(model.getRows().get(0).getCells().get(0).getValue()));
528   }
529   
530   @Test
531   public void testNegativeCustomFilter() throws IOException, JAXBException {
532     StringBuilder builder = new StringBuilder();
533     builder = new StringBuilder();
534     builder.append("/b*");
535     builder.append("?");
536     builder.append(Constants.SCAN_COLUMN + "=" + COLUMN_1);
537     builder.append("&");
538     builder.append(Constants.SCAN_FILTER + "=" + URLEncoder.encode("CustomFilter('abc')", "UTF-8"));
539     Response response =
540         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     // Should return no rows as the filters conflict
547     assertEquals(0, count);
548   }
549 
550   public static class CustomFilter extends PrefixFilter {
551     private byte[] key = null;
552 
553     public CustomFilter(byte[] key) {
554       super(key);
555     }
556     
557     @Override
558     public boolean filterRowKey(byte[] buffer, int offset, int length) {
559       int cmp = Bytes.compareTo(buffer, offset, length, this.key, 0, this.key.length);
560       return cmp != 0;
561     }
562 
563     public static Filter createFilterFromArguments(ArrayList<byte[]> filterArguments) {
564       byte[] prefix = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0));
565       return new CustomFilter(prefix);
566     }
567   }
568 
569   /**
570    * The Class ClientSideCellSetModel which mimics cell set model, and contains listener to perform
571    * user defined operations on the row model.
572    */
573   @XmlRootElement(name = "CellSet")
574   @XmlAccessorType(XmlAccessType.FIELD)
575   public static class ClientSideCellSetModel implements Serializable {
576 
577     private static final long serialVersionUID = 1L;
578 
579     /**
580      * This list is not a real list; instead it will notify a listener whenever JAXB has
581      * unmarshalled the next row.
582      */
583     @XmlElement(name="Row")
584     private List<RowModel> row;
585 
586     static boolean listenerInvoked = false;
587 
588     /**
589      * Install a listener for row model on this object. If l is null, the listener
590      * is removed again.
591      */
592     public void setCellSetModelListener(final Listener l) {
593         row = (l == null) ? null : new ArrayList<RowModel>() {
594         private static final long serialVersionUID = 1L;
595 
596             public boolean add(RowModel o) {
597                 l.handleRowModel(ClientSideCellSetModel.this, o);
598                 listenerInvoked = true;
599                 return false;
600             }
601         };
602     }
603 
604     /**
605      * This listener is invoked every time a new row model is unmarshalled.
606      */
607     public static interface Listener {
608         void handleRowModel(ClientSideCellSetModel helper, RowModel rowModel);
609     }
610   }
611 }
612 
613 
614