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 } 086 CellSetModel model = new CellSetModel(); 087 RowModel rowModel = null; 088 byte[] rowKey = null; 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) 106 .type(MIMETYPE_TEXT).entity("Gone" + CRLF) 107 .build(); 108 } catch (IllegalArgumentException e) { 109 Throwable t = e.getCause(); 110 if (t instanceof TableNotFoundException) { 111 return Response.status(Response.Status.NOT_FOUND) 112 .type(MIMETYPE_TEXT).entity("Not found" + CRLF) 113 .build(); 114 } 115 throw e; 116 } 117 if (value == null) { 118 if (LOG.isTraceEnabled()) { 119 LOG.trace("generator exhausted"); 120 } 121 // respond with 204 (No Content) if an empty cell set would be 122 // returned 123 if (count == limit) { 124 return Response.noContent().build(); 125 } 126 break; 127 } 128 if (rowKey == null) { 129 rowKey = CellUtil.cloneRow(value); 130 rowModel = new RowModel(rowKey); 131 } 132 if (!Bytes.equals(CellUtil.cloneRow(value), rowKey)) { 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 rowKey = CellUtil.cloneRow(value); 143 rowModel = new RowModel(rowKey); 144 } 145 rowModel.addCell( 146 new CellModel(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value), 147 value.getTimestamp(), CellUtil.cloneValue(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 " + 161 MIMETYPE_BINARY); 162 } 163 servlet.getMetrics().incrementRequests(1); 164 try { 165 Cell value = generator.next(); 166 if (value == null) { 167 if (LOG.isTraceEnabled()) { 168 LOG.trace("generator exhausted"); 169 } 170 return Response.noContent().build(); 171 } 172 ResponseBuilder response = Response.ok(CellUtil.cloneValue(value)); 173 response.cacheControl(cacheControl); 174 response.header("X-Row", Bytes.toString(Base64.getEncoder().encode( 175 CellUtil.cloneRow(value)))); 176 response.header("X-Column", Bytes.toString(Base64.getEncoder().encode( 177 CellUtil.makeColumn(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value))))); 178 response.header("X-Timestamp", value.getTimestamp()); 179 servlet.getMetrics().incrementSucessfulGetRequests(1); 180 return response.build(); 181 } catch (IllegalStateException e) { 182 if (ScannerResource.delete(id)) { 183 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 184 } else { 185 servlet.getMetrics().incrementFailedDeleteRequests(1); 186 } 187 servlet.getMetrics().incrementFailedGetRequests(1); 188 return Response.status(Response.Status.GONE) 189 .type(MIMETYPE_TEXT).entity("Gone" + CRLF) 190 .build(); 191 } 192 } 193 194 @DELETE 195 public Response delete(final @Context UriInfo uriInfo) { 196 if (LOG.isTraceEnabled()) { 197 LOG.trace("DELETE " + uriInfo.getAbsolutePath()); 198 } 199 servlet.getMetrics().incrementRequests(1); 200 if (servlet.isReadOnly()) { 201 return Response.status(Response.Status.FORBIDDEN) 202 .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF) 203 .build(); 204 } 205 if (ScannerResource.delete(id)) { 206 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 207 } else { 208 servlet.getMetrics().incrementFailedDeleteRequests(1); 209 } 210 return Response.ok().build(); 211 } 212}