Home | FAQ | Contact me

Resource pools

One day, I needed a good resource pool manager that's flexible.

ResourcePool.java:

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;
  }
}
HttpUrlConnectionPool.java:

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();
  }
}
HttpUrlConnectionPoolTest.java:

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