/** * */ package com.trendrr.oss; import java.util.Date; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.trendrr.oss.concurrent.Sleep; import com.trendrr.oss.exceptions.TrendrrClosedException; import com.trendrr.oss.exceptions.TrendrrInitializationException; import com.trendrr.oss.exceptions.TrendrrTimeoutException; /** * A simple threadsafe, fixed size, round robin pool of objects. * * @author Dustin Norlander * @created Jul 26, 2013 * */ public class Pool<T> { protected static Log log = LogFactory.getLog(Pool.class); /** * interface used to create new items in the pool. This will be called lazily when a new item is needed * @author Dustin Norlander * @created Jul 26, 2013 * */ public static interface Creator<T> { /** * creates a new object. This should return a fully initialized object or an exception, never null. * @return * @throws Exception */ public T create() throws Exception; //clean up this object, it was returned as bad, or the pool is closed public void cleanup(T obj); } /** * Internal class to handle refreshing a connection * * * @author Dustin Norlander * @created Jul 26, 2013 * */ private static class Refresher { Date lastLookup = null; } private Creator<T> creator; private LinkedBlockingQueue queue; private int size; /** * The min amount of time before we retry to create a new object. */ private int minRefreshMillis = 1000*5; private ReentrantReadWriteLock closeLock = new ReentrantReadWriteLock(); private boolean closed = false; public Pool(Creator<T> creator, int size) { this.creator = creator; this.queue = new LinkedBlockingQueue(size); for (int i=0; i < size; i++) { this.queue.offer(new Refresher()); } this.size = size; } /** * Gets the total pool size * @return */ public int getSize() { return this.size; } public void close() { try { this.closeLock.writeLock().lock(); if (this.closed) { return; } Object o = this.queue.poll(); while(o != null) { if (!(o instanceof Refresher)) { T v = (T)o; this.creator.cleanup(v); } o = this.queue.poll(); } } finally { this.closeLock.writeLock().unlock(); } } /** * Return an item to the pool * @param obj */ public void returnGood(T obj) { try { this.closeLock.readLock().lock(); if (this.closed) { this.creator.cleanup(obj); return; } this.queue.offer(obj); } finally { this.closeLock.readLock().unlock(); } } public void returnBroken(T obj) { try { this.closeLock.readLock().lock(); this.creator.cleanup(obj); if (this.closed) { return; } this.queue.offer(new Refresher()); } finally { this.closeLock.readLock().unlock(); } } public T borrow(long timeoutMillis) throws TrendrrTimeoutException, TrendrrInitializationException, TrendrrClosedException { try { this.closeLock.readLock().lock(); if (this.closed) { throw new TrendrrClosedException("pool is closed, try the beach"); } Object v = null; try { v = this.queue.poll(timeoutMillis, TimeUnit.MILLISECONDS); } catch (InterruptedException x) { //not exactly the right exception, but this case should be exceedingly rare. throw new TrendrrTimeoutException(x); } if (v == null) { throw new TrendrrTimeoutException("Pool error unable to get item within :" + timeoutMillis + " millis"); } if (v instanceof Refresher) { //refresh the item Refresher r = (Refresher)v; if (r.lastLookup != null && r.lastLookup.getTime() > new Date().getTime()-this.minRefreshMillis) { //do a little sleep and try again. long sleep = Math.min(timeoutMillis, this.minRefreshMillis); Sleep.millis(sleep); if (r.lastLookup.getTime() > new Date().getTime()-this.minRefreshMillis) { this.queue.offer(r); throw new TrendrrTimeoutException("Pool error unable to get item within :" + timeoutMillis + " millis"); } } //we should be good now to create a new object. try { T val = this.creator.create(); return val; } catch (Exception x) { r.lastLookup = new Date(); this.queue.offer(r); throw new TrendrrInitializationException(x); } } return (T)v; } finally { this.closeLock.readLock().unlock(); } } }