001/* 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020package org.apache.hadoop.hbase.rest; 021 022import java.io.IOException; 023import java.util.Base64; 024 025import javax.ws.rs.DELETE; 026import javax.ws.rs.GET; 027import javax.ws.rs.Produces; 028import javax.ws.rs.QueryParam; 029import javax.ws.rs.core.CacheControl; 030import javax.ws.rs.core.Context; 031import javax.ws.rs.core.Response; 032import javax.ws.rs.core.Response.ResponseBuilder; 033import javax.ws.rs.core.UriInfo; 034 035import org.apache.hadoop.hbase.Cell; 036import org.apache.hadoop.hbase.CellUtil; 037import org.apache.hadoop.hbase.TableNotFoundException; 038import org.apache.yetus.audience.InterfaceAudience; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041import org.apache.hadoop.hbase.rest.model.CellModel; 042import org.apache.hadoop.hbase.rest.model.CellSetModel; 043import org.apache.hadoop.hbase.rest.model.RowModel; 044import org.apache.hadoop.hbase.util.Bytes; 045 046@InterfaceAudience.Private 047public class ScannerInstanceResource extends ResourceBase { 048 private static final Logger LOG = 049 LoggerFactory.getLogger(ScannerInstanceResource.class); 050 051 static CacheControl cacheControl; 052 static { 053 cacheControl = new CacheControl(); 054 cacheControl.setNoCache(true); 055 cacheControl.setNoTransform(false); 056 } 057 058 ResultGenerator generator = null; 059 String id = null; 060 int batch = 1; 061 062 public ScannerInstanceResource() throws IOException { } 063 064 public ScannerInstanceResource(String table, String id, 065 ResultGenerator generator, int batch) throws IOException { 066 this.id = id; 067 this.generator = generator; 068 this.batch = batch; 069 } 070 071 @GET 072 @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF, 073 MIMETYPE_PROTOBUF_IETF}) 074 public Response get(final @Context UriInfo uriInfo, 075 @QueryParam("n") int maxRows, final @QueryParam("c") int maxValues) { 076 if (LOG.isTraceEnabled()) { 077 LOG.trace("GET " + uriInfo.getAbsolutePath()); 078 } 079 servlet.getMetrics().incrementRequests(1); 080 if (generator == null) { 081 servlet.getMetrics().incrementFailedGetRequests(1); 082 return Response.status(Response.Status.NOT_FOUND) 083 .type(MIMETYPE_TEXT).entity("Not found" + CRLF) 084 .build(); 085 } else { 086 // Updated the connection access time for each client next() call 087 RESTServlet.getInstance().getConnectionCache().updateConnectionAccessTime(); 088 } 089 CellSetModel model = new CellSetModel(); 090 RowModel rowModel = null; 091 byte[] rowKey = null; 092 int limit = batch; 093 if (maxValues > 0) { 094 limit = maxValues; 095 } 096 int count = limit; 097 do { 098 Cell value = null; 099 try { 100 value = generator.next(); 101 } catch (IllegalStateException e) { 102 if (ScannerResource.delete(id)) { 103 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 104 } else { 105 servlet.getMetrics().incrementFailedDeleteRequests(1); 106 } 107 servlet.getMetrics().incrementFailedGetRequests(1); 108 return Response.status(Response.Status.GONE) 109 .type(MIMETYPE_TEXT).entity("Gone" + CRLF) 110 .build(); 111 } catch (IllegalArgumentException e) { 112 Throwable t = e.getCause(); 113 if (t instanceof TableNotFoundException) { 114 return Response.status(Response.Status.NOT_FOUND) 115 .type(MIMETYPE_TEXT).entity("Not found" + CRLF) 116 .build(); 117 } 118 throw e; 119 } 120 if (value == null) { 121 if (LOG.isTraceEnabled()) { 122 LOG.trace("generator exhausted"); 123 } 124 // respond with 204 (No Content) if an empty cell set would be 125 // returned 126 if (count == limit) { 127 return Response.noContent().build(); 128 } 129 break; 130 } 131 if (rowKey == null) { 132 rowKey = CellUtil.cloneRow(value); 133 rowModel = new RowModel(rowKey); 134 } 135 if (!Bytes.equals(CellUtil.cloneRow(value), rowKey)) { 136 // if maxRows was given as a query param, stop if we would exceed the 137 // specified number of rows 138 if (maxRows > 0) { 139 if (--maxRows == 0) { 140 generator.putBack(value); 141 break; 142 } 143 } 144 model.addRow(rowModel); 145 rowKey = CellUtil.cloneRow(value); 146 rowModel = new RowModel(rowKey); 147 } 148 rowModel.addCell( 149 new CellModel(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value), 150 value.getTimestamp(), CellUtil.cloneValue(value))); 151 } while (--count > 0); 152 model.addRow(rowModel); 153 ResponseBuilder response = Response.ok(model); 154 response.cacheControl(cacheControl); 155 servlet.getMetrics().incrementSucessfulGetRequests(1); 156 return response.build(); 157 } 158 159 @GET 160 @Produces(MIMETYPE_BINARY) 161 public Response getBinary(final @Context UriInfo uriInfo) { 162 if (LOG.isTraceEnabled()) { 163 LOG.trace("GET " + uriInfo.getAbsolutePath() + " as " + 164 MIMETYPE_BINARY); 165 } 166 servlet.getMetrics().incrementRequests(1); 167 try { 168 Cell value = generator.next(); 169 if (value == null) { 170 if (LOG.isTraceEnabled()) { 171 LOG.trace("generator exhausted"); 172 } 173 return Response.noContent().build(); 174 } 175 ResponseBuilder response = Response.ok(CellUtil.cloneValue(value)); 176 response.cacheControl(cacheControl); 177 response.header("X-Row", Bytes.toString(Base64.getEncoder().encode( 178 CellUtil.cloneRow(value)))); 179 response.header("X-Column", Bytes.toString(Base64.getEncoder().encode( 180 CellUtil.makeColumn(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value))))); 181 response.header("X-Timestamp", value.getTimestamp()); 182 servlet.getMetrics().incrementSucessfulGetRequests(1); 183 return response.build(); 184 } catch (IllegalStateException e) { 185 if (ScannerResource.delete(id)) { 186 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 187 } else { 188 servlet.getMetrics().incrementFailedDeleteRequests(1); 189 } 190 servlet.getMetrics().incrementFailedGetRequests(1); 191 return Response.status(Response.Status.GONE) 192 .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) 205 .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF) 206 .build(); 207 } 208 if (ScannerResource.delete(id)) { 209 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 210 } else { 211 servlet.getMetrics().incrementFailedDeleteRequests(1); 212 } 213 return Response.ok().build(); 214 } 215}