Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

The Resource Pool is similar to the implementation of a semaphore. It takes the class type of the resource and number of resource pools associated with it as the constructor parameters. There is a small ambiguity here, when the Resource instance is created it uses reflection but when we require the resource name a call to getName() from the ResourceA interface is sufficient. Each Resource has a corresponding resource pool entry in the resource_pool_table Map. The poolAvailable Condition variable will signal all waiting threads when a resource is released back into the pool. Correspondingly threads will wait on the Resource Pool lock condition until an object pool is available for the resource with the resourceName.

package concurrency;

import java.lang.reflect.Constructor;
import java.util.Hashtable;
import java.util.Vector;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

interface ResourceA {
    String getName();
}

interface ResourceExceptionHandler {
    void handle(Exception exc);
}

class ResourceExceptionHandlerImpl implements ResourceExceptionHandler {

    @Override
    public void handle(Exception exc) {

    }
}

class ResourceCreationException extends Exception {
}

public class ResourcePool<R extends ResourceA> {

    private final Lock lock = new ReentrantLock();
    private final Condition poolAvailable = lock.newCondition();
    private int num_resources;
    private final int max_num_resources;
    private final Hashtable<String, Vector<R>> resource_pool_table = new Hashtable<String, Vector<R>>();
    private final Class<R> resourceClass;
    private final Constructor<R> resourceConstructor;
    private final ResourceExceptionHandler resourceExceptionHandler = new ResourceExceptionHandlerImpl();

    private R createResource(String str) {
        R resource = null;
        try {
            resource = resourceConstructor.newInstance(str);

            if (resource == null)
                throw new ResourceCreationException();

        } catch (Exception e) {
            resourceExceptionHandler.handle(e);
        }
        return resource;
    }

    public ResourcePool(Class<R> resourceClass, int num_resource_pools) throws Exception {
            this.resourceClass = resourceClass;
            this.resourceConstructor = resourceClass.getDeclaredConstructor(String.class);
            this.max_num_resources = num_resource_pools;
            this.num_resources = max_num_resources;
    }

    public R acquireResource(String resourceName) throws InterruptedException {
        lock.lock();
        try {
            while (num_resources <= 0)
                poolAvailable.await();

            --num_resources;

            Vector<R> pool = resource_pool_table.get(resourceName);

            if (pool != null) {
                int size = pool.size();
                if (size > 0)
                    return pool.remove(size - 1);
            }
            return createResource(resourceName);
        } finally {
            lock.unlock();
        }
    }

    public void releaseResource(R resource) {
        lock.lock();
        try {
            // check to ensure release does not occur before acquire
            if(num_resources >= max_num_resources)      
                return;

            String resourceName = resource.getName();
            Vector<R> pool = resource_pool_table.get(resourceName);

            if (pool == null) {
                pool = new Vector<R>();
                resource_pool_table.put(resourceName, pool);
            }

            pool.addElement(resource);
            ++num_resources;
            poolAvailable.signal();
        } finally {
            lock.unlock();
        }
    }


    public boolean isEmpty(String poolName){
        if(resource_pool_table.get(poolName)==null)
            return true;
        else 
            return false;
    }

    public boolean isFull(String poolName){
        if(resource_pool_table.get(poolName)==null)
            return false;
        else if(resource_pool_table.get(poolName).size() == max_num_resources)
            return true;
        else
            return false;
    }

    public int getNumOfResourcesInPool(String poolName){
        if(resource_pool_table.get(poolName)==null)
            return 0;
        else
            return resource_pool_table.get(poolName).size();
    }
}

JUnit tests

 package concurrency;

import junit.framework.TestCase;

/*
 * Test Resource 
 */

class SomeResource implements ResourceA {
    private String name;
    public SomeResource(String name) {
        super();
        this.name = name;
    }
    @Override
    public String getName() {
        return name;
    }
}

/*
 * Test cases for Resource Pool
 */
public class ResourcePoolTest extends TestCase {

  /*Checks the number of the resources in the Resource Table.
    Resource is created only if needed.*/
    public void testIsEmptyBeforeResourceAcquired() throws Exception {
        ResourcePool<SomeResource> resPool = new ResourcePool<SomeResource>(
                SomeResource.class, 5);
        assertTrue(resPool.isEmpty("A"));
    }

 /* Checks if the number of resouces in the Resource Table is equal to the 
  * maximum number of resources declared at the Resouce Pool creation time.*/   
    public void testIsFull() throws Exception {
        ResourcePool<SomeResource> resPool = new ResourcePool<SomeResource>(
                SomeResource.class, 5);
        SomeResource[] res = new SomeResource[5];
        for (int i = 0; i <= 4; i++) {
            res[i] = resPool.acquireResource("A");
        }
        for (int i = 0; i <= 4; i++) {
            resPool.releaseResource(res[i]);
        }
        assertTrue(resPool.isFull("A"));
        assertFalse(resPool.isEmpty("A"));
    }

    public  void testAcquireBlocksWhenEmpty() throws Exception {
        final ResourcePool<SomeResource> resPool = new ResourcePool<SomeResource>(
                SomeResource.class, 0);
        Thread resourceConsumer = new Thread() {
            public void run() {
                try {
                    SomeResource unused = resPool.acquireResource("A");
                    fail(); // error if control flow reaches this line
                } catch (InterruptedException e) {

                }
            }
        };

            resourceConsumer.start();
            Thread.sleep(1000); // waits for the resourceConsumer to block
            resourceConsumer.interrupt();   
            resourceConsumer.join(1000);    // resume after the resourceConsumer ends
            assertFalse(resourceConsumer.isAlive());

    }

}
share|improve this question
add comment

1 Answer 1

  • No need to use synchronization in the constructor - at the moment of creation the object is not yet visible to any concurrent thread.

  • Declare field Constructor<R> resourceConstructor and in the constructor, initialize it with resourceClass.getDeclaredConstructor(String.class). Thus possible error when there is no such constrictor is determined early, and saves time to extract the constructor later. Use it in the method createResource():

          resource = resourceConstructor.newInstance(str);
    
  • Rename num_resource_pools to num_resources, as it actually counts the overall number of resources, not number of pools.

  • Create a couple of tests and run them every time the code is modified.

share|improve this answer
    
Thanks Alexei for your review. I incorporated the review comments in the edited code. –  clinton Dec 1 '12 at 5:06
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.