One day, I needed a good resource pool manager that's flexible.
First, the generic pool code:
package com.etretatlogiciels.utilities;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.ReentrantLock;
public abstract class ResourcePool< T >
{
private int size; // size to which pool can grow
private int count; // of objects in pool currently
private BlockingQueue< T > poolQueue;
private final ReentrantLock lock = new ReentrantLock();
public int size() { return size; }
public int count() { return count; }
public T acquire() throws Exception
{
if( !lock.isLocked() && lock.tryLock() )
{
try
{
count++;
return create();
}
finally
{
if( count < size )
lock.unlock();
}
}
return poolQueue.take();
}
public void recycle( T resource ) { poolQueue.add( resource ); }
protected abstract T create() throws Exception;
protected void clear() { if( poolQueue != null ) poolQueue.clear(); count = size = 0; }
protected void initializePool( int size )
{
// enable fairness; otherwise, some threads may wait forever.
poolQueue = new ArrayBlockingQueue<>( size, true );
this.size = size;
}
}
Next, the implementation of a specific pool. Today, it's an HTTP connection:
package com.etretatlogiciels.utilities;
import java.net.MalformedURLException;
import java.net.URL;
public class HttpUrlConnectionPool< HttpURLConnection > extends ResourcePool< HttpURLConnection >
{
private static final String PROTOCOL_HOSTNAME_AND_PORT = "http://localhost:9999";
private final URL CONNECTION_URL;
public HttpUrlConnectionPool( final String uri ) throws MalformedURLException
{
String path = ( uri.startsWith( "/" ) ) ? uri : '/' + uri;
CONNECTION_URL = new URL( PROTOCOL_HOSTNAME_AND_PORT + path );
}
@Override
protected HttpURLConnection create() throws Exception
{
HttpURLConnection connection = ( HttpURLConnection ) CONNECTION_URL.openConnection();
return connection;
}
@Override
public HttpURLConnection acquire() throws Exception
{
return super.acquire();
}
}
An obligatory test of the pool infrastructure—not the connection(s) themselves:
package com.etretatlogiciels.utilities;
import java.net.HttpURLConnection;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import static org.junit.Assert.assertTrue;
public class HttpUrlConnectionPoolTest
{
@Rule public TestName name = new TestName();
@Before public void setUp() { TestUtilities.setUp( name ); }
@After public void tearDown() { }
private static final boolean VERBOSE = false;
@Test
public void testSanity() throws Exception
{
HttpUrlConnectionPool pool = new HttpUrlConnectionPool( "fun_and_games" );
// create the pool...
println( "Size (after instantiation): " + pool.size() );
println( "Count (after instantiation): " + pool.count() );
assertTrue( "Wrong size", pool.size() == 0 );
assertTrue( "Wrong count", pool.count() == 0 );
// set the pool size...
pool.initializePool( 3 );
println( "Size (after initialization): " + pool.size() );
println( "Count (after initialization): " + pool.count() );
assertTrue( "Wrong size", pool.size() == 3 );
assertTrue( "Wrong count", pool.count() == 0 );
// how to acquire an instance from the pool...
HttpURLConnection connection = ( HttpURLConnection ) pool.acquire();
println( "Size (after acquisition): " + pool.size() );
println( "Count (after acquisition): " + pool.count() );
assertTrue( "Wrong size", pool.size() == 3 );
assertTrue( "Wrong count", pool.count() == 1 );
// verify that we can set various qualities of the connection in our use:
connection.setRequestMethod( "POST" );
connection.setUseCaches( false );
connection.setDoOutput( true );
String method = connection.getRequestMethod();
assertTrue( "Wrong HTTP method", method.equals( "POST" ) );
// how to zero-out the pool when finished...
pool.clear();
println( "Size (after clearing): " + pool.size() );
println( "Count (after clearing): " + pool.count() );
assertTrue( "Wrong size", pool.size() == 0 );
assertTrue( "Wrong count", pool.count() == 0 );
}
private static void println( final String line )
{
if( VERBOSE )
System.out.println( line );
}
}
Here's the output from the test above:
Size (after instantiation): 0 Count (after instantiation): 0 Size (after initialization): 3 Count (after initialization): 0 Size (after acquisition): 3 Count (after acquisition): 1 Size (after clearing): 0 Count (after clearing): 0