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}