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