View Javadoc

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