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}