Package org.apache.hadoop.hbase

Source Code of org.apache.hadoop.hbase.HTable$ClientScanner

/**
* Copyright 2007 The Apache Software Foundation
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.Callable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.filter.RowFilterInterface;
import org.apache.hadoop.hbase.filter.StopRowFilter;
import org.apache.hadoop.hbase.filter.WhileMatchRowFilter;
import org.apache.hadoop.hbase.io.BatchUpdate;
import org.apache.hadoop.hbase.io.HbaseMapWritable;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.util.Writables;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.ipc.RemoteException;

/**
* Used to communicate with a single HBase table
*/
public class HTable implements HConstants {
  protected final Log LOG = LogFactory.getLog(this.getClass().getName());

  protected final HConnection connection;
  protected final Text tableName;
  protected final long pause;
  protected final int numRetries;
  protected Random rand;
  protected AtomicReference<BatchUpdate> batch;

  protected volatile boolean tableDoesNotExist;
 
  // For row mutation operations
 
  protected volatile boolean closed;

  protected void checkClosed() {
    if (tableDoesNotExist) {
      throw new IllegalStateException("table does not exist: " + tableName);
    }
    if (closed) {
      throw new IllegalStateException("table is closed");
    }
  }
 
  /**
   * Creates an object to access a HBase table
   *
   * @param conf configuration object
   * @param tableName name of the table
   * @throws IOException
   */
  public HTable(HBaseConfiguration conf, Text tableName) throws IOException {
    closed = true;
    tableDoesNotExist = true;
    this.connection = HConnectionManager.getConnection(conf);
    this.tableName = tableName;
    this.pause = conf.getLong("hbase.client.pause", 10 * 1000);
    this.numRetries = conf.getInt("hbase.client.retries.number", 5);
    this.rand = new Random();
    this.batch = new AtomicReference<BatchUpdate>();
    this.connection.locateRegion(tableName, EMPTY_START_ROW);
    tableDoesNotExist = false;
    closed = false;
  }

  /**
   * Find region location hosting passed row using cached info
   * @param row Row to find.
   * @return Location of row.
   */
  HRegionLocation getRegionLocation(Text row) throws IOException {
    checkClosed();
    return this.connection.locateRegion(this.tableName, row);
  }

  /**
   * Find region location hosting passed row
   * @param row Row to find.
   * @param reload If true do not use cache, otherwise bypass.
   * @return Location of row.
   */
  HRegionLocation getRegionLocation(Text row, boolean reload) throws IOException {
    checkClosed();
    return reload?
      this.connection.relocateRegion(this.tableName, row):
      this.connection.locateRegion(tableName, row);
  }


  /** @return the connection */
  public HConnection getConnection() {
    checkClosed();
    return connection;
  }

  /**
   * Releases resources associated with this table. After calling close(), all
   * other methods will throw an IllegalStateException
   */
  public synchronized void close() {
    if (!closed) {
      closed = true;
      batch.set(null);
      connection.close(tableName);
    }
  }
 
  /**
   * Verifies that no update is in progress
   */
  public synchronized void checkUpdateInProgress() {
    updateInProgress(false);
  }
 
  /*
   * Checks to see if an update is in progress
   *
   * @param updateMustBeInProgress
   *    If true, an update must be in progress. An IllegalStateException will be
   *    thrown if not.
   *   
   *    If false, an update must not be in progress. An IllegalStateException
   *    will be thrown if an update is in progress.
   */
  private void updateInProgress(boolean updateMustBeInProgress) {
    if (updateMustBeInProgress) {
      if (batch.get() == null) {
        throw new IllegalStateException("no update in progress");
      }
    } else {
      if (batch.get() != null) {
        throw new IllegalStateException("update in progress");
      }
    }
  }
 

  /** @return the table name */
  public Text getTableName() {
    return this.tableName;
  }

  /**
   * @return table metadata
   * @throws IOException
   */
  public HTableDescriptor getMetadata() throws IOException {
    HTableDescriptor [] metas = this.connection.listTables();
    HTableDescriptor result = null;
    for (int i = 0; i < metas.length; i++) {
      if (metas[i].getName().equals(this.tableName)) {
        result = metas[i];
        break;
      }
    }
    return result;
  }

  /**
   * Gets the starting row key for every region in the currently open table
   * @return Array of region starting row keys
   * @throws IOException
   */
  @SuppressWarnings("null")
  public Text[] getStartKeys() throws IOException {
    checkClosed();
    List<Text> keyList = new ArrayList<Text>();

    long scannerId = -1L;

    Text startRow = new Text(tableName.toString() + ",,999999999999999");
    HRegionLocation metaLocation = null;
    HRegionInterface server;
   
    // scan over the each meta region
    do {
      try{
        // turn the start row into a location
        metaLocation =
          connection.locateRegion(META_TABLE_NAME, startRow);

        // connect to the server hosting the .META. region
        server =
          connection.getHRegionConnection(metaLocation.getServerAddress());

        // open a scanner over the meta region
        scannerId = server.openScanner(
          metaLocation.getRegionInfo().getRegionName(),
          COLUMN_FAMILY_ARRAY, tableName, LATEST_TIMESTAMP,
          null);
       
        // iterate through the scanner, accumulating unique table names
        SCANNER_LOOP: while (true) {
          HbaseMapWritable values = server.next(scannerId);
          if (values == null || values.size() == 0) {
            break;
          }
          for (Map.Entry<Writable, Writable> e: values.entrySet()) {
            HStoreKey key = (HStoreKey) e.getKey();
            if (key.getColumn().equals(COL_REGIONINFO)) {
              HRegionInfo info = new HRegionInfo();
              info = (HRegionInfo) Writables.getWritable(
                  ((ImmutableBytesWritable) e.getValue()).get(), info);

              if (!info.getTableDesc().getName().equals(this.tableName)) {
                break SCANNER_LOOP;
              }

              if (info.isOffline()) {
                continue SCANNER_LOOP;
              }

              if (info.isSplit()) {
                continue SCANNER_LOOP;
              }

              keyList.add(info.getStartKey());
            }
          }
        }
       
        // close that remote scanner
        server.close(scannerId);
         
        // advance the startRow to the end key of the current region
        startRow = metaLocation.getRegionInfo().getEndKey();         
      } catch (IOException e) {
        // need retry logic?
        throw e;
      }
    } while (startRow.compareTo(EMPTY_START_ROW) != 0);

    Text[] arr = new Text[keyList.size()];
    for (int i = 0; i < keyList.size(); i++ ){
      arr[i] = keyList.get(i);
    }
   
    return arr;
  }
 
  /**
   * Get a single value for the specified row and column
   *
   * @param row row key
   * @param column column name
   * @return value for specified row/column
   * @throws IOException
   */
   public byte[] get(Text row, final Text column) throws IOException {
     checkClosed();
    
     return getRegionServerWithRetries(new ServerCallable<byte[]>(row){
       public byte[] call() throws IOException {
         return server.get(location.getRegionInfo().getRegionName(), row, column);
       }
     });
   }
  /**
   * Get the specified number of versions of the specified row and column
   *
   * @param row         - row key
   * @param column      - column name
   * @param numVersions - number of versions to retrieve
   * @return            - array byte values
   * @throws IOException
   */
  public byte[][] get(final Text row, final Text column, final int numVersions)
  throws IOException {
    checkClosed();
    byte [][] values = null;

    values = getRegionServerWithRetries(new ServerCallable<byte[][]>(row) {
      public byte [][] call() throws IOException {
        return server.get(location.getRegionInfo().getRegionName(), row,
          column, numVersions);
      }
    });

    if (values != null) {
      ArrayList<byte[]> bytes = new ArrayList<byte[]>();
      for (int i = 0 ; i < values.length; i++) {
        bytes.add(values[i]);
      }
      return bytes.toArray(new byte[values.length][]);
    }
    return null;
  }
 
  /**
   * Get the specified number of versions of the specified row and column with
   * the specified timestamp.
   *
   * @param row         - row key
   * @param column      - column name
   * @param timestamp   - timestamp
   * @param numVersions - number of versions to retrieve
   * @return            - array of values that match the above criteria
   * @throws IOException
   */
  public byte[][] get(final Text row, final Text column, final long timestamp,
    final int numVersions)
  throws IOException {
    checkClosed();
    byte [][] values = null;

    values = getRegionServerWithRetries(new ServerCallable<byte[][]>(row) {
      public byte [][] call() throws IOException {
        return server.get(location.getRegionInfo().getRegionName(), row,
          column, timestamp, numVersions);
      }
    });

    if (values != null) {
      ArrayList<byte[]> bytes = new ArrayList<byte[]>();
      for (int i = 0 ; i < values.length; i++) {
        bytes.add(values[i]);
      }
      return bytes.toArray(new byte[values.length][]);
    }
    return null;
  }
   
  /**
   * Get all the data for the specified row at the latest timestamp
   *
   * @param row row key
   * @return Map of columns to values.  Map is empty if row does not exist.
   * @throws IOException
   */
  public SortedMap<Text, byte[]> getRow(Text row) throws IOException {
    return getRow(row, HConstants.LATEST_TIMESTAMP);
  }

  /**
   * Get all the data for the specified row at a specified timestamp
   *
   * @param row row key
   * @param ts timestamp
   * @return Map of columns to values.  Map is empty if row does not exist.
   * @throws IOException
   */
  public SortedMap<Text, byte[]> getRow(final Text row, final long ts)
  throws IOException {
    checkClosed();
    HbaseMapWritable value = null;
        
    value = getRegionServerWithRetries(new ServerCallable<HbaseMapWritable>(row) {
      public HbaseMapWritable call() throws IOException {
        return server.getRow(location.getRegionInfo().getRegionName(), row, ts);
      }
    });
   
    SortedMap<Text, byte[]> results = new TreeMap<Text, byte[]>();
    if (value != null && value.size() != 0) {
      for (Map.Entry<Writable, Writable> e: value.entrySet()) {
        HStoreKey key = (HStoreKey) e.getKey();
        results.put(key.getColumn(),
            ((ImmutableBytesWritable) e.getValue()).get());
      }
    }
    return results;
  }


  /**
   * Get a scanner on the current table starting at the specified row.
   * Return the specified columns.
   *
   * @param columns columns to scan. If column name is a column family, all
   * columns of the specified column family are returned.  Its also possible
   * to pass a regex in the column qualifier. A column qualifier is judged to
   * be a regex if it contains at least one of the following characters:
   * <code>\+|^&*$[]]}{)(</code>.
   * @param startRow starting row in table to scan
   * @return scanner
   * @throws IOException
   */
  public HScannerInterface obtainScanner(Text[] columns, Text startRow)
  throws IOException {
    return obtainScanner(columns, startRow, HConstants.LATEST_TIMESTAMP, null);
  }
 
  /**
   * Get a scanner on the current table starting at the specified row.
   * Return the specified columns.
   *
   * @param columns columns to scan. If column name is a column family, all
   * columns of the specified column family are returned.  Its also possible
   * to pass a regex in the column qualifier. A column qualifier is judged to
   * be a regex if it contains at least one of the following characters:
   * <code>\+|^&*$[]]}{)(</code>.
   * @param startRow starting row in table to scan
   * @param timestamp only return results whose timestamp <= this value
   * @return scanner
   * @throws IOException
   */
  public HScannerInterface obtainScanner(Text[] columns, Text startRow,
      long timestamp)
  throws IOException {
    return obtainScanner(columns, startRow, timestamp, null);
  }
 
  /**
   * Get a scanner on the current table starting at the specified row.
   * Return the specified columns.
   *
   * @param columns columns to scan. If column name is a column family, all
   * columns of the specified column family are returned.  Its also possible
   * to pass a regex in the column qualifier. A column qualifier is judged to
   * be a regex if it contains at least one of the following characters:
   * <code>\+|^&*$[]]}{)(</code>.
   * @param startRow starting row in table to scan
   * @param filter a row filter using row-key regexp and/or column data filter.
   * @return scanner
   * @throws IOException
   */
  public HScannerInterface obtainScanner(Text[] columns, Text startRow,
      RowFilterInterface filter)
  throws IOException {
    return obtainScanner(columns, startRow, HConstants.LATEST_TIMESTAMP, filter);
  }

  /**
   * Get a scanner on the current table starting at the specified row and
   * ending just before <code>stopRow<code>.
   * Return the specified columns.
   *
   * @param columns columns to scan. If column name is a column family, all
   * columns of the specified column family are returned.  Its also possible
   * to pass a regex in the column qualifier. A column qualifier is judged to
   * be a regex if it contains at least one of the following characters:
   * <code>\+|^&*$[]]}{)(</code>.
   * @param startRow starting row in table to scan
   * @param stopRow Row to stop scanning on. Once we hit this row we stop
   * returning values; i.e. we return the row before this one but not the
   * <code>stopRow</code> itself.
   * @return scanner
   * @throws IOException
   */
  public HScannerInterface obtainScanner(final Text[] columns,
      final Text startRow, final Text stopRow)
  throws IOException {
    return obtainScanner(columns, startRow, stopRow,
      HConstants.LATEST_TIMESTAMP);
  }

  /**
   * Get a scanner on the current table starting at the specified row and
   * ending just before <code>stopRow<code>.
   * Return the specified columns.
   *
   * @param columns columns to scan. If column name is a column family, all
   * columns of the specified column family are returned.  Its also possible
   * to pass a regex in the column qualifier. A column qualifier is judged to
   * be a regex if it contains at least one of the following characters:
   * <code>\+|^&*$[]]}{)(</code>.
   * @param startRow starting row in table to scan
   * @param stopRow Row to stop scanning on. Once we hit this row we stop
   * returning values; i.e. we return the row before this one but not the
   * <code>stopRow</code> itself.
   * @param timestamp only return results whose timestamp <= this value
   * @return scanner
   * @throws IOException
   */
  public HScannerInterface obtainScanner(final Text[] columns,
      final Text startRow, final Text stopRow, final long timestamp)
  throws IOException {
    return obtainScanner(columns, startRow, timestamp,
      new WhileMatchRowFilter(new StopRowFilter(stopRow)));
  }
 
  /**
   * Get a scanner on the current table starting at the specified row.
   * Return the specified columns.
   *
   * @param columns columns to scan. If column name is a column family, all
   * columns of the specified column family are returned.  Its also possible
   * to pass a regex in the column qualifier. A column qualifier is judged to
   * be a regex if it contains at least one of the following characters:
   * <code>\+|^&*$[]]}{)(</code>.
   * @param startRow starting row in table to scan
   * @param timestamp only return results whose timestamp <= this value
   * @param filter a row filter using row-key regexp and/or column data filter.
   * @return scanner
   * @throws IOException
   */
  public HScannerInterface obtainScanner(Text[] columns,
      Text startRow, long timestamp, RowFilterInterface filter)
  throws IOException {
    checkClosed();
    return new ClientScanner(columns, startRow, timestamp, filter);
  }

  /**
   * Start an atomic row insertion/update.  No changes are committed until the
   * call to commit() returns. A call to abort() will abandon any updates in
   * progress.
   *
   * <p>
   * Example:
   * <br>
   * <pre><span style="font-family: monospace;">
   * long lockid = table.startUpdate(new Text(article.getName()));
   * for (File articleInfo: article.listFiles(new NonDirectories())) {
   *   String article = null;
   *   try {
   *     DataInputStream in = new DataInputStream(new FileInputStream(articleInfo));
   *     article = in.readUTF();
   *   } catch (IOException e) {
   *     // Input error - abandon update
   *     table.abort(lockid);
   *     throw e;
   *   }
   *   try {
   *     table.put(lockid, columnName(articleInfo.getName()), article.getBytes());
   *   } catch (RuntimeException e) {
   *     // Put failed - abandon update
   *     table.abort(lockid);
   *     throw e;
   *   }
   * }
   * table.commit(lockid);
   * </span></pre>
   *
   *
   * @param row Name of row to start update against.  Note, choose row names
   * with care.  Rows are sorted lexicographically (comparison is done
   * using {@link Text#compareTo(Object)}.  If your keys are numeric,
   * lexicographic sorting means that 46 sorts AFTER 450 (If you want to use
   * numerics for keys, zero-pad).
   * @return Row lock id..
   * @see #commit(long)
   * @see #commit(long, long)
   * @see #abort(long)
   */
  public synchronized long startUpdate(final Text row) {
    checkClosed();
    updateInProgress(false);
    batch.set(new BatchUpdate(rand.nextLong()));
    return batch.get().startUpdate(row);
  }
 
  /**
   * Update a value for the specified column.
   * Runs {@link #abort(long)} if exception thrown.
   *
   * @param lockid lock id returned from startUpdate
   * @param column column whose value is being set
   * @param val new value for column.  Cannot be null.
   */
  public void put(long lockid, Text column, byte val[]) {
    checkClosed();
    if (val == null) {
      throw new IllegalArgumentException("value cannot be null");
    }
    updateInProgress(true);
    batch.get().put(lockid, column, val);
  }
 
  /**
   * Update a value for the specified column.
   * Runs {@link #abort(long)} if exception thrown.
   *
   * @param lockid lock id returned from startUpdate
   * @param column column whose value is being set
   * @param val new value for column.  Cannot be null.
   * @throws IOException throws this if the writable can't be
   * converted into a byte array
   */
  public void put(long lockid, Text column, Writable val) throws IOException {   
    put(lockid, column, Writables.getBytes(val));
  }
 
  /**
   * Delete the value for a column.
   * Deletes the cell whose row/column/commit-timestamp match those of the
   * delete.
   * @param lockid lock id returned from startUpdate
   * @param column name of column whose value is to be deleted
   */
  public void delete(long lockid, Text column) {
    checkClosed();
    updateInProgress(true);
    batch.get().delete(lockid, column);
  }
 
  /**
   * Delete all cells that match the passed row and column.
   * @param row Row to update
   * @param column name of column whose value is to be deleted
   * @throws IOException
   */
  public void deleteAll(final Text row, final Text column) throws IOException {
    deleteAll(row, column, LATEST_TIMESTAMP);
  }
 
  /**
   * Delete all cells that match the passed row and column and whose
   * timestamp is equal-to or older than the passed timestamp.
   * @param row Row to update
   * @param column name of column whose value is to be deleted
   * @param ts Delete all cells of the same timestamp or older.
   * @throws IOException
   */
  public void deleteAll(final Text row, final Text column, final long ts)
  throws IOException {
    checkClosed();
         
    getRegionServerWithRetries(new ServerCallable<Boolean>(row) {
      public Boolean call() throws IOException {
        server.deleteAll(location.getRegionInfo().getRegionName(), row,
          column, ts);
        return null;
      }
    });
  }
 
  /**
   * Completely delete the row's cells of the same timestamp or older.
   *
   * @param row Key of the row you want to completely delete.
   * @param ts Timestamp of cells to delete
   * @throws IOException
   */
  public void deleteAll(final Text row, final long ts) throws IOException {
    checkClosed();
   
    getRegionServerWithRetries(new ServerCallable<Boolean>(row){
      public Boolean call() throws IOException {
        server.deleteAll(location.getRegionInfo().getRegionName(), row, ts);
        return null;
      }
    });
  }
     
  /**
   * Completely delete the row's cells.
   *
   * @param row Key of the row you want to completely delete.
   * @throws IOException
   */
  public void deleteAll(final Text row) throws IOException {
    deleteAll(row, HConstants.LATEST_TIMESTAMP);
  }
 
  /**
   * Delete all cells for a row with matching column family with timestamps
   * less than or equal to <i>timestamp</i>.
   *
   * @param row The row to operate on
   * @param family The column family to match
   * @param timestamp Timestamp to match
   * @throws IOException
   */
  public void deleteFamily(final Text row, final Text family,
    final long timestamp)
  throws IOException {
    checkClosed();
   
    getRegionServerWithRetries(new ServerCallable<Boolean>(row){
      public Boolean call() throws IOException {
        server.deleteFamily(location.getRegionInfo().getRegionName(), row,
          family, timestamp);
        return null;
      }
    });
  }

  /**
   * Delete all cells for a row with matching column family at all timestamps.
   *
   * @param row The row to operate on
   * @param family The column family to match
   * @throws IOException
   */ 
  public void deleteFamily(final Text row, final Text family) throws IOException{
    deleteFamily(row, family, HConstants.LATEST_TIMESTAMP);
  }
 
  /**
   * Abort a row mutation.
   *
   * This method should be called only when an update has been started and it
   * is determined that the update should not be committed.
   *
   * Releases resources being held by the update in progress.
   *
   * @param lockid lock id returned from startUpdate
   */
  public synchronized void abort(long lockid) {
    checkClosed();
    if (batch.get() != null && batch.get().getLockid() != lockid) {
      throw new IllegalArgumentException("invalid lock id " + lockid);
    }
    batch.set(null);
  }
 
  /**
   * Finalize a row mutation.
   *
   * When this method is specified, we pass the server a value that says use
   * the 'latest' timestamp.  If we are doing a put, on the server-side, cells
   * will be given the servers's current timestamp.  If the we are commiting
   * deletes, then delete removes the most recently modified cell of stipulated
   * column.
   *
   * @see #commit(long, long)
   *
   * @param lockid lock id returned from startUpdate
   * @throws IOException
   */
  public void commit(long lockid) throws IOException {
    commit(lockid, LATEST_TIMESTAMP);
  }

  /**
   * Finalize a row mutation and release any resources associated with the update.
   *
   * @param lockid lock id returned from startUpdate
   * @param timestamp time to associate with the change
   * @throws IOException
   */
  public synchronized void commit(long lockid, final long timestamp)
  throws IOException {
    checkClosed();
    updateInProgress(true);
    if (batch.get().getLockid() != lockid) {
      throw new IllegalArgumentException("invalid lock id " + lockid);
    }
   
    try {
      getRegionServerWithRetries(
        new ServerCallable<Boolean>(batch.get().getRow()){
          public Boolean call() throws IOException {
            server.batchUpdate(location.getRegionInfo().getRegionName(),
              timestamp, batch.get());
            return null;
          }
        }
      );
    } finally {
      batch.set(null);
    }
  }
 
  /**
   * Implements the scanner interface for the HBase client.
   * If there are multiple regions in a table, this scanner will iterate
   * through them all.
   */
  protected class ClientScanner implements HScannerInterface {
    private final Text EMPTY_COLUMN = new Text();
    private Text[] columns;
    private Text startRow;
    private long scanTime;
    @SuppressWarnings("hiding")
    private boolean closed;
    private HRegionLocation currentRegionLocation;
    private HRegionInterface server;
    private long scannerId;
    private RowFilterInterface filter;
   
    protected ClientScanner(Text[] columns, Text startRow, long timestamp,
      RowFilterInterface filter)
    throws IOException {

      LOG.info("Creating scanner over " + tableName + " starting at key " + startRow);

      // defaults
      this.closed = false;
      this.server = null;
      this.scannerId = -1L;
   
      // save off the simple parameters
      this.columns = columns;
      this.startRow = startRow;
      this.scanTime = timestamp;
     
      // save the filter, and make sure that the filter applies to the data
      // we're expecting to pull back
      this.filter = filter;
      if (filter != null) {
        filter.validate(columns);
      }

      nextScanner();
    }
       
    /*
     * Gets a scanner for the next region.
     * Returns false if there are no more scanners.
     */
    private boolean nextScanner() throws IOException {
      checkClosed();
     
      // close the previous scanner if it's open
      if (this.scannerId != -1L) {
        this.server.close(this.scannerId);
        this.scannerId = -1L;
      }

      // if we're at the end of the table, then close and return false
      // to stop iterating
      if (this.currentRegionLocation != null){
        LOG.debug("Advancing forward from region "
          + this.currentRegionLocation.getRegionInfo());
        Text endKey =  this.currentRegionLocation.getRegionInfo().getEndKey();
        if (endKey == null || endKey.equals(EMPTY_TEXT) || filterSaysStop(endKey)) {
            close();
            return false;
        }
      }
     
      HRegionLocation oldLocation = this.currentRegionLocation;
     
      Text localStartKey = oldLocation == null ?
        startRow : oldLocation.getRegionInfo().getEndKey();

      // advance to the region that starts with the current region's end key
      LOG.debug("Advancing internal scanner to startKey '" + localStartKey + "'");
      this.currentRegionLocation = getRegionLocation(localStartKey);
     
      LOG.debug("New region: " + this.currentRegionLocation);
     
      try {
        for (int tries = 0; tries < numRetries; tries++) {
          // connect to the server
          server = connection.getHRegionConnection(
            this.currentRegionLocation.getServerAddress());
         
          try {
            // open a scanner on the region server starting at the
            // beginning of the region
            scannerId = server.openScanner(
              this.currentRegionLocation.getRegionInfo().getRegionName(),
              this.columns, localStartKey, scanTime, filter);
             
            break;
          } catch (IOException e) {
            if (e instanceof RemoteException) {
              e = RemoteExceptionHandler.decodeRemoteException(
                  (RemoteException) e);
            }
            if (tries == numRetries - 1) {
              // No more tries
              throw e;
            }
            try {
              Thread.sleep(pause);
            } catch (InterruptedException ie) {
              // continue
            }
            if (LOG.isDebugEnabled()) {
              LOG.debug("reloading table servers because: " + e.getMessage());
            }
            currentRegionLocation = getRegionLocation(localStartKey, true);
          }
        }
      } catch (IOException e) {
        close();
        if (e instanceof RemoteException) {
          e = RemoteExceptionHandler.decodeRemoteException((RemoteException) e);
        }
        throw e;
      }
      return true;
    }

    /**
     * @param endKey
     * @return Returns true if the passed region endkey is judged beyond
     * filter.
     */
    private boolean filterSaysStop(final Text endKey) {
      if (this.filter == null) {
        return false;
      }
      // Let the filter see current row.
      this.filter.filter(endKey);
      return this.filter.filterAllRemaining();
    }

    /** {@inheritDoc} */
    public boolean next(HStoreKey key, SortedMap<Text, byte[]> results)
    throws IOException {
      checkClosed();
      if (this.closed) {
        return false;
      }
      HbaseMapWritable values = null;
      // Clear the results so we don't inherit any values from any previous
      // calls to next.
      results.clear();
      do {
        values = server.next(scannerId);
      } while ((values == null || values.size() == 0) && nextScanner());

      if (values != null && values.size() != 0) {
        for (Map.Entry<Writable, Writable> e: values.entrySet()) {
          HStoreKey k = (HStoreKey) e.getKey();
          key.setRow(k.getRow());
          key.setVersion(k.getTimestamp());
          key.setColumn(EMPTY_COLUMN);
          results.put(k.getColumn(),
              ((ImmutableBytesWritable) e.getValue()).get());
        }
      }
      return values == null ? false : values.size() != 0;
    }

    /**
     * {@inheritDoc}
     */
    public void close() throws IOException {
      checkClosed();
      if (scannerId != -1L) {
        try {
          server.close(scannerId);
         
        } catch (IOException e) {
          if (e instanceof RemoteException) {
            e = RemoteExceptionHandler.decodeRemoteException((RemoteException) e);
          }
          if (!(e instanceof NotServingRegionException)) {
            throw e;
          }
        }
        scannerId = -1L;
      }
      server = null;
      closed = true;
    }

    /** {@inheritDoc} */
    public Iterator<Entry<HStoreKey, SortedMap<Text, byte[]>>> iterator() {
      return new Iterator<Entry<HStoreKey, SortedMap<Text, byte[]>>>() {
        HStoreKey key = null;
        SortedMap<Text, byte []> value = null;
       
        public boolean hasNext() {
          boolean hasNext = false;
          try {
            this.key = new HStoreKey();
            this.value = new TreeMap<Text, byte[]>();
            hasNext = ClientScanner.this.next(key, value);
          } catch (IOException e) {
            throw new RuntimeException(e);
          }
          return hasNext;
        }

        public Entry<HStoreKey, SortedMap<Text, byte[]>> next() {
          return new Map.Entry<HStoreKey, SortedMap<Text, byte[]>>() {
            public HStoreKey getKey() {
              return key;
            }

            public SortedMap<Text, byte[]> getValue() {
              return value;
            }

            public SortedMap<Text, byte[]> setValue(@SuppressWarnings("unused")
            SortedMap<Text, byte[]> value) {
              throw new UnsupportedOperationException();
            }
          };
        }

        public void remove() {
          throw new UnsupportedOperationException();
        }
      };
    }
  }
 
  /**
   * Inherits from Callable, used to define the particular actions you would
   * like to take with retry logic.
   */
  protected abstract class ServerCallable<T> implements Callable<T> {
    HRegionLocation location;
    HRegionInterface server;
    Text row;
 
    protected ServerCallable(Text row) {
      this.row = row;
    }
 
    void instantiateServer(boolean reload) throws IOException {
      this.location = getRegionLocation(row, reload);
      this.server = connection.getHRegionConnection(location.getServerAddress());
    }   
  }
 
  /**
   * Pass in a ServerCallable with your particular bit of logic defined and
   * this method will manage the process of doing retries with timed waits
   * and refinds of missing regions.
   */
  protected <T> T getRegionServerWithRetries(ServerCallable<T> callable)
  throws IOException, RuntimeException {
    List<IOException> exceptions = new ArrayList<IOException>();
    for(int tries = 0; tries < numRetries; tries++) {
      try {
        callable.instantiateServer(tries != 0);
        return callable.call();
      } catch (IOException e) {
        if (e instanceof RemoteException) {
          e = RemoteExceptionHandler.decodeRemoteException((RemoteException) e);
        }
        if (tries == numRetries - 1) {
          if (LOG.isDebugEnabled()) {
            String message = "Trying to contact region server for row '" +
              callable.row + "', but failed after " + (tries + 1+
              " attempts.\n";
            int i = 1;
            for (IOException e2 : exceptions) {
              message = message + "Exception " + i + ":\n" + e2;
            }
            LOG.debug(message);
          }
          throw e;
        }
        if (LOG.isDebugEnabled()) {
          exceptions.add(e);
          LOG.debug("reloading table servers because: " + e.getMessage());
        }

      } catch (Exception e) {
        throw new RuntimeException(e);
      }
      try {
        Thread.sleep(pause);
      } catch (InterruptedException e) {
        // continue
      }
    }
    return null;   
  }
}
TOP

Related Classes of org.apache.hadoop.hbase.HTable$ClientScanner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.