package org.marketcetera.core.resourcepool; import java.util.Collection; import java.util.Iterator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.lang.Validate; import org.marketcetera.core.CloseableLock; import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.util.misc.ClassVersion; /* $License$ */ /** * Provides access to a pool of similar resources. * * @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a> * @version $Id: AbstractResourcePool.java 16901 2014-05-11 16:14:11Z colin $ * @since 2.4.0 */ @ClassVersion("$Id: AbstractResourcePool.java 16901 2014-05-11 16:14:11Z colin $") public abstract class AbstractResourcePool<ResourceClazz extends Resource<ResourceAllocationHintClazz>,ResourceAllocationHintClazz> implements ResourcePool<ResourceClazz,ResourceAllocationHintClazz> { /* (non-Javadoc) * @see org.springframework.context.Lifecycle#isRunning() */ @Override public final boolean isRunning() { return running.get(); } /* (non-Javadoc) * @see org.springframework.context.Lifecycle#start() */ @Override public synchronized final void start() { if(running.get()) { try { stop(); } catch (Exception e) { SLF4JLoggerProxy.warn(this, e); } } setStatus(ResourcePoolStatus.STARTING); doStart(); int availableResourceCount = 0; for(ResourceClazz resource : getResources()) { try { availableResourceCount += (resource.getResourceStatus().isReady() ? 1 : 0); } catch (Exception e) { SLF4JLoggerProxy.warn(this, e); } } Validate.isTrue(availableResourceCount > 0); setStatus(ResourcePoolStatus.READY); running.set(true); } /* (non-Javadoc) * @see org.springframework.context.Lifecycle#stop() */ @Override public synchronized final void stop() { setStatus(ResourcePoolStatus.STOPPING); try { doStop(); } catch (Exception e){ SLF4JLoggerProxy.warn(this, e); } finally { running.set(false); setStatus(ResourcePoolStatus.NOT_READY); } } /* (non-Javadoc) * @see com.marketcetera.marketdata.exegy.resource.ResourcePool#getStatus() */ @Override public ResourcePoolStatus getStatus() { return status; } /* (non-Javadoc) * @see com.marketcetera.marketdata.exegy.resource.ResourcePool#getResource() */ @Override public ResourceClazz getResource() { return getResource(null); } /* (non-Javadoc) * @see com.marketcetera.marketdata.exegy.resource.ResourcePool#getResource(java.lang.Object) */ @Override public ResourceClazz getResource(ResourceAllocationHintClazz inHint) { try(CloseableLock theLock = CloseableLock.create(getResourceLock().writeLock())) { theLock.lock(); Iterator<ResourceClazz> resourceCandidateIterator = getResources().iterator(); ResourceClazz candidateResource = null; while(resourceCandidateIterator.hasNext()) { candidateResource = resourceCandidateIterator.next(); if(candidateResource.getResourceStatus().isReady() && candidateResource.isSuitable(inHint)) { resourceCandidateIterator.remove(); candidateResource.allocated(); break; } candidateResource = null; } Validate.notNull(candidateResource); return candidateResource; } } /* (non-Javadoc) * @see com.marketcetera.marketdata.exegy.resource.ResourcePool#returnResource(com.marketcetera.marketdata.exegy.resource.Resource) */ @Override public void returnResource(ResourceClazz inResource) { if(inResource.isRunning() && inResource.getResourceStatus().isReady()) { returnResourceToPool(inResource); inResource.returned(); } else { inResource.stop(); inResource.released(); } } /** * Sets the status value. * * @param inStatus a <code>ResourcePoolStatus</code> value */ protected final void setStatus(ResourcePoolStatus inStatus) { status = inStatus; } /** * Gets the lock object used to control access to pool resources. * * @return a <code>ReadWriteLock</code> value */ protected final ReadWriteLock getResourceLock() { return resourceLock; } /** * Executed when the pool is starting. * * <p>The subclass is expected to allocate and start its resources in this method. */ protected abstract void doStart(); /** * Executed when the pool is stopping. * * <p>The subclass is expected to shut down its resources in this method. */ protected abstract void doStop(); /** * Gets the resources in the pool. * * <p>The order and contents of the resource pool are determined by the subclass and affect * what resources are allocated. The subclass is expected to modulate the order and contents * of this collection according to its whims. When a request for a resource is made, the * resources in this collection will be interrogated in the order of this collection. * * @return a <code>Collection<ResourceClazz></code> value */ protected abstract Collection<ResourceClazz> getResources(); /** * Returns the given resource to the resource pool. * * <p>It is the responsibility of the subclass to return this resource to the pool or not as it * sees fit. * * @param inResource a <code>ResourceClazz</code> value */ protected abstract void returnResourceToPool(ResourceClazz inResource); /** * allows access to critical resources to be controlled */ private final ReadWriteLock resourceLock = new ReentrantReadWriteLock(); /** * indicates the status of the resource pool */ private volatile ResourcePoolStatus status = ResourcePoolStatus.NOT_READY; /** * indicates if the resource pool is running or not */ private final AtomicBoolean running = new AtomicBoolean(false); }