package org.marketcetera.module; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import org.marketcetera.util.misc.ClassVersion; import org.marketcetera.util.misc.NamedThreadFactory; /* $License$ */ /** * CopierModule passes the data coming in as part of DataRequest to the receiver. * * @author anshul@marketcetera.com * @author toli@marketcetera.com * @version $Id: CopierModule.java 16154 2012-07-14 16:34:05Z colin $ * @since 1.0.0 */ @ClassVersion("$Id: CopierModule.java 16154 2012-07-14 16:34:05Z colin $") //$NON-NLS-1$ public class CopierModule extends Module implements DataEmitter { protected CopierModule() { super(CopierModuleFactory.INSTANCE_URN, true); } protected void preStart() throws ModuleException { } protected void preStop() throws ModuleException { } /** Passes the incoming data to the receiver */ public void requestData(final DataRequest inRequest, final DataEmitterSupport inSupport) throws RequestDataException { Future<?> result = mService.submit(new Callable<Object>(){ public Object call() throws Exception { Object req = inRequest.getData(); Semaphore requester = null; if(req instanceof SynchronousRequest) { SynchronousRequest synchronousRequest = (SynchronousRequest)req; requester = synchronousRequest.semaphore; if(requester.availablePermits() != 0) { inSupport.dataEmitError(TestMessages.INCORRECT_SEMAPHORE_STATE, true); return null; } req = synchronousRequest.getPayload(); } if(req instanceof Object[]) { for(Object o:(Object[])req) { inSupport.send(o); } } else if (req instanceof Collection) { for(Object o: (Collection<?>)req) { inSupport.send(o); } } else { inSupport.send(req); } if(requester != null) { requester.release(); } return null; } }); mRequestTable.put(inSupport.getRequestID(), result); } public void cancel(DataFlowID inFlowID, RequestID inRequestID) { Future<?> f = mRequestTable.get(inRequestID); if(f != null) { f.cancel(true); } } private final ExecutorService mService = Executors.newCachedThreadPool( new NamedThreadFactory("TestCopierModule")); private final Map<RequestID, Future<?>> mRequestTable = new HashMap<RequestID, Future<?>>(); /** * A special kind of request that indicates that the given payload should be delivered synchronously. * * <p> This class is intended to be used with {@link CopierModule}. Normally, {@link CopierModule} delivers * the payload passed to it via {@link CopierModule#requestData(DataRequest, DataEmitterSupport)} asynchronously. * However, if the data request payload is actually an instance of <code>SynchronousRequest</code>, the * payload <em>can</em> be induced to behave synchronously by means of a {@link Semaphore}. * * <p>For example: * <pre> * ModuleURN myDestinationURN; * Object[] payload = new Object[] { "Some object", "that I want copier to send", "while I wait" }; * SynchronousRequest request = new SynchronousRequest(payload); * request.semaphore.acquire(); * moduleManager.createDataFlow(new DataRequest[] { new DataRequest(CopierModuleFactory.INSTANCE_URN, * request), * new DataRequest(myDestinationURN) }, * false); * request.semaphore.acquire(); * </pre> * * <p>Note that at the conclusion of the above block, the request should be allowed to go out of scope and * be garbage-collected or the permit acquired by the calling code should be released: * <pre> * request.semaphore.release(); * </pre> * * <p>Proper use of this class guarantees only that the payload will be delivered synchronously. The recipient * of the payload may, of course, choose to act asynchronously itself. * * @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a> * @version $Id: CopierModule.java 16154 2012-07-14 16:34:05Z colin $ * @since 1.0.0 */ public static class SynchronousRequest { /** * the payload for the {@link CopierModule} to deliver */ private final Object payload; /** * the semaphore used by the {@link CopierModule} to indicate that the payload has been delivered */ public final Semaphore semaphore; /** * Create a new SynchronousRequest instance. * * @param inPayload an <code>Object</code> value to be delivered by the {@link CopierModule}. */ public SynchronousRequest(Object inPayload) { payload = inPayload; semaphore = new Semaphore(1); } /** * Returns the payload to be delivered. * * @return an <code>Object</code> value */ public Object getPayload() { return payload; } } }