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 java.io.IOException; 021import java.util.Base64; 022import org.apache.hadoop.hbase.Cell; 023import org.apache.hadoop.hbase.CellUtil; 024import org.apache.hadoop.hbase.TableNotFoundException; 025import org.apache.hadoop.hbase.rest.model.CellModel; 026import org.apache.hadoop.hbase.rest.model.CellSetModel; 027import org.apache.hadoop.hbase.rest.model.RowModel; 028import org.apache.hadoop.hbase.util.Bytes; 029import org.apache.yetus.audience.InterfaceAudience; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import org.apache.hbase.thirdparty.javax.ws.rs.DELETE; 034import org.apache.hbase.thirdparty.javax.ws.rs.GET; 035import org.apache.hbase.thirdparty.javax.ws.rs.Produces; 036import org.apache.hbase.thirdparty.javax.ws.rs.QueryParam; 037import org.apache.hbase.thirdparty.javax.ws.rs.core.CacheControl; 038import org.apache.hbase.thirdparty.javax.ws.rs.core.Context; 039import org.apache.hbase.thirdparty.javax.ws.rs.core.Response; 040import org.apache.hbase.thirdparty.javax.ws.rs.core.Response.ResponseBuilder; 041import org.apache.hbase.thirdparty.javax.ws.rs.core.UriInfo; 042 043@InterfaceAudience.Private 044public class ScannerInstanceResource extends ResourceBase { 045 private static final Logger LOG = LoggerFactory.getLogger(ScannerInstanceResource.class); 046 047 static CacheControl cacheControl; 048 static { 049 cacheControl = new CacheControl(); 050 cacheControl.setNoCache(true); 051 cacheControl.setNoTransform(false); 052 } 053 054 ResultGenerator generator = null; 055 String id = null; 056 int batch = 1; 057 058 public ScannerInstanceResource() throws IOException { 059 } 060 061 public ScannerInstanceResource(String table, String id, ResultGenerator generator, int batch) 062 throws IOException { 063 this.id = id; 064 this.generator = generator; 065 this.batch = batch; 066 } 067 068 @GET 069 @Produces({ MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF, MIMETYPE_PROTOBUF_IETF }) 070 public Response get(final @Context UriInfo uriInfo, @QueryParam("n") int maxRows, 071 final @QueryParam("c") int maxValues) { 072 if (LOG.isTraceEnabled()) { 073 LOG.trace("GET " + uriInfo.getAbsolutePath()); 074 } 075 servlet.getMetrics().incrementRequests(1); 076 if (generator == null) { 077 servlet.getMetrics().incrementFailedGetRequests(1); 078 return Response.status(Response.Status.NOT_FOUND).type(MIMETYPE_TEXT) 079 .entity("Not found" + CRLF).build(); 080 } else { 081 // Updated the connection access time for each client next() call 082 RESTServlet.getInstance().getConnectionCache().updateConnectionAccessTime(); 083 } 084 CellSetModel model = new CellSetModel(); 085 RowModel rowModel = null; 086 byte[] rowKeyArray = null; 087 int rowKeyOffset = 0; 088 int rowKeyLength = 0; 089 int limit = batch; 090 if (maxValues > 0) { 091 limit = maxValues; 092 } 093 int count = limit; 094 do { 095 Cell value = null; 096 try { 097 value = generator.next(); 098 } catch (IllegalStateException e) { 099 if (ScannerResource.delete(id)) { 100 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 101 } else { 102 servlet.getMetrics().incrementFailedDeleteRequests(1); 103 } 104 servlet.getMetrics().incrementFailedGetRequests(1); 105 return Response.status(Response.Status.GONE).type(MIMETYPE_TEXT).entity("Gone" + CRLF) 106 .build(); 107 } catch (IllegalArgumentException e) { 108 Throwable t = e.getCause(); 109 if (t instanceof TableNotFoundException) { 110 return Response.status(Response.Status.NOT_FOUND).type(MIMETYPE_TEXT) 111 .entity("Not found" + CRLF).build(); 112 } 113 throw e; 114 } 115 if (value == null) { 116 if (LOG.isTraceEnabled()) { 117 LOG.trace("generator exhausted"); 118 } 119 // respond with 204 (No Content) if an empty cell set would be 120 // returned 121 if (count == limit) { 122 return Response.noContent().build(); 123 } 124 break; 125 } 126 if (rowKeyArray == null) { 127 rowKeyArray = value.getRowArray(); 128 rowKeyOffset = value.getRowOffset(); 129 rowKeyLength = value.getRowLength(); 130 rowModel = new RowModel(rowKeyArray, rowKeyOffset, rowKeyLength); 131 } 132 if (!CellUtil.matchingRow(value, rowKeyArray, rowKeyOffset, rowKeyLength)) { 133 // if maxRows was given as a query param, stop if we would exceed the 134 // specified number of rows 135 if (maxRows > 0) { 136 if (--maxRows == 0) { 137 generator.putBack(value); 138 break; 139 } 140 } 141 model.addRow(rowModel); 142 rowKeyArray = value.getRowArray(); 143 rowKeyOffset = value.getRowOffset(); 144 rowKeyLength = value.getRowLength(); 145 rowModel = new RowModel(rowKeyArray, rowKeyOffset, rowKeyLength); 146 } 147 rowModel.addCell(new CellModel(value)); 148 } while (--count > 0); 149 model.addRow(rowModel); 150 ResponseBuilder response = Response.ok(model); 151 response.cacheControl(cacheControl); 152 servlet.getMetrics().incrementSucessfulGetRequests(1); 153 return response.build(); 154 } 155 156 @GET 157 @Produces(MIMETYPE_BINARY) 158 public Response getBinary(final @Context UriInfo uriInfo) { 159 if (LOG.isTraceEnabled()) { 160 LOG.trace("GET " + uriInfo.getAbsolutePath() + " as " + MIMETYPE_BINARY); 161 } 162 servlet.getMetrics().incrementRequests(1); 163 try { 164 if (generator == null) { 165 servlet.getMetrics().incrementFailedGetRequests(1); 166 return Response.status(Response.Status.NOT_FOUND).type(MIMETYPE_TEXT) 167 .entity("Not found" + CRLF).build(); 168 } 169 Cell value = generator.next(); 170 if (value == null) { 171 if (LOG.isTraceEnabled()) { 172 LOG.trace("generator exhausted"); 173 } 174 return Response.noContent().build(); 175 } 176 ResponseBuilder response = Response.ok(CellUtil.cloneValue(value)); 177 response.cacheControl(cacheControl); 178 response.header("X-Row", 179 Bytes.toString(Base64.getEncoder().encode(CellUtil.cloneRow(value)))); 180 response.header("X-Column", Bytes.toString(Base64.getEncoder() 181 .encode(CellUtil.makeColumn(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value))))); 182 response.header("X-Timestamp", value.getTimestamp()); 183 servlet.getMetrics().incrementSucessfulGetRequests(1); 184 return response.build(); 185 } catch (IllegalStateException e) { 186 if (ScannerResource.delete(id)) { 187 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 188 } else { 189 servlet.getMetrics().incrementFailedDeleteRequests(1); 190 } 191 servlet.getMetrics().incrementFailedGetRequests(1); 192 return Response.status(Response.Status.GONE).type(MIMETYPE_TEXT).entity("Gone" + CRLF) 193 .build(); 194 } 195 } 196 197 @DELETE 198 public Response delete(final @Context UriInfo uriInfo) { 199 if (LOG.isTraceEnabled()) { 200 LOG.trace("DELETE " + uriInfo.getAbsolutePath()); 201 } 202 servlet.getMetrics().incrementRequests(1); 203 if (servlet.isReadOnly()) { 204 return Response.status(Response.Status.FORBIDDEN).type(MIMETYPE_TEXT) 205 .entity("Forbidden" + CRLF).build(); 206 } 207 if (generator == null) { 208 servlet.getMetrics().incrementFailedDeleteRequests(1); 209 return Response.status(Response.Status.NOT_FOUND).type(MIMETYPE_TEXT) 210 .entity("Not found" + CRLF).build(); 211 } 212 if (ScannerResource.delete(id)) { 213 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 214 } else { 215 servlet.getMetrics().incrementFailedDeleteRequests(1); 216 } 217 return Response.ok().build(); 218 } 219}