View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.rest;
21  
22  import java.io.IOException;
23  
24  import javax.ws.rs.DELETE;
25  import javax.ws.rs.GET;
26  import javax.ws.rs.Produces;
27  import javax.ws.rs.QueryParam;
28  import javax.ws.rs.core.CacheControl;
29  import javax.ws.rs.core.Context;
30  import javax.ws.rs.core.Response;
31  import javax.ws.rs.core.Response.ResponseBuilder;
32  import javax.ws.rs.core.UriInfo;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  
37  import org.apache.hadoop.classification.InterfaceAudience;
38  import org.apache.hadoop.hbase.Cell;
39  import org.apache.hadoop.hbase.CellUtil;
40  import org.apache.hadoop.hbase.KeyValue;
41  import org.apache.hadoop.hbase.rest.model.CellModel;
42  import org.apache.hadoop.hbase.rest.model.CellSetModel;
43  import org.apache.hadoop.hbase.rest.model.RowModel;
44  import org.apache.hadoop.hbase.util.Base64;
45  import org.apache.hadoop.hbase.util.Bytes;
46  
47  @InterfaceAudience.Private
48  public class ScannerInstanceResource extends ResourceBase {
49    private static final Log LOG =
50      LogFactory.getLog(ScannerInstanceResource.class);
51  
52    static CacheControl cacheControl;
53    static {
54      cacheControl = new CacheControl();
55      cacheControl.setNoCache(true);
56      cacheControl.setNoTransform(false);
57    }
58  
59    ResultGenerator generator = null;
60    String id = null;
61    int batch = 1;
62  
63    public ScannerInstanceResource() throws IOException { }
64  
65    public ScannerInstanceResource(String table, String id, 
66        ResultGenerator generator, int batch) throws IOException {
67      this.id = id;
68      this.generator = generator;
69      this.batch = batch;
70    }
71  
72    @GET
73    @Produces({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
74      MIMETYPE_PROTOBUF_IETF})
75    public Response get(final @Context UriInfo uriInfo, 
76        @QueryParam("n") int maxRows, final @QueryParam("c") int maxValues) {
77      if (LOG.isDebugEnabled()) {
78        LOG.debug("GET " + uriInfo.getAbsolutePath());
79      }
80      servlet.getMetrics().incrementRequests(1);
81      if (generator == null) {
82        servlet.getMetrics().incrementFailedGetRequests(1);
83        return Response.status(Response.Status.NOT_FOUND)
84          .type(MIMETYPE_TEXT).entity("Not found" + CRLF)
85          .build();
86      }
87      CellSetModel model = new CellSetModel();
88      RowModel rowModel = null;
89      byte[] rowKey = null;
90      int limit = batch;
91      if (maxValues > 0) {
92        limit = maxValues;
93      }
94      int count = limit;
95      do {
96        Cell value = null;
97        try {
98          value = generator.next();
99        } catch (IllegalStateException e) {
100         if (ScannerResource.delete(id)) {
101           servlet.getMetrics().incrementSucessfulDeleteRequests(1);
102         } else {
103           servlet.getMetrics().incrementFailedDeleteRequests(1);
104         }
105         servlet.getMetrics().incrementFailedGetRequests(1);
106         return Response.status(Response.Status.GONE)
107           .type(MIMETYPE_TEXT).entity("Gone" + CRLF)
108           .build();
109       }
110       if (value == null) {
111         LOG.info("generator exhausted");
112         // respond with 204 (No Content) if an empty cell set would be
113         // returned
114         if (count == limit) {
115           return Response.noContent().build();
116         }
117         break;
118       }
119       if (rowKey == null) {
120         rowKey = CellUtil.cloneRow(value);
121         rowModel = new RowModel(rowKey);
122       }
123       if (!Bytes.equals(CellUtil.cloneRow(value), rowKey)) {
124         // if maxRows was given as a query param, stop if we would exceed the
125         // specified number of rows
126         if (maxRows > 0) { 
127           if (--maxRows == 0) {
128             generator.putBack(value);
129             break;
130           }
131         }
132         model.addRow(rowModel);
133         rowKey = CellUtil.cloneRow(value);
134         rowModel = new RowModel(rowKey);
135       }
136       rowModel.addCell(
137         new CellModel(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value), 
138           value.getTimestamp(), CellUtil.cloneValue(value)));
139     } while (--count > 0);
140     model.addRow(rowModel);
141     ResponseBuilder response = Response.ok(model);
142     response.cacheControl(cacheControl);
143     servlet.getMetrics().incrementSucessfulGetRequests(1);
144     return response.build();
145   }
146 
147   @GET
148   @Produces(MIMETYPE_BINARY)
149   public Response getBinary(final @Context UriInfo uriInfo) {
150     if (LOG.isDebugEnabled()) {
151       LOG.debug("GET " + uriInfo.getAbsolutePath() + " as " +
152         MIMETYPE_BINARY);
153     }
154     servlet.getMetrics().incrementRequests(1);
155     try {
156       Cell value = generator.next();
157       if (value == null) {
158         LOG.info("generator exhausted");
159         return Response.noContent().build();
160       }
161       ResponseBuilder response = Response.ok(CellUtil.cloneValue(value));
162       response.cacheControl(cacheControl);
163       response.header("X-Row", Base64.encodeBytes(CellUtil.cloneRow(value)));      
164       response.header("X-Column", 
165         Base64.encodeBytes(
166           KeyValue.makeColumn(CellUtil.cloneFamily(value), CellUtil.cloneQualifier(value))));
167       response.header("X-Timestamp", value.getTimestamp());
168       servlet.getMetrics().incrementSucessfulGetRequests(1);
169       return response.build();
170     } catch (IllegalStateException e) {
171       if (ScannerResource.delete(id)) {
172         servlet.getMetrics().incrementSucessfulDeleteRequests(1);
173       } else {
174         servlet.getMetrics().incrementFailedDeleteRequests(1);
175       }
176       servlet.getMetrics().incrementFailedGetRequests(1);
177       return Response.status(Response.Status.GONE)
178         .type(MIMETYPE_TEXT).entity("Gone" + CRLF)
179         .build();
180     }
181   }
182 
183   @DELETE
184   public Response delete(final @Context UriInfo uriInfo) {
185     if (LOG.isDebugEnabled()) {
186       LOG.debug("DELETE " + uriInfo.getAbsolutePath());
187     }
188     servlet.getMetrics().incrementRequests(1);
189     if (servlet.isReadOnly()) {
190       return Response.status(Response.Status.FORBIDDEN)
191         .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
192         .build();
193     }
194     if (ScannerResource.delete(id)) {
195       servlet.getMetrics().incrementSucessfulDeleteRequests(1);
196     } else {
197       servlet.getMetrics().incrementFailedDeleteRequests(1);
198     }
199     return Response.ok().build();
200   }
201 }