Allan, Thanks for the bug report, analysis, and contribution. I will incorporate you patches as part of HBASE-4116: https://issues.apache.org/jira/browse/HBASE-4116
Best regards, - Andy Problems worthy of attack prove their worth by hitting back. - Piet Hein (via Tom White) ----- Original Message ----- > From: allan yan <hailun...@gmail.com> > To: user@hbase.apache.org > Cc: d...@hbase.apache.org > Sent: Monday, July 18, 2011 2:49 PM > Subject: HBase rest RowSpec bug on startRow and endRow > > Hi, > > I don't know how to report issue to HBase team. So I just post it here. > There might be a bug for REST web service to get rows with given startRow and > endRow. > > For example, to get a list of rows with startRow=testrow1, endRow=testrow2, > I > send GET request: > > curl http://localhost:8123/TestRowResource/testrow1,testrow2/a:1 > > And got StringIndexOutOfBoundsException: > > 2011-07-18 14:25:49,977 ERROR [1378924033@qtp-310826391-0] log.Slf4jLog(87): > /TestRowResource/testrow1,testrow2/a:1 > java.lang.IllegalArgumentException: > java.lang.StringIndexOutOfBoundsException: > String index out of range: -1 > at org.apache.hadoop.hbase.rest.RowSpec.parseRowKeys(RowSpec.java:84) > at org.apache.hadoop.hbase.rest.RowSpec.<init>(RowSpec.java:56) > at > org.apache.hadoop.hbase.rest.RowResource.<init>(RowResource.java:71) > at > org.apache.hadoop.hbase.rest.TableResource.getRowResource(TableResource.java:278) > at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) > at > sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) > at > sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) > at java.lang.reflect.Method.invoke(Method.java:597) > at > com.sun.jersey.server.impl.uri.rules.SubLocatorRule.invokeSubLocator(SubLocatorRule.java:165) > at > com.sun.jersey.server.impl.uri.rules.SubLocatorRule.accept(SubLocatorRule.java:97) > at > com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:136) > at > com.sun.jersey.server.impl.uri.rules.SubLocatorRule.accept(SubLocatorRule.java:121) > at > com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:136) > at > com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:86) > at > com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:136) > at > com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:74) > at > com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1357) > at > com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1289) > at > com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1239) > at > com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1229) > at > com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:420) > at > com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:497) > at > com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:684) > at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) > at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) > at > org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1221) > at > org.apache.hadoop.hbase.rest.filter.GzipFilter.doFilter(GzipFilter.java:73) > at > org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1212) > at > org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:399) > at > org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) > at > org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766) > at > org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) > at org.mortbay.jetty.Server.handle(Server.java:326) > at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) > at > org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:928) > at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549) > at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212) > at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) > at > org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228) > at > org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582) > Caused by: java.lang.StringIndexOutOfBoundsException: String index out of > range: > -1 > at java.lang.String.substring(String.java:1937) > at java.lang.String.substring(String.java:1904) > at org.apache.hadoop.hbase.rest.RowSpec.parseRowKeys(RowSpec.java:78) > ... 39 more > > This was because in the RowSpec.java, parseRowKeys method, startRow value was > changed: > --------------------------------------------------------------------------------- > startRow = sb.toString(); > int idx = startRow.indexOf(','); > if (idx != -1) { > startRow = URLDecoder.decode(startRow.substring(0, idx), > HConstants.UTF8_ENCODING); > endRow = URLDecoder.decode(startRow.substring(idx + 1), > HConstants.UTF8_ENCODING); > } else { > startRow = URLDecoder.decode(startRow, HConstants.UTF8_ENCODING); > } > --------------------------------------------------------------------------------- > After change to this, it works: > --------------------------------------------------------------------------------- > String row = sb.toString(); > int idx = row.indexOf(','); > if (idx != -1) { > startRow = URLDecoder.decode(row.substring(0, idx), > HConstants.UTF8_ENCODING); > endRow = URLDecoder.decode(row.substring(idx + 1), > HConstants.UTF8_ENCODING); > } else { > startRow = URLDecoder.decode(row, HConstants.UTF8_ENCODING); > } > --------------------------------------------------------------------------------- > > I've also created a unit test method in TestRowResource.java, > --------------------------------------------------------------------------------- > @Test > public void testStartEndRowGetPutXML() throws IOException, JAXBException { > String[] rows = {ROW_1,ROW_2,ROW_3}; > String[] values = {VALUE_1,VALUE_2,VALUE_3}; > Response response = null; > for(int i=0; i<rows.length; i++){ > response = putValueXML(TABLE, rows[i], COLUMN_1, values[i]); > assertEquals(200, response.getCode()); > checkValueXML(TABLE, rows[i], COLUMN_1, values[i]); > } > > response = getValueXML(TABLE, rows[0], rows[2], COLUMN_1); > assertEquals(200, response.getCode()); > CellSetModel cellSet = (CellSetModel) > unmarshaller.unmarshal(new ByteArrayInputStream(response.getBody())); > assertEquals(2, cellSet.getRows().size()); > for(int i=0; i<cellSet.getRows().size()-1; i++){ > RowModel rowModel = cellSet.getRows().get(i); > for(CellModel cell : rowModel.getCells()){ > assertEquals(COLUMN_1, Bytes.toString(cell.getColumn())); > assertEquals(values[i], Bytes.toString(cell.getValue())); > } > } > > for(String row : rows){ > response = deleteRow(TABLE, row); > assertEquals(200, response.getCode()); > } > } > > private static Response getValueXML(String table, String startRow, String > endRow, String column) > throws IOException { > StringBuilder path = new StringBuilder(); > path.append('/'); > path.append(table); > path.append('/'); > path.append(startRow); > path.append(","); > path.append(endRow); > path.append('/'); > path.append(column); > return getValueXML(path.toString()); > } > --------------------------------------------------------------------------------- > > Thanks, > Allan Yan >