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