You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by st...@apache.org on 2011/07/07 23:58:35 UTC

svn commit: r1144068 - in /hbase/trunk: CHANGES.txt src/main/java/org/apache/hadoop/hbase/client/HTablePool.java src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java

Author: stack
Date: Thu Jul  7 21:58:35 2011
New Revision: 1144068

URL: http://svn.apache.org/viewvc?rev=1144068&view=rev
Log:
HBASE-4054 Usability improvement to HTablePool

Modified:
    hbase/trunk/CHANGES.txt
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java

Modified: hbase/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/hbase/trunk/CHANGES.txt?rev=1144068&r1=1144067&r2=1144068&view=diff
==============================================================================
--- hbase/trunk/CHANGES.txt (original)
+++ hbase/trunk/CHANGES.txt Thu Jul  7 21:58:35 2011
@@ -311,6 +311,7 @@ Release 0.91.0 - Unreleased
    HBASE-4048  [Coprocessors] Support configuration of coprocessor at load time
    HBASE-3240  Improve documentation of importtsv and bulk loads.
                (Aaron T. Myers via todd)
+   HBASE-4054  Usability improvement to HTablePool (Daniel Iancu)
 
   TASKS
    HBASE-3559  Move report of split to master OFF the heartbeat channel

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java?rev=1144068&r1=1144067&r2=1144068&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java Thu Jul  7 21:58:35 2011
@@ -22,25 +22,39 @@ package org.apache.hadoop.hbase.client;
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.client.coprocessor.Batch;
+import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.PoolMap;
 import org.apache.hadoop.hbase.util.PoolMap.PoolType;
 
 /**
- * A simple pool of HTable instances.<p>
- *
- * Each HTablePool acts as a pool for all tables.  To use, instantiate an
+ * A simple pool of HTable instances.
+ * 
+ * Each HTablePool acts as a pool for all tables. To use, instantiate an
  * HTablePool and use {@link #getTable(String)} to get an HTable from the pool.
- * Once you are done with it, return it to the pool with {@link #putTable(HTableInterface)}.
+ *
+   * This method is not needed anymore, clients should call
+   * HTableInterface.close() rather than returning the tables to the pool
+   *
+ * Once you are done with it, close your instance of {@link HTableInterface}
+ * by calling {@link HTableInterface#close()} rather than returning the tables
+ * to the pool with (deprecated) {@link #putTable(HTableInterface)}.
  * 
- * <p>A pool can be created with a <i>maxSize</i> which defines the most HTable
- * references that will ever be retained for each table.  Otherwise the default
+ * <p>
+ * A pool can be created with a <i>maxSize</i> which defines the most HTable
+ * references that will ever be retained for each table. Otherwise the default
  * is {@link Integer#MAX_VALUE}.
- *
- * <p>Pool will manage its own cluster to the cluster. See {@link HConnectionManager}.
+ * 
+ * <p>
+ * Pool will manage its own connections to the cluster. See
+ * {@link HConnectionManager}.
  */
 public class HTablePool implements Closeable {
   private final PoolMap<String, HTableInterface> tables;
@@ -50,7 +64,7 @@ public class HTablePool implements Close
   private final HTableInterfaceFactory tableFactory;
 
   /**
-   * Default Constructor.  Default HBaseConfiguration and no limit on pool size.
+   * Default Constructor. Default HBaseConfiguration and no limit on pool size.
    */
   public HTablePool() {
     this(HBaseConfiguration.create(), Integer.MAX_VALUE);
@@ -58,8 +72,11 @@ public class HTablePool implements Close
 
   /**
    * Constructor to set maximum versions and use the specified configuration.
-   * @param config configuration
-   * @param maxSize maximum number of references to keep for each table
+   * 
+   * @param config
+   *          configuration
+   * @param maxSize
+   *          maximum number of references to keep for each table
    */
   public HTablePool(final Configuration config, final int maxSize) {
     this(config, maxSize, null, null);
@@ -68,7 +85,7 @@ public class HTablePool implements Close
   /**
    * Constructor to set maximum versions and use the specified configuration and
    * table factory.
-   *
+   * 
    * @param config
    *          configuration
    * @param maxSize
@@ -84,7 +101,7 @@ public class HTablePool implements Close
   /**
    * Constructor to set maximum versions and use the specified configuration and
    * pool type.
-   *
+   * 
    * @param config
    *          configuration
    * @param maxSize
@@ -106,7 +123,7 @@ public class HTablePool implements Close
    * {@link PoolType#Reusable} and {@link PoolType#ThreadLocal}. If the pool
    * type is null or not one of those two values, then it will default to
    * {@link PoolType#Reusable}.
-   *
+   * 
    * @param config
    *          configuration
    * @param maxSize
@@ -121,9 +138,10 @@ public class HTablePool implements Close
       final HTableInterfaceFactory tableFactory, PoolType poolType) {
     // Make a new configuration instance so I can safely cleanup when
     // done with the pool.
-    this.config = config == null? new Configuration(): config;
+    this.config = config == null ? new Configuration() : config;
     this.maxSize = maxSize;
-    this.tableFactory = tableFactory == null? new HTableFactory(): tableFactory;
+    this.tableFactory = tableFactory == null ? new HTableFactory()
+        : tableFactory;
     if (poolType == null) {
       this.poolType = PoolType.Reusable;
     } else {
@@ -137,47 +155,106 @@ public class HTablePool implements Close
         break;
       }
     }
-    this.tables = new PoolMap<String, HTableInterface>(this.poolType, this.maxSize);
+    this.tables = new PoolMap<String, HTableInterface>(this.poolType,
+        this.maxSize);
   }
 
   /**
-   * Get a reference to the specified table from the pool.<p>
-   *
-   * Create a new one if one is not available.
-   * @param tableName table name
+   * Get a reference to the specified table from the pool.
+   * <p>
+   * <p/>
+   * 
+   * @param tableName
+   *          table name
    * @return a reference to the specified table
-   * @throws RuntimeException if there is a problem instantiating the HTable
+   * @throws RuntimeException
+   *           if there is a problem instantiating the HTable
    */
   public HTableInterface getTable(String tableName) {
+    // call the old getTable implementation renamed to findOrCreateTable
+    HTableInterface table = findOrCreateTable(tableName);
+    // return a proxy table so when user closes the proxy, the actual table
+    // will be returned to the pool
+    return new PooledHTable(table);
+
+  }
+
+  /**
+   * Get a reference to the specified table from the pool.
+   * <p>
+   * 
+   * Create a new one if one is not available.
+   * 
+   * @param tableName
+   *          table name
+   * @return a reference to the specified table
+   * @throws RuntimeException
+   *           if there is a problem instantiating the HTable
+   */
+  private HTableInterface findOrCreateTable(String tableName) {
     HTableInterface table = tables.get(tableName);
-    if(table == null) {
+    if (table == null) {
       table = createHTable(tableName);
     }
     return table;
   }
 
   /**
-   * Get a reference to the specified table from the pool.<p>
-   *
+   * Get a reference to the specified table from the pool.
+   * <p>
+   * 
    * Create a new one if one is not available.
-   * @param tableName table name
+   * 
+   * @param tableName
+   *          table name
    * @return a reference to the specified table
-   * @throws RuntimeException if there is a problem instantiating the HTable
+   * @throws RuntimeException
+   *           if there is a problem instantiating the HTable
    */
-  public HTableInterface getTable(byte [] tableName) {
+  public HTableInterface getTable(byte[] tableName) {
     return getTable(Bytes.toString(tableName));
   }
 
   /**
-   * Puts the specified HTable back into the pool.<p>
-   *
-   * If the pool already contains <i>maxSize</i> references to the table,
-   * then the table instance gets closed after flushing buffered edits.
-   * @param table table
+   * This method is not needed anymore, clients should call
+   * HTableInterface.close() rather than returning the tables to the pool
+   * 
+   * @param table
+   *          the proxy table user got from pool
+   * @deprecated
    */
   public void putTable(HTableInterface table) throws IOException {
+    // we need to be sure nobody puts a proxy implementation in the pool
+    // but if the client code is not updated
+    // and it will continue to call putTable() instead of calling close()
+    // then we need to return the wrapped table to the pool instead of the
+    // proxy
+    // table
+    if (table instanceof PooledHTable) {
+      returnTable(((PooledHTable) table).getWrappedTable());
+    } else {
+      // normally this should not happen if clients pass back the same
+      // table
+      // object they got from the pool
+      // but if it happens then it's better to reject it
+      throw new IllegalArgumentException("not a pooled table: " + table);
+    }
+  }
+
+  /**
+   * Puts the specified HTable back into the pool.
+   * <p>
+   * 
+   * If the pool already contains <i>maxSize</i> references to the table, then
+   * the table instance gets closed after flushing buffered edits.
+   * 
+   * @param table
+   *          table
+   */
+  private void returnTable(HTableInterface table) throws IOException {
+    // this is the old putTable method renamed and made private
     String tableName = Bytes.toString(table.getTableName());
-    if(tables.size(tableName) >= maxSize) {
+    if (tables.size(tableName) >= maxSize) {
       // release table instance since we're not reusing it
       this.tableFactory.releaseHTableInterface(table);
       return;
@@ -186,16 +263,18 @@ public class HTablePool implements Close
   }
 
   protected HTableInterface createHTable(String tableName) {
-    return this.tableFactory.createHTableInterface(config, Bytes.toBytes(tableName));
+    return this.tableFactory.createHTableInterface(config,
+        Bytes.toBytes(tableName));
   }
 
   /**
-   * Closes all the HTable instances , belonging to the given table, in the table pool.
+   * Closes all the HTable instances , belonging to the given table, in the
+   * table pool.
    * <p>
    * Note: this is a 'shutdown' of the given table pool and different from
    * {@link #putTable(HTableInterface)}, that is used to return the table
    * instance to the pool for future re-use.
-   *
+   * 
    * @param tableName
    */
   public void closeTablePool(final String tableName) throws IOException {
@@ -210,7 +289,7 @@ public class HTablePool implements Close
 
   /**
    * See {@link #closeTablePool(String)}.
-   *
+   * 
    * @param tableName
    */
   public void closeTablePool(final byte[] tableName) throws IOException {
@@ -218,7 +297,8 @@ public class HTablePool implements Close
   }
 
   /**
-   * Closes all the HTable instances , belonging to all tables in the table pool.
+   * Closes all the HTable instances , belonging to all tables in the table
+   * pool.
    * <p>
    * Note: this is a 'shutdown' of all the table pools.
    */
@@ -231,4 +311,195 @@ public class HTablePool implements Close
   int getCurrentPoolSize(String tableName) {
     return tables.size(tableName);
   }
+
+  /**
+   * A proxy class that implements HTableInterface.close method to return the
+   * wrapped table back to the table pool
+   * 
+   */
+  class PooledHTable implements HTableInterface {
+
+    private HTableInterface table; // actual table implementation
+
+    public PooledHTable(HTableInterface table) {
+      this.table = table;
+    }
+
+    @Override
+    public byte[] getTableName() {
+      return table.getTableName();
+    }
+
+    @Override
+    public Configuration getConfiguration() {
+      return table.getConfiguration();
+    }
+
+    @Override
+    public HTableDescriptor getTableDescriptor() throws IOException {
+      return table.getTableDescriptor();
+    }
+
+    @Override
+    public boolean exists(Get get) throws IOException {
+      return table.exists(get);
+    }
+
+    @Override
+    public void batch(List<Row> actions, Object[] results) throws IOException,
+        InterruptedException {
+      table.batch(actions, results);
+    }
+
+    @Override
+    public Object[] batch(List<Row> actions) throws IOException,
+        InterruptedException {
+      return table.batch(actions);
+    }
+
+    @Override
+    public Result get(Get get) throws IOException {
+      return table.get(get);
+    }
+
+    @Override
+    public Result[] get(List<Get> gets) throws IOException {
+      return table.get(gets);
+    }
+
+    @Override
+    public Result getRowOrBefore(byte[] row, byte[] family) throws IOException {
+      return table.getRowOrBefore(row, family);
+    }
+
+    @Override
+    public ResultScanner getScanner(Scan scan) throws IOException {
+      return table.getScanner(scan);
+    }
+
+    @Override
+    public ResultScanner getScanner(byte[] family) throws IOException {
+      return table.getScanner(family);
+    }
+
+    @Override
+    public ResultScanner getScanner(byte[] family, byte[] qualifier)
+        throws IOException {
+      return table.getScanner(family, qualifier);
+    }
+
+    @Override
+    public void put(Put put) throws IOException {
+      table.put(put);
+    }
+
+    @Override
+    public void put(List<Put> puts) throws IOException {
+      table.put(puts);
+    }
+
+    @Override
+    public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,
+        byte[] value, Put put) throws IOException {
+      return table.checkAndPut(row, family, qualifier, value, put);
+    }
+
+    @Override
+    public void delete(Delete delete) throws IOException {
+      table.delete(delete);
+    }
+
+    @Override
+    public void delete(List<Delete> deletes) throws IOException {
+      table.delete(deletes);
+    }
+
+    @Override
+    public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
+        byte[] value, Delete delete) throws IOException {
+      return table.checkAndDelete(row, family, qualifier, value, delete);
+    }
+
+    @Override
+    public Result increment(Increment increment) throws IOException {
+      return table.increment(increment);
+    }
+
+    @Override
+    public long incrementColumnValue(byte[] row, byte[] family,
+        byte[] qualifier, long amount) throws IOException {
+      return table.incrementColumnValue(row, family, qualifier, amount);
+    }
+
+    @Override
+    public long incrementColumnValue(byte[] row, byte[] family,
+        byte[] qualifier, long amount, boolean writeToWAL) throws IOException {
+      return table.incrementColumnValue(row, family, qualifier, amount,
+          writeToWAL);
+    }
+
+    @Override
+    public boolean isAutoFlush() {
+      return table.isAutoFlush();
+    }
+
+    @Override
+    public void flushCommits() throws IOException {
+      table.flushCommits();
+    }
+
+    /**
+     * Returns the actual table back to the pool
+     * 
+     * @throws IOException
+     */
+    public void close() throws IOException {
+      returnTable(table);
+    }
+
+    @Override
+    public RowLock lockRow(byte[] row) throws IOException {
+      return table.lockRow(row);
+    }
+
+    @Override
+    public void unlockRow(RowLock rl) throws IOException {
+      table.unlockRow(rl);
+    }
+
+    @Override
+    public <T extends CoprocessorProtocol> T coprocessorProxy(
+        Class<T> protocol, byte[] row) {
+      return table.coprocessorProxy(protocol, row);
+    }
+
+    @Override
+    public <T extends CoprocessorProtocol, R> Map<byte[], R> coprocessorExec(
+        Class<T> protocol, byte[] startKey, byte[] endKey,
+        Batch.Call<T, R> callable) throws IOException, Throwable {
+      return table.coprocessorExec(protocol, startKey, endKey, callable);
+    }
+
+    @Override
+    public <T extends CoprocessorProtocol, R> void coprocessorExec(
+        Class<T> protocol, byte[] startKey, byte[] endKey,
+        Batch.Call<T, R> callable, Batch.Callback<R> callback)
+        throws IOException, Throwable {
+      table.coprocessorExec(protocol, startKey, endKey, callable, callback);
+    }
+
+    @Override
+    public String toString() {
+      return "PooledHTable{" + ", table=" + table + '}';
+    }
+
+    /**
+     * Expose the wrapped HTable to tests in the same package
+     * 
+     * @return wrapped htable
+     */
+    HTableInterface getWrappedTable() {
+      return table;
+    }
+  }
 }

Modified: hbase/trunk/src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java?rev=1144068&r1=1144067&r2=1144068&view=diff
==============================================================================
--- hbase/trunk/src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java (original)
+++ hbase/trunk/src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java Thu Jul  7 21:58:35 2011
@@ -37,221 +37,300 @@ import org.junit.Test;
  * Tests HTablePool.
  */
 public class TestHTablePool {
-  private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
-  private final static byte[] TABLENAME = Bytes.toBytes("TestHTablePool");
+	private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+	private final static byte[] TABLENAME = Bytes.toBytes("TestHTablePool");
 
-  public abstract static class TestHTablePoolType extends TestCase {
-    protected void setUp() throws Exception {
-      TEST_UTIL.startMiniCluster(1);
-      TEST_UTIL.createTable(TABLENAME, HConstants.CATALOG_FAMILY);
-    }
-
-    protected void tearDown() throws IOException {
-      TEST_UTIL.shutdownMiniCluster();
-    }
-
-    protected abstract PoolType getPoolType();
-
-    @Test
-    public void testTableWithStringName() throws Exception {
-      HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
-          Integer.MAX_VALUE, getPoolType());
-      String tableName = Bytes.toString(TABLENAME);
-
-      // Request a table from an empty pool
-      HTableInterface table = pool.getTable(tableName);
-      Assert.assertNotNull(table);
-
-      // Return the table to the pool
-      pool.putTable(table);
-
-      // Request a table of the same name
-      HTableInterface sameTable = pool.getTable(tableName);
-      Assert.assertSame(table, sameTable);
-    }
-
-    @Test
-    public void testTableWithByteArrayName() throws IOException {
-      HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
-          Integer.MAX_VALUE, getPoolType());
-
-      // Request a table from an empty pool
-      HTableInterface table = pool.getTable(TABLENAME);
-      Assert.assertNotNull(table);
-
-      // Return the table to the pool
-      pool.putTable(table);
-
-      // Request a table of the same name
-      HTableInterface sameTable = pool.getTable(TABLENAME);
-      Assert.assertSame(table, sameTable);
-    }
-
-    @Test
-    public void testTablesWithDifferentNames() throws IOException {
-      HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
-          Integer.MAX_VALUE, getPoolType());
-      byte[] otherTable = Bytes.toBytes("OtherTable");
-      TEST_UTIL.createTable(otherTable, HConstants.CATALOG_FAMILY);
-
-      // Request a table from an empty pool
-      HTableInterface table1 = pool.getTable(TABLENAME);
-      HTableInterface table2 = pool.getTable(otherTable);
-      Assert.assertNotNull(table2);
-
-      // Return the tables to the pool
-      pool.putTable(table1);
-      pool.putTable(table2);
-
-      // Request tables of the same names
-      HTableInterface sameTable1 = pool.getTable(TABLENAME);
-      HTableInterface sameTable2 = pool.getTable(otherTable);
-      Assert.assertSame(table1, sameTable1);
-      Assert.assertSame(table2, sameTable2);
-    }
-
-  }
-
-  public static class TestHTableReusablePool extends TestHTablePoolType {
-    @Override
-    protected PoolType getPoolType() {
-      return PoolType.Reusable;
-    }
-
-    @Test
-    public void testTableWithMaxSize() throws Exception {
-      HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
-          getPoolType());
-
-      // Request tables from an empty pool
-      HTableInterface table1 = pool.getTable(TABLENAME);
-      HTableInterface table2 = pool.getTable(TABLENAME);
-      HTableInterface table3 = pool.getTable(TABLENAME);
-
-      // Return the tables to the pool
-      pool.putTable(table1);
-      pool.putTable(table2);
-      // The pool should reject this one since it is already full
-      pool.putTable(table3);
-
-      // Request tables of the same name
-      HTableInterface sameTable1 = pool.getTable(TABLENAME);
-      HTableInterface sameTable2 = pool.getTable(TABLENAME);
-      HTableInterface sameTable3 = pool.getTable(TABLENAME);
-      Assert.assertSame(table1, sameTable1);
-      Assert.assertSame(table2, sameTable2);
-      Assert.assertNotSame(table3, sameTable3);
-    }
-
-    @Test
-    public void testCloseTablePool() throws IOException {
-      HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
-          getPoolType());
-      HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
-
-      if (admin.tableExists(TABLENAME)) {
-        admin.disableTable(TABLENAME);
-        admin.deleteTable(TABLENAME);
-      }
-
-      HTableDescriptor tableDescriptor = new HTableDescriptor(TABLENAME);
-      tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
-      admin.createTable(tableDescriptor);
-
-      // Request tables from an empty pool
-      HTableInterface[] tables = new HTableInterface[4];
-      for (int i = 0; i < 4; ++i) {
-        tables[i] = pool.getTable(TABLENAME);
-      }
-
-      pool.closeTablePool(TABLENAME);
-
-      for (int i = 0; i < 4; ++i) {
-        pool.putTable(tables[i]);
-      }
-
-      Assert
-          .assertEquals(4, pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
-
-      pool.closeTablePool(TABLENAME);
-
-      Assert
-          .assertEquals(0, pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
-    }
-  }
-
-  public static class TestHTableThreadLocalPool extends TestHTablePoolType {
-    @Override
-    protected PoolType getPoolType() {
-      return PoolType.ThreadLocal;
-    }
-
-    @Test
-    public void testTableWithMaxSize() throws Exception {
-      HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
-          getPoolType());
-
-      // Request tables from an empty pool
-      HTableInterface table1 = pool.getTable(TABLENAME);
-      HTableInterface table2 = pool.getTable(TABLENAME);
-      HTableInterface table3 = pool.getTable(TABLENAME);
-
-      // Return the tables to the pool
-      pool.putTable(table1);
-      pool.putTable(table2);
-      // The pool should not reject this one since the number of threads <= 2
-      pool.putTable(table3);
-
-      // Request tables of the same name
-      HTableInterface sameTable1 = pool.getTable(TABLENAME);
-      HTableInterface sameTable2 = pool.getTable(TABLENAME);
-      HTableInterface sameTable3 = pool.getTable(TABLENAME);
-      Assert.assertSame(table3, sameTable1);
-      Assert.assertSame(table3, sameTable2);
-      Assert.assertSame(table3, sameTable3);
-    }
-
-    @Test
-    public void testCloseTablePool() throws IOException {
-      HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
-          getPoolType());
-      HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
-
-      if (admin.tableExists(TABLENAME)) {
-        admin.disableTable(TABLENAME);
-        admin.deleteTable(TABLENAME);
-      }
-
-      HTableDescriptor tableDescriptor = new HTableDescriptor(TABLENAME);
-      tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
-      admin.createTable(tableDescriptor);
-
-      // Request tables from an empty pool
-      HTableInterface[] tables = new HTableInterface[4];
-      for (int i = 0; i < 4; ++i) {
-        tables[i] = pool.getTable(TABLENAME);
-      }
-
-      pool.closeTablePool(TABLENAME);
-
-      for (int i = 0; i < 4; ++i) {
-        pool.putTable(tables[i]);
-      }
-
-      Assert
-          .assertEquals(1, pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
-
-      pool.closeTablePool(TABLENAME);
-
-      Assert
-          .assertEquals(0, pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
-    }
-  }
-
-  public static junit.framework.Test suite() {
-    TestSuite suite = new TestSuite();
-    suite.addTestSuite(TestHTableReusablePool.class);
-    suite.addTestSuite(TestHTableThreadLocalPool.class);
-    return suite;
-  }
+	public abstract static class TestHTablePoolType extends TestCase {
+		protected void setUp() throws Exception {
+			TEST_UTIL.startMiniCluster(1);
+			TEST_UTIL.createTable(TABLENAME, HConstants.CATALOG_FAMILY);
+		}
+
+		protected void tearDown() throws IOException {
+			TEST_UTIL.shutdownMiniCluster();
+		}
+
+		protected abstract PoolType getPoolType();
+
+		@Test
+		public void testTableWithStringName() throws Exception {
+			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+					Integer.MAX_VALUE, getPoolType());
+			String tableName = Bytes.toString(TABLENAME);
+
+			// Request a table from an empty pool
+			HTableInterface table = pool.getTable(tableName);
+			Assert.assertNotNull(table);
+
+			// Close table (returns table to the pool)
+			table.close();
+
+			// Request a table of the same name
+			HTableInterface sameTable = pool.getTable(tableName);
+			Assert.assertSame(
+					((HTablePool.PooledHTable) table).getWrappedTable(),
+					((HTablePool.PooledHTable) sameTable).getWrappedTable());
+		}
+
+		@Test
+		public void testTableWithByteArrayName() throws IOException {
+			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+					Integer.MAX_VALUE, getPoolType());
+
+			// Request a table from an empty pool
+			HTableInterface table = pool.getTable(TABLENAME);
+			Assert.assertNotNull(table);
+
+			// Close table (returns table to the pool)
+			table.close();
+
+			// Request a table of the same name
+			HTableInterface sameTable = pool.getTable(TABLENAME);
+			Assert.assertSame(
+					((HTablePool.PooledHTable) table).getWrappedTable(),
+					((HTablePool.PooledHTable) sameTable).getWrappedTable());
+		}
+
+		@Test
+		public void testTablesWithDifferentNames() throws IOException {
+			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+					Integer.MAX_VALUE, getPoolType());
+			byte[] otherTable = Bytes.toBytes("OtherTable");
+			TEST_UTIL.createTable(otherTable, HConstants.CATALOG_FAMILY);
+
+			// Request a table from an empty pool
+			HTableInterface table1 = pool.getTable(TABLENAME);
+			HTableInterface table2 = pool.getTable(otherTable);
+			Assert.assertNotNull(table2);
+
+			// Close tables (returns tables to the pool)
+			table1.close();
+			table2.close();
+
+			// Request tables of the same names
+			HTableInterface sameTable1 = pool.getTable(TABLENAME);
+			HTableInterface sameTable2 = pool.getTable(otherTable);
+			Assert.assertSame(
+					((HTablePool.PooledHTable) table1).getWrappedTable(),
+					((HTablePool.PooledHTable) sameTable1).getWrappedTable());
+			Assert.assertSame(
+					((HTablePool.PooledHTable) table2).getWrappedTable(),
+					((HTablePool.PooledHTable) sameTable2).getWrappedTable());
+		}
+
+	}
+
+	public static class TestHTableReusablePool extends TestHTablePoolType {
+		@Override
+		protected PoolType getPoolType() {
+			return PoolType.Reusable;
+		}
+
+		@Test
+		public void testTableWithMaxSize() throws Exception {
+			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
+					getPoolType());
+
+			// Request tables from an empty pool
+			HTableInterface table1 = pool.getTable(TABLENAME);
+			HTableInterface table2 = pool.getTable(TABLENAME);
+			HTableInterface table3 = pool.getTable(TABLENAME);
+
+			// Close tables (returns tables to the pool)
+			table1.close();
+			table2.close();
+			// The pool should reject this one since it is already full
+			table3.close();
+
+			// Request tables of the same name
+			HTableInterface sameTable1 = pool.getTable(TABLENAME);
+			HTableInterface sameTable2 = pool.getTable(TABLENAME);
+			HTableInterface sameTable3 = pool.getTable(TABLENAME);
+			Assert.assertSame(
+					((HTablePool.PooledHTable) table1).getWrappedTable(),
+					((HTablePool.PooledHTable) sameTable1).getWrappedTable());
+			Assert.assertSame(
+					((HTablePool.PooledHTable) table2).getWrappedTable(),
+					((HTablePool.PooledHTable) sameTable2).getWrappedTable());
+			Assert.assertNotSame(
+					((HTablePool.PooledHTable) table3).getWrappedTable(),
+					((HTablePool.PooledHTable) sameTable3).getWrappedTable());
+		}
+
+		@Test
+		public void testCloseTablePool() throws IOException {
+			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
+					getPoolType());
+			HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
+
+			if (admin.tableExists(TABLENAME)) {
+				admin.disableTable(TABLENAME);
+				admin.deleteTable(TABLENAME);
+			}
+
+			HTableDescriptor tableDescriptor = new HTableDescriptor(TABLENAME);
+			tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
+			admin.createTable(tableDescriptor);
+
+			// Request tables from an empty pool
+			HTableInterface[] tables = new HTableInterface[4];
+			for (int i = 0; i < 4; ++i) {
+				tables[i] = pool.getTable(TABLENAME);
+			}
+
+			pool.closeTablePool(TABLENAME);
+
+			for (int i = 0; i < 4; ++i) {
+				tables[i].close();
+			}
+
+			Assert.assertEquals(4,
+					pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
+
+			pool.closeTablePool(TABLENAME);
+
+			Assert.assertEquals(0,
+					pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
+		}
+	}
+
+	public static class TestHTableThreadLocalPool extends TestHTablePoolType {
+		@Override
+		protected PoolType getPoolType() {
+			return PoolType.ThreadLocal;
+		}
+
+		@Test
+		public void testTableWithMaxSize() throws Exception {
+			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
+					getPoolType());
+
+			// Request tables from an empty pool
+			HTableInterface table1 = pool.getTable(TABLENAME);
+			HTableInterface table2 = pool.getTable(TABLENAME);
+			HTableInterface table3 = pool.getTable(TABLENAME);
+
+			// Close tables (returns tables to the pool)
+			table1.close();
+			table2.close();
+			// The pool should not reject this one since the number of threads
+			// <= 2
+			table3.close();
+
+			// Request tables of the same name
+			HTableInterface sameTable1 = pool.getTable(TABLENAME);
+			HTableInterface sameTable2 = pool.getTable(TABLENAME);
+			HTableInterface sameTable3 = pool.getTable(TABLENAME);
+			Assert.assertSame(
+					((HTablePool.PooledHTable) table3).getWrappedTable(),
+					((HTablePool.PooledHTable) sameTable1).getWrappedTable());
+			Assert.assertSame(
+					((HTablePool.PooledHTable) table3).getWrappedTable(),
+					((HTablePool.PooledHTable) sameTable2).getWrappedTable());
+			Assert.assertSame(
+					((HTablePool.PooledHTable) table3).getWrappedTable(),
+					((HTablePool.PooledHTable) sameTable3).getWrappedTable());
+		}
+
+		@Test
+		public void testCloseTablePool() throws IOException {
+			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
+					getPoolType());
+			HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
+
+			if (admin.tableExists(TABLENAME)) {
+				admin.disableTable(TABLENAME);
+				admin.deleteTable(TABLENAME);
+			}
+
+			HTableDescriptor tableDescriptor = new HTableDescriptor(TABLENAME);
+			tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
+			admin.createTable(tableDescriptor);
+
+			// Request tables from an empty pool
+			HTableInterface[] tables = new HTableInterface[4];
+			for (int i = 0; i < 4; ++i) {
+				tables[i] = pool.getTable(TABLENAME);
+			}
+
+			pool.closeTablePool(TABLENAME);
+
+			for (int i = 0; i < 4; ++i) {
+				tables[i].close();
+			}
+
+			Assert.assertEquals(1,
+					pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
+
+			pool.closeTablePool(TABLENAME);
+
+			Assert.assertEquals(0,
+					pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
+		}
+	}
+
+	public static junit.framework.Test suite() {
+		TestSuite suite = new TestSuite();
+		suite.addTestSuite(TestHTableReusablePool.class);
+		suite.addTestSuite(TestHTableThreadLocalPool.class);
+		return suite;
+	}
+	@Test
+	public void testProxyImplementationReturned() {
+		HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+				Integer.MAX_VALUE);
+		String tableName = Bytes.toString(TABLENAME);// Request a table from
+														// an
+														// empty pool
+		HTableInterface table = pool.getTable(tableName);
+
+		// Test if proxy implementation is returned
+		Assert.assertTrue(table instanceof HTablePool.PooledHTable);
+	}
+
+	@Test
+	public void testDeprecatedUsagePattern() throws IOException {
+		HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+				Integer.MAX_VALUE);
+		String tableName = Bytes.toString(TABLENAME);// Request a table from
+														// an
+														// empty pool
+
+		// get table will return proxy implementation
+		HTableInterface table = pool.getTable(tableName);
+
+		// put back the proxy implementation instead of closing it
+		pool.putTable(table);
+
+		// Request a table of the same name
+		HTableInterface sameTable = pool.getTable(tableName);
+
+		// test no proxy over proxy created
+		Assert.assertSame(((HTablePool.PooledHTable) table).getWrappedTable(),
+				((HTablePool.PooledHTable) sameTable).getWrappedTable());
+	}
+
+	@Test
+	public void testReturnDifferentTable() throws IOException {
+		HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+				Integer.MAX_VALUE);
+		String tableName = Bytes.toString(TABLENAME);// Request a table from
+														// an
+														// empty pool
+
+		// get table will return proxy implementation
+		final HTableInterface table = pool.getTable(tableName);
+		HTableInterface alienTable = new HTable(TABLENAME) {
+			// implementation doesn't matter as long the table is not from
+			// pool
+		};
+		try {
+			// put the wrong table in pool
+			pool.putTable(alienTable);
+			Assert.fail("alien table accepted in pool");
+		} catch (IllegalArgumentException e) {
+			Assert.assertTrue("alien table rejected", true);
+		}
+
+	}
 }
\ No newline at end of file