package org.marketcetera.module; import org.marketcetera.util.misc.ClassVersion; import org.marketcetera.util.log.SLF4JLoggerProxy; import org.marketcetera.util.log.I18NBoundMessage1P; import org.marketcetera.core.NoMoreIDsException; import org.marketcetera.core.IDFactory; import org.marketcetera.core.InMemoryIDFactory; import java.util.Date; import java.util.HashSet; /* $License$ */ /** * Instances of this class maintain all the information on a data flow. * These instances are used by the module framework to keep track of * all the data flows. * * @author anshul@marketcetera.com * @version $Id: DataFlow.java 16154 2012-07-14 16:34:05Z colin $ * @since 1.0.0 */ @ClassVersion("$Id: DataFlow.java 16154 2012-07-14 16:34:05Z colin $") //$NON-NLS-1$ class DataFlow { /** * Returns the data flow ID. * * @return the data flow ID. */ public DataFlowID getFlowID() { return mFlowID; } /** * The URN of the module that requested this data flow. * * @return URN of the module that requested this data flow. * null, if this data flow was not requested by a module. */ public ModuleURN getRequesterURN() { return mRequesterURN; } /** * Creates a data flow info instance that snapshots the current * state of this data flow. * * @return a snapshot of the current state of this data flow. */ public DataFlowInfo toDataFlowInfo() { DataFlowStep [] steps = new DataFlowStep[mRequests.length]; for(int i = 0; i < mRequests.length; i++) { //every module except the last one is an emitter //number of couplers is one less than number of requests. boolean isEmitter = i < mCouplers.length; //every module except the first one is a receiver boolean isReceiver = i > 0; steps[i] = new DataFlowStep( mRequests[i].toStringRequest(), isEmitter ? mCouplers[i].getEmitterURN() : mCouplers[i - 1].getReceiverURN(), isEmitter, isReceiver, isEmitter ? mCouplers[i].getEmitted() : 0, isReceiver ? mCouplers[i - 1].getReceived() : 0, isEmitter ? mCouplers[i].getEmitErrors() : 0, isReceiver ? mCouplers[i-1].getReceiveErrors() : 0, isEmitter ? mCouplers[i].getLastEmitError() : null, isReceiver ? mCouplers[i - 1].getLastReceiveError() : null); } return new DataFlowInfo(steps, mFlowID, mRequesterURN, mStopRequesterURN, mCreated, mStopped); } /** * Returns true if this data flow was created by a module. * * @return if this data flow was created by a module. */ public boolean isModuleCreated() { return mRequesterURN != null; } /** * Creates an instance. * * @param inManager the module manager instance. * @param inRequesterURN the data flow requester URN. * @param inRequests the data requests specified when requesting * this data flow. * @param inModules the modules participating in this data flow. * * @throws ModuleException if there was an error generating a flowID * for this data flow. */ DataFlow(ModuleManager inManager, ModuleURN inRequesterURN, DataRequest[] inRequests, Module[] inModules) throws ModuleException { mFlowID = generateFlowID(); mRequesterURN = inRequesterURN; mRequests = inRequests; mCouplers = new AbstractDataCoupler[inModules.length - 1]; assert inModules.length == mRequests.length; for(int i = mCouplers.length - 1; i >= 0; i--) { mCouplers[i] = mRequests[i].getCoupling().createCoupler( inManager, inModules[i], inModules[i + 1], mFlowID); } } /** * Initialize the flow by initiating requests on the emitters and plumbing * them to the respective receivers. * * @throws ModuleException if there was a failure setting up the data flow. */ void initFlow() throws ModuleException { boolean failed = true; int i = mCouplers.length - 1; //Acquire lock to prevent concurrent cancellation that might happen //if a participating module emits or throws an error within the data //flow requesting that the data flow be stopped. This lock will //cause the cancellation to block until the flow has been //completely setup. synchronized (this) { try { for(; i >= 0; i--) { mCouplers[i].initiateRequest(generateRequestID(), mRequests[i]); } failed = false; } finally { if(failed) { mCancelling = true; //go through all the initiated requests and cancel them. while(++i < mCouplers.length) { mCouplers[i].cancelRequest(); } } } } } /** * Cancels the data flow. Iterates through all the data flows * and cancels their requests. * * @param inStopRequester the module requesting that the data flow * be stopped. null, if this request is not being made by a module. * * @throws DataFlowException if the data flow is in the process of * being canceled by another operation. */ void cancel(ModuleURN inStopRequester) throws DataFlowException { SLF4JLoggerProxy.debug(this, "Stopping flow {} requested by {}", //$NON-NLS-1$ getFlowID(), inStopRequester ); //Only allow one thread to carry out cancellation at a time. synchronized (this) { if(mCancelling) { throw new DataFlowException(new I18NBoundMessage1P( Messages.DATA_FLOW_ALREADY_CANCELING, getFlowID().toString())); } else { mCancelling = true; } } // Go through each of the requests and cancel the requests // from first to last for(AbstractDataCoupler coupler: mCouplers) { coupler.cancelRequest(); } mStopRequesterURN = inStopRequester; mStopped = new Date(); } /** * Returns the set of URNs of modules that are participating * in this data flow. * * @return the set of URNs of modules participating in this * data flow. */ HashSet<ModuleURN> getParticipants() { HashSet<ModuleURN> participants = new HashSet<ModuleURN>(); for(int i = 0; i < mCouplers.length; i++) { if(i == 0) { participants.add(mCouplers[i].getEmitterURN()); } participants.add(mCouplers[i].getReceiverURN()); } return participants; } /** * Generates a unique ID for a data flow. * * @return a unique ID for identifying a data flow * * @throws ModuleException if there were errors generating the * data flow ID. */ private static DataFlowID generateFlowID() throws ModuleException { try { return new DataFlowID(sIDFactory.getNext()); } catch (NoMoreIDsException e) { throw new ModuleException(e, Messages.UNABLE_GENERATE_FLOW_ID); } } /** * Generates the request ID. * * @return the request ID. * * @throws ModuleException if there were errors generating the request ID. */ private static RequestID generateRequestID() throws ModuleException { try { return new RequestID(sIDFactory.getNext()); } catch (NoMoreIDsException e) { throw new ModuleException(e, Messages.UNABLE_GENERATE_REQUEST_ID); } } /* * These variables are defined as volatile as they can be read * and written by different threads. Its assumed that synchronization * is carried out at a higher level. */ private volatile ModuleURN mStopRequesterURN; private volatile Date mStopped = null; private final ModuleURN mRequesterURN; private final DataFlowID mFlowID; private final DataRequest[] mRequests; private final AbstractDataCoupler[] mCouplers; private final Date mCreated = new Date(); private boolean mCancelling = false; /** * ID factory for generating data flow IDs. */ private static final IDFactory sIDFactory = new InMemoryIDFactory(1); }