package org.distributeme.core.asynch; import org.distributeme.core.Defaults; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * A helper class to synchronously execute multiple asynchronous calls and collect results. * * @author lrosenberg * @version $Id: $Id */ public class MultiCallCollector { /** * Internal latch which counts down executed calls. */ private final CountDownLatch latch; /** * Subhandlers for calls. */ private ConcurrentMap<String, SubCallBackHandler> handlers; /** * Number of total calls that should be executed here. */ private int numberOfCalls; /** * <p>Constructor for MultiCallCollector.</p> * * @param aNumberOfCalls a int. */ public MultiCallCollector(int aNumberOfCalls){ numberOfCalls = aNumberOfCalls; latch = new CountDownLatch(numberOfCalls); handlers = new ConcurrentHashMap<String, MultiCallCollector.SubCallBackHandler>(); } /** * <p>waitForResults.</p> * * @param timeout a long. * @throws java.lang.InterruptedException if any. */ public void waitForResults(long timeout) throws InterruptedException{ latch.await(timeout, TimeUnit.MILLISECONDS); } /** * <p>waitForResults.</p> * * @throws java.lang.InterruptedException if any. */ public void waitForResults() throws InterruptedException{ waitForResults(Defaults.getDefaultAsynchCallTimeout()); } /** * <p>isFinished.</p> * * @return a boolean. */ public boolean isFinished(){ return latch.getCount() == 0; } /** * <p>createSubCallHandler.</p> * * @param id a {@link java.lang.String} object. * @return a {@link org.distributeme.core.asynch.CallBackHandler} object. */ public CallBackHandler createSubCallHandler(String id){ if (handlers.size()>=numberOfCalls) throw new IllegalStateException("There are already "+numberOfCalls+" calls running"); SubCallBackHandler newHandler = new SubCallBackHandler(this); if (handlers.putIfAbsent(id, newHandler)!=null){ throw new IllegalArgumentException("Call with id "+id+" is already started!"); } return newHandler; } /** * <p>notifySubCallFinished.</p> */ protected void notifySubCallFinished(){ latch.countDown(); } /** * An instance of this class is used as a call handler for each subcall. * @author lrosenberg * */ public static class SubCallBackHandler implements CallBackHandler{ /** * Link to the collector. */ private MultiCallCollector parent; /** * Return value. */ private volatile Object returnValue; /** * Exception if thrown. */ private volatile Exception returnException; /** * Creates a new subcallbackhandler. * @param aParent */ public SubCallBackHandler(MultiCallCollector aParent) { parent = aParent; } @Override public void success(Object o) { returnValue = o; parent.notifySubCallFinished(); } @Override public void error(Exception e) { returnException = e; parent.notifySubCallFinished(); } } /** * Returns the handler for a call id. * @param id * @return */ private SubCallBackHandler getHandler(String id){ return handlers.get(id); } /** * <p>isError.</p> * * @param id a {@link java.lang.String} object. * @return a boolean. */ public boolean isError(String id){ return isFinished() && getHandler(id).returnException!=null; } /** * <p>isSuccess.</p> * * @param id a {@link java.lang.String} object. * @return a boolean. */ public boolean isSuccess(String id){ return isFinished() && getHandler(id).returnException==null; } /** * <p>getReturnValue.</p> * * @param id a {@link java.lang.String} object. * @return a {@link java.lang.Object} object. */ public Object getReturnValue(String id){ return getHandler(id).returnValue; } /** * <p>getReturnException.</p> * * @param id a {@link java.lang.String} object. * @return a {@link java.lang.Exception} object. */ public Exception getReturnException(String id){ return getHandler(id).returnException; } }