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 com.fasterxml.jackson.annotation.JsonIgnore;
021import com.fasterxml.jackson.annotation.JsonProperty;
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Iterator;
025import java.util.List;
026import javax.xml.bind.annotation.XmlAccessType;
027import javax.xml.bind.annotation.XmlAccessorType;
028import javax.xml.bind.annotation.XmlElement;
029import javax.xml.bind.annotation.XmlRootElement;
030import org.apache.hadoop.hbase.Cell;
031import org.apache.hadoop.hbase.CellUtil;
032import org.apache.hadoop.hbase.client.Result;
033import org.apache.hadoop.hbase.client.ResultScanner;
034import org.apache.hadoop.hbase.rest.model.CellModel;
035import org.apache.hadoop.hbase.rest.model.RowModel;
036import org.apache.yetus.audience.InterfaceAudience;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040import org.apache.hbase.thirdparty.javax.ws.rs.GET;
041import org.apache.hbase.thirdparty.javax.ws.rs.HeaderParam;
042import org.apache.hbase.thirdparty.javax.ws.rs.Produces;
043import org.apache.hbase.thirdparty.javax.ws.rs.core.Context;
044import org.apache.hbase.thirdparty.javax.ws.rs.core.Response;
045import org.apache.hbase.thirdparty.javax.ws.rs.core.Response.ResponseBuilder;
046import org.apache.hbase.thirdparty.javax.ws.rs.core.StreamingOutput;
047import org.apache.hbase.thirdparty.javax.ws.rs.core.UriInfo;
048
049@InterfaceAudience.Private
050public class TableScanResource extends ResourceBase {
051  private static final Logger LOG = LoggerFactory.getLogger(TableScanResource.class);
052
053  TableResource tableResource;
054  ResultScanner results;
055  int userRequestedLimit;
056
057  public TableScanResource(ResultScanner scanner, int userRequestedLimit) throws IOException {
058    super();
059    this.results = scanner;
060    this.userRequestedLimit = userRequestedLimit;
061  }
062
063  @GET
064  @Produces({ Constants.MIMETYPE_XML, Constants.MIMETYPE_JSON })
065  public CellSetModelStream get(final @Context UriInfo uriInfo) {
066    if (LOG.isTraceEnabled()) {
067      LOG.trace("GET " + uriInfo.getAbsolutePath());
068    }
069    servlet.getMetrics().incrementRequests(1);
070    final int rowsToSend = userRequestedLimit;
071    servlet.getMetrics().incrementSucessfulScanRequests(1);
072    final Iterator<Result> itr = results.iterator();
073    return new CellSetModelStream(new ArrayList<RowModel>() {
074      @Override
075      public Iterator<RowModel> iterator() {
076        return new Iterator<RowModel>() {
077          int count = rowsToSend;
078
079          @Override
080          public boolean hasNext() {
081            return count > 0 && itr.hasNext();
082          }
083
084          @Override
085          public RowModel next() {
086            Result rs = itr.next();
087            if ((rs == null) || (count <= 0)) {
088              return null;
089            }
090            byte[] rowKey = rs.getRow();
091            RowModel rModel = new RowModel(rowKey);
092            List<Cell> kvs = rs.listCells();
093            for (Cell kv : kvs) {
094              rModel.addCell(new CellModel(CellUtil.cloneFamily(kv), CellUtil.cloneQualifier(kv),
095                kv.getTimestamp(), CellUtil.cloneValue(kv)));
096            }
097            count--;
098            if (count == 0) {
099              results.close();
100            }
101            return rModel;
102          }
103        };
104      }
105    });
106  }
107
108  @GET
109  @Produces({ Constants.MIMETYPE_PROTOBUF, Constants.MIMETYPE_PROTOBUF_IETF })
110  public Response getProtobuf(final @Context UriInfo uriInfo,
111    final @HeaderParam("Accept") String contentType) {
112    if (LOG.isTraceEnabled()) {
113      LOG.trace("GET " + uriInfo.getAbsolutePath() + " as " + MIMETYPE_BINARY);
114    }
115    servlet.getMetrics().incrementRequests(1);
116    try {
117      int fetchSize = this.servlet.getConfiguration().getInt(Constants.SCAN_FETCH_SIZE, 10);
118      StreamingOutput stream =
119        new ProtobufStreamingOutput(this.results, contentType, userRequestedLimit, fetchSize);
120      servlet.getMetrics().incrementSucessfulScanRequests(1);
121      ResponseBuilder response = Response.ok(stream);
122      response.header("content-type", contentType);
123      return response.build();
124    } catch (Exception exp) {
125      servlet.getMetrics().incrementFailedScanRequests(1);
126      processException(exp);
127      LOG.warn(exp.toString(), exp);
128      return null;
129    }
130  }
131
132  @XmlRootElement(name = "CellSet")
133  @XmlAccessorType(XmlAccessType.FIELD)
134  public static class CellSetModelStream {
135    // JAXB needs an arraylist for streaming
136    @XmlElement(name = "Row")
137    @JsonIgnore
138    private ArrayList<RowModel> Row;
139
140    public CellSetModelStream() {
141    }
142
143    public CellSetModelStream(final ArrayList<RowModel> rowList) {
144      this.Row = rowList;
145    }
146
147    // jackson needs an iterator for streaming
148    @JsonProperty("Row")
149    public Iterator<RowModel> getIterator() {
150      return Row.iterator();
151    }
152  }
153}