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[] rowKey = null; 087 int limit = batch; 088 if (maxValues > 0) { 089 limit = maxValues; 090 } 091 int count = limit; 092 do { 093 Cell value = null; 094 try { 095 value = generator.next(); 096 } catch (IllegalStateException e) { 097 if (ScannerResource.delete(id)) { 098 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 099 } else { 100 servlet.getMetrics().incrementFailedDeleteRequests(1); 101 } 102 servlet.getMetrics().incrementFailedGetRequests(1); 103 return Response.status(Response.Status.GONE).type(MIMETYPE_TEXT).entity("Gone" + CRLF) 104 .build(); 105 } catch (IllegalArgumentException e) { 106 Throwable t = e.getCause(); 107 if (t instanceof TableNotFoundException) { 108 return Response.status(Response.Status.NOT_FOUND).type(MIMETYPE_TEXT) 109 .entity("Not found" + CRLF).build(); 110 } 111 throw e; 112 } 113 if (value == null) { 114 if (LOG.isTraceEnabled()) { 115 LOG.trace("generator exhausted"); 116 } 117 // respond with 204 (No Content) if an empty cell set would be 118 // returned 119 if (count == limit) { 120 return Response.noContent().build(); 121 } 122 break; 123 } 124 if (rowKey == null) { 125 rowKey = CellUtil.cloneRow(value); 126 rowModel = new RowModel(rowKey); 127 } 128 if (!Bytes.equals(CellUtil.cloneRow(value), rowKey)) { 129 // if maxRows was given as a query param, stop if we would exceed the 130 // specified number of rows 131 if (maxRows > 0) { 132 if (--maxRows == 0) { 133 generator.putBack(value); 134 break; 135 } 136 } 137 model.addRow(rowModel); 138 rowKey = CellUtil.cloneRow(value); 139 rowModel = new RowModel(rowKey); 140 } 141 rowModel.addCell(new CellModel(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value), 142 value.getTimestamp(), CellUtil.cloneValue(value))); 143 } while (--count > 0); 144 model.addRow(rowModel); 145 ResponseBuilder response = Response.ok(model); 146 response.cacheControl(cacheControl); 147 servlet.getMetrics().incrementSucessfulGetRequests(1); 148 return response.build(); 149 } 150 151 @GET 152 @Produces(MIMETYPE_BINARY) 153 public Response getBinary(final @Context UriInfo uriInfo) { 154 if (LOG.isTraceEnabled()) { 155 LOG.trace("GET " + uriInfo.getAbsolutePath() + " as " + MIMETYPE_BINARY); 156 } 157 servlet.getMetrics().incrementRequests(1); 158 try { 159 Cell value = generator.next(); 160 if (value == null) { 161 if (LOG.isTraceEnabled()) { 162 LOG.trace("generator exhausted"); 163 } 164 return Response.noContent().build(); 165 } 166 ResponseBuilder response = Response.ok(CellUtil.cloneValue(value)); 167 response.cacheControl(cacheControl); 168 response.header("X-Row", 169 Bytes.toString(Base64.getEncoder().encode(CellUtil.cloneRow(value)))); 170 response.header("X-Column", Bytes.toString(Base64.getEncoder() 171 .encode(CellUtil.makeColumn(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value))))); 172 response.header("X-Timestamp", value.getTimestamp()); 173 servlet.getMetrics().incrementSucessfulGetRequests(1); 174 return response.build(); 175 } catch (IllegalStateException e) { 176 if (ScannerResource.delete(id)) { 177 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 178 } else { 179 servlet.getMetrics().incrementFailedDeleteRequests(1); 180 } 181 servlet.getMetrics().incrementFailedGetRequests(1); 182 return Response.status(Response.Status.GONE).type(MIMETYPE_TEXT).entity("Gone" + CRLF) 183 .build(); 184 } 185 } 186 187 @DELETE 188 public Response delete(final @Context UriInfo uriInfo) { 189 if (LOG.isTraceEnabled()) { 190 LOG.trace("DELETE " + uriInfo.getAbsolutePath()); 191 } 192 servlet.getMetrics().incrementRequests(1); 193 if (servlet.isReadOnly()) { 194 return Response.status(Response.Status.FORBIDDEN).type(MIMETYPE_TEXT) 195 .entity("Forbidden" + CRLF).build(); 196 } 197 if (ScannerResource.delete(id)) { 198 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 199 } else { 200 servlet.getMetrics().incrementFailedDeleteRequests(1); 201 } 202 return Response.ok().build(); 203 } 204}