Index: src/test/java/org/apache/hadoop/hbase/client/TestHCM.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/client/TestHCM.java (revision 1307656) +++ src/test/java/org/apache/hadoop/hbase/client/TestHCM.java (working copy) @@ -23,22 +23,19 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertEquals; -import java.io.IOException; -import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Random; -import java.util.Set; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; -import org.apache.commons.httpclient.HostConfiguration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.*; +import org.apache.hadoop.hbase.client.HTable.DaemonThreadFactory; import org.apache.hadoop.hbase.util.Bytes; import org.junit.AfterClass; import org.junit.Assert; @@ -54,6 +51,7 @@ private static final Log LOG = LogFactory.getLog(TestHCM.class); private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); private static final byte[] TABLE_NAME = Bytes.toBytes("test"); + private static final byte[] TABLE_NAME1 = Bytes.toBytes("test1"); private static final byte[] FAM_NAM = Bytes.toBytes("f"); private static final byte[] ROW = Bytes.toBytes("bbb"); @@ -136,11 +134,34 @@ conn.deleteCachedLocation(TABLE_NAME, ROW); HRegionLocation rl = conn.getCachedLocation(TABLE_NAME, ROW); assertNull("What is this location?? " + rl, rl); - conn.close(); table.close(); } /** + * Test that Connection or Pool are not closed when managed externally + * @throws Exception + */ + @Test + public void testConnectionManagement() throws Exception{ + TEST_UTIL.createTable(TABLE_NAME1, FAM_NAM); + HConnection conn = HConnectionManager.createConnection(TEST_UTIL.getConfiguration()); + ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 10, + 60, TimeUnit.SECONDS, + new SynchronousQueue(), + new DaemonThreadFactory()); + + HTable table = new HTable(TABLE_NAME1, conn, pool); + table.close(); + assertFalse(conn.isClosed()); + assertFalse(pool.isShutdown()); + table = new HTable(TEST_UTIL.getConfiguration(), TABLE_NAME1, pool); + table.close(); + assertFalse(pool.isShutdown()); + conn.close(); + pool.shutdownNow(); + } + + /** * Make sure that {@link HConfiguration} instances that are essentially the * same map to the same {@link HConnection} instance. */ Index: src/main/java/org/apache/hadoop/hbase/client/HTable.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/client/HTable.java (revision 1307656) +++ src/main/java/org/apache/hadoop/hbase/client/HTable.java (working copy) @@ -115,7 +115,8 @@ private boolean closed; private int operationTimeout; private static final int DOPUT_WB_CHECK = 10; // i.e., doPut checks the writebuffer every X Puts. - private final boolean cleanupOnClose; // close the connection in close() + private final boolean cleanupPoolOnClose; // shutdown the pool in close() + private final boolean cleanupConnectionOnClose; // close the connection in close() /** * Creates an object to access a HBase table. @@ -146,7 +147,7 @@ public HTable(Configuration conf, final byte [] tableName) throws IOException { this.tableName = tableName; - this.cleanupOnClose = true; + this.cleanupPoolOnClose = this.cleanupConnectionOnClose = true; if (conf == null) { this.connection = null; return; @@ -176,6 +177,30 @@ /** * Creates an object to access a HBase table. * Shares zookeeper connection and other resources with other HTable instances + * created with the same conf instance. Uses already-populated + * region cache if one is available, populated by any other HTable instances + * sharing this conf instance. + * Use this constructor when the ExecutorService is externally managed. + * @param conf Configuration object to use. + * @param tableName Name of the table. + * @param pool ExecutorService to be used. + * @throws IOException if a remote or network exception occurs + */ + public HTable(Configuration conf, final byte[] tableName, final ExecutorService pool) + throws IOException { + this.connection = HConnectionManager.getConnection(conf); + this.configuration = conf; + this.pool = pool; + this.tableName = tableName; + this.cleanupPoolOnClose = false; + this.cleanupConnectionOnClose = true; + + this.finishSetup(); + } + + /** + * Creates an object to access a HBase table. + * Shares zookeeper connection and other resources with other HTable instances * created with the same connection instance. * Use this constructor when the ExecutorService and HConnection instance are * externally managed. @@ -193,7 +218,7 @@ throw new IllegalArgumentException("Connection is null or closed."); } this.tableName = tableName; - this.cleanupOnClose = false; + this.cleanupPoolOnClose = this.cleanupConnectionOnClose = false; this.connection = connection; this.configuration = connection.getConfiguration(); this.pool = pool; @@ -955,8 +980,10 @@ return; } flushCommits(); - if (cleanupOnClose) { + if (cleanupPoolOnClose) { this.pool.shutdown(); + } + if (cleanupConnectionOnClose) { if (this.connection != null) { this.connection.close(); }