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.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_resource_pools;
private final Hashtable<String, Vector<R>> resource_pool_table = new Hashtable<String, Vector<R>>();
private final Class<R> resourceClass;
private final ResourceExceptionHandler resourceExceptionHandler = new ResourceExceptionHandlerImpl();
private R createResource(String str) {
R resource = null;
try {
resource = resourceClass.getDeclaredConstructor(String.class)
.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) {
lock.lock();
try {
this.resourceClass = resourceClass;
this.num_resource_pools = num_resource_pools;
} finally {
lock.unlock();
}
}
public R acquireResource(String resourceName) throws InterruptedException {
lock.lock();
try {
while (num_resource_pools <= 0)
poolAvailable.await();
--num_resource_pools;
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 {
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_resource_pools;
poolAvailable.signal();
} finally {
lock.unlock();
}
}
}
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());
}
}