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.List;
022import org.apache.commons.lang3.StringUtils;
023import org.apache.hadoop.hbase.CellUtil;
024import org.apache.hadoop.hbase.TableName;
025import org.apache.hadoop.hbase.client.Scan;
026import org.apache.hadoop.hbase.client.Table;
027import org.apache.hadoop.hbase.filter.Filter;
028import org.apache.hadoop.hbase.filter.FilterList;
029import org.apache.hadoop.hbase.filter.ParseFilter;
030import org.apache.hadoop.hbase.filter.PrefixFilter;
031import org.apache.hadoop.hbase.util.Bytes;
032import org.apache.yetus.audience.InterfaceAudience;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036import org.apache.hbase.thirdparty.javax.ws.rs.DefaultValue;
037import org.apache.hbase.thirdparty.javax.ws.rs.Encoded;
038import org.apache.hbase.thirdparty.javax.ws.rs.Path;
039import org.apache.hbase.thirdparty.javax.ws.rs.PathParam;
040import org.apache.hbase.thirdparty.javax.ws.rs.QueryParam;
041
042@InterfaceAudience.Private
043public class TableResource extends ResourceBase {
044
045  String table;
046  private static final Logger LOG = LoggerFactory.getLogger(TableResource.class);
047
048  /**
049   * Constructor nn
050   */
051  public TableResource(String table) throws IOException {
052    super();
053    this.table = table;
054  }
055
056  /** Returns the table name */
057  String getName() {
058    return table;
059  }
060
061  /** Returns true if the table exists n */
062  boolean exists() throws IOException {
063    return servlet.getAdmin().tableExists(TableName.valueOf(table));
064  }
065
066  @Path("exists")
067  public ExistsResource getExistsResource() throws IOException {
068    return new ExistsResource(this);
069  }
070
071  @Path("regions")
072  public RegionsResource getRegionsResource() throws IOException {
073    return new RegionsResource(this);
074  }
075
076  @Path("scanner")
077  public ScannerResource getScannerResource() throws IOException {
078    return new ScannerResource(this);
079  }
080
081  @Path("schema")
082  public SchemaResource getSchemaResource() throws IOException {
083    return new SchemaResource(this);
084  }
085
086  @Path("{multiget: multiget.*}")
087  public MultiRowResource getMultipleRowResource(final @QueryParam("v") String versions,
088    @PathParam("multiget") String path) throws IOException {
089    return new MultiRowResource(this, versions, path.replace("multiget", "").replace("/", ""));
090  }
091
092  @Path("{rowspec: [^*]+}")
093  public RowResource getRowResource(
094    // We need the @Encoded decorator so Jersey won't urldecode before
095    // the RowSpec constructor has a chance to parse
096    final @PathParam("rowspec") @Encoded String rowspec, final @QueryParam("v") String versions,
097    final @QueryParam("check") String check, final @QueryParam("rr") String returnResult)
098    throws IOException {
099    return new RowResource(this, rowspec, versions, check, returnResult);
100  }
101
102  @Path("{suffixglobbingspec: .*\\*/.+}")
103  public RowResource getRowResourceWithSuffixGlobbing(
104    // We need the @Encoded decorator so Jersey won't urldecode before
105    // the RowSpec constructor has a chance to parse
106    final @PathParam("suffixglobbingspec") @Encoded String suffixglobbingspec,
107    final @QueryParam("v") String versions, final @QueryParam("check") String check,
108    final @QueryParam("rr") String returnResult) throws IOException {
109    return new RowResource(this, suffixglobbingspec, versions, check, returnResult);
110  }
111
112  @Path("{scanspec: .*[*]$}")
113  public TableScanResource getScanResource(final @PathParam("scanspec") String scanSpec,
114    @DefaultValue(Integer.MAX_VALUE + "") @QueryParam(Constants.SCAN_LIMIT) int userRequestedLimit,
115    @DefaultValue("") @QueryParam(Constants.SCAN_START_ROW) String startRow,
116    @DefaultValue("") @QueryParam(Constants.SCAN_END_ROW) String endRow,
117    @QueryParam(Constants.SCAN_COLUMN) List<String> column,
118    @DefaultValue("1") @QueryParam(Constants.SCAN_MAX_VERSIONS) int maxVersions,
119    @DefaultValue("-1") @QueryParam(Constants.SCAN_BATCH_SIZE) int batchSize,
120    @DefaultValue("0") @QueryParam(Constants.SCAN_START_TIME) long startTime,
121    @DefaultValue(Long.MAX_VALUE + "") @QueryParam(Constants.SCAN_END_TIME) long endTime,
122    @DefaultValue("true") @QueryParam(Constants.SCAN_CACHE_BLOCKS) boolean cacheBlocks,
123    @DefaultValue("false") @QueryParam(Constants.SCAN_REVERSED) boolean reversed,
124    @DefaultValue("") @QueryParam(Constants.SCAN_FILTER) String paramFilter) {
125    try {
126      Filter prefixFilter = null;
127      Scan tableScan = new Scan();
128      if (scanSpec.indexOf('*') > 0) {
129        String prefix = scanSpec.substring(0, scanSpec.indexOf('*'));
130        byte[] prefixBytes = Bytes.toBytes(prefix);
131        prefixFilter = new PrefixFilter(Bytes.toBytes(prefix));
132        if (startRow.isEmpty()) {
133          tableScan.withStartRow(prefixBytes);
134        }
135      }
136      if (LOG.isTraceEnabled()) {
137        LOG.trace("Query parameters  : Table Name = > " + this.table + " Start Row => " + startRow
138          + " End Row => " + endRow + " Columns => " + column + " Start Time => " + startTime
139          + " End Time => " + endTime + " Cache Blocks => " + cacheBlocks + " Max Versions => "
140          + maxVersions + " Batch Size => " + batchSize);
141      }
142      Table hTable = RESTServlet.getInstance().getTable(this.table);
143      tableScan.setBatch(batchSize);
144      tableScan.readVersions(maxVersions);
145      tableScan.setTimeRange(startTime, endTime);
146      if (!startRow.isEmpty()) {
147        tableScan.withStartRow(Bytes.toBytes(startRow));
148      }
149      tableScan.withStopRow(Bytes.toBytes(endRow));
150      for (String col : column) {
151        byte[][] parts = CellUtil.parseColumn(Bytes.toBytes(col.trim()));
152        if (parts.length == 1) {
153          if (LOG.isTraceEnabled()) {
154            LOG.trace("Scan family : " + Bytes.toStringBinary(parts[0]));
155          }
156          tableScan.addFamily(parts[0]);
157        } else if (parts.length == 2) {
158          if (LOG.isTraceEnabled()) {
159            LOG.trace("Scan family and column : " + Bytes.toStringBinary(parts[0]) + "  "
160              + Bytes.toStringBinary(parts[1]));
161          }
162          tableScan.addColumn(parts[0], parts[1]);
163        } else {
164          throw new IllegalArgumentException("Invalid column specifier.");
165        }
166      }
167      FilterList filterList = new FilterList();
168      if (StringUtils.isNotEmpty(paramFilter)) {
169        ParseFilter pf = new ParseFilter();
170        Filter parsedParamFilter = pf.parseFilterString(paramFilter);
171        if (parsedParamFilter != null) {
172          filterList.addFilter(parsedParamFilter);
173        }
174        if (prefixFilter != null) {
175          filterList.addFilter(prefixFilter);
176        }
177      }
178      if (filterList.size() > 0) {
179        tableScan.setFilter(filterList);
180      }
181
182      int fetchSize = this.servlet.getConfiguration().getInt(Constants.SCAN_FETCH_SIZE, 10);
183      tableScan.setCaching(fetchSize);
184      tableScan.setReversed(reversed);
185      tableScan.setCacheBlocks(cacheBlocks);
186      return new TableScanResource(hTable.getScanner(tableScan), userRequestedLimit);
187    } catch (IOException exp) {
188      servlet.getMetrics().incrementFailedScanRequests(1);
189      processException(exp);
190      LOG.warn(exp.toString(), exp);
191      return null;
192    }
193  }
194}