package org.marketcetera.module;
import org.marketcetera.util.misc.ClassVersion;
import java.util.Date;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/* $License$ */
/**
* An abstraction representing a module that can emit data, consume data or
* create / cancel data flows.
*
* A module that is capable of emitting data must
* implement {@link org.marketcetera.module.DataEmitter}.
*
* A module that is capable of receiving data must implement
* {@link org.marketcetera.module.DataReceiver}.
*
* A module that can request data to be supplied to it must implement
* {@link DataFlowRequester}
*
* If the module wants to expose a management interface, it should
* implement an interface that is annotated with
* {@link javax.management.MXBean}. The module framework will automatically
* export that interface as the management interface for the module after
* it has been initialized.
*
* @author anshul@marketcetera.com
* @version $Id: Module.java 16154 2012-07-14 16:34:05Z colin $
* @since 1.0.0
*/
@ClassVersion("$Id: Module.java 16154 2012-07-14 16:34:05Z colin $") //$NON-NLS-1$
public abstract class Module {
/**
* Returns true if this module should be auto-started when its
* instantiated. If this method returns true, the module
* is auto-started right after its created. Otherwise, the user
* has to explicitly request the module framework to start
* the module.
*
* @return true if the module should auto-started on creation.
*/
public final boolean isAutoStart() {
return mAutoStart;
}
/**
* Returns the module's URN.
*
* The module URN should have the provider's URN as its parent.
* If it doesn't, the module creation fails with an error.
*
* @return the module's URN.
*/
public final ModuleURN getURN() {
return mURN;
}
/**
* Returns the module state.
*
* @return the module state.
*/
public final ModuleState getState() {
return mState;
}
/**
* The timestamp, when the module was created.
*
* @return the timestamp when the module was created.
*/
public final Date getCreated() {
return mCreated;
}
/**
* Returns true if the module has been auto-created.
*
* A module instance is auto-created, if it doesn't exist, when its URN
* is specified in a create data flow request and its factory supports
* {@link ModuleFactory#isAutoInstantiate() auto-instantiation}.
*
* @return if the module has been auto-created.
*/
public final boolean isAutoCreated() {
return mAutoCreated;
}
/**
* The timestamp when the module was last started. Null,
* if the module hasn't been started yet.
*
* @return the timestamp when the module was started.
*/
public final Date getStarted() {
return mStarted;
}
/**
* The timestamp when the module was last stopped. Null,
* if the module hasn't been stopped yet.
*
* @return the timestamp when the module was stopped.
*/
public final Date getStopped() {
return mStopped;
}
/**
* The failure message from the last attempt to start
* the module. Null, if the last module start did not fail.
*
* @return the failure message from the last attempt
* to start the module.
*/
public final String getLastStartFailure() {
return mLastStartFailure;
}
/**
* The failure message from the last failed attempt to stop
* the module. Null, if the last module stop did not fail.
*
* @return the failure message from the last failed attempt
* to stop the module
*/
public final String getLastStopFailure() {
return mLastStopFailure;
}
/**
* This method is invoked when the module is being started
* before it receives or can send data. Module can implement this method to
* carry out any checks / initialization.
*
* This method can be used by the module to verify that all
* its correctly setup and is configured to start operating.
*
* The module framework will start sending this module data,
* if its a receiver or data requests if this module is an
* emitter once this method returns successfully.
*
* Typically, a module that implements {@link DataFlowRequester} will
* create data flows from within this method. Do note that any
* data flows created from within this method are started immediately.
* If this module is participating in the data flow that its creating,
* it should make sure that its ready to participate in the
* data flows as it will get plumbed into the data flow right away.
*
* The module state is set to {@link ModuleState#STARTING} when its
* executing this method. If the method returns without throwing any
* exceptions, the module state is set to {@link ModuleState#STARTED}.
* If the methods throws an exception, the module fails to start and
* its state is set to {@link ModuleState#START_FAILED}.
*
* @throws ModuleException if the module is not ready to
* start receiving / sending data.
*/
protected abstract void preStart() throws ModuleException;
/**
* This method is invoked when a module is being stopped.
* Module can implement this method to carry out
* cleanups or cancel data flow requests in an orderly way.
*
* After this method returns, all the data flows that the module
* initiated and are still running, will be stopped.
*
* A module can prevent itself from stopping by throwing an
* exception from this method.
*
* @throws ModuleException if the module is not ready to be
* stopped. Throwing this exception will cause stop to fail.
*/
protected abstract void preStop() throws ModuleException;
/**
* Creates an instance.
* The specified module URN should have the provider URN as
* its parent. ie.
* <code>inURN.parent().equals(providersURN);</code>
*
* Otherwise, module creation fails with an error.
*
* @param inURN the module's URN.
* @param inAutoStart if the module should be automatically started
* after being created.
*/
protected Module(ModuleURN inURN, boolean inAutoStart) {
mURN = inURN;
mAutoStart = inAutoStart;
}
/**
* Sets if the module has been auto-created. This method is
* invoked by the module framework. This attribute is set to
* true when the module instance is auto-created by the framework.
*
* @param inAutoCreated if the module has been auto-created
*/
final void setAutoCreated(boolean inAutoCreated) {
mAutoCreated = inAutoCreated;
}
/**
* Sets the module state.
*
* @param inState the module state.
*/
final void setState(ModuleState inState) {
assert inState != null;
mState = inState;
switch (inState) {
case STARTED:
mStarted = new Date();
break;
case STOPPED:
mStopped = new Date();
break;
default:
//do nothing
}
}
/**
* Sets the failure message from the last attempt to start
* the module.
*
* @param inLastStartFailure failure message from the last
* attempt to start the module. null, if the last attempt to
* start the module succeeded.
*/
final void setLastStartFailure(String inLastStartFailure) {
mLastStartFailure = inLastStartFailure;
}
/**
* Sets the failure message from the last failed attempt to
* stop the module.
*
* @param inLastStopFailure failure message from the last
* failed attempt to stop the module. null, if the last
* attempt to stop the module succeeded.
*/
final void setLastStopFailure(String inLastStopFailure) {
mLastStopFailure = inLastStopFailure;
}
/**
* Returns the lock that should be used for serializing module
* operations.
*
* @return the lock to be used for serializing module operations.
*/
final ReadWriteLock getLock() {
return mLock;
}
/**
* Returns module info describing the current state of the module.
*
* @param inInitiatedFlows the set of IDs for the data flows that this
* module has initiated
* @param inParticipatingFlows the set of IDs for the data flows that this
* module is participating in.
*
* @return the module info.
*/
final ModuleInfo getModuleInfo(DataFlowID[] inInitiatedFlows,
DataFlowID[] inParticipatingFlows) {
return new ModuleInfo(getURN(), getState(), inInitiatedFlows,
inParticipatingFlows, getCreated(), getStarted(),
getStopped(), isAutoStart(), isAutoCreated(),
this instanceof DataReceiver, this instanceof DataEmitter,
this instanceof DataFlowRequester, getLastStartFailure(),
getLastStopFailure(), mLock.getReadLockCount(),
mLock.isWriteLocked(), mLock.getQueueLength());
}
private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
private volatile boolean mAutoCreated = false;
private volatile ModuleState mState = ModuleState.CREATED;
private volatile String mLastStartFailure;
private volatile String mLastStopFailure;
private volatile Date mStarted = null;
private volatile Date mStopped = null;
private final Date mCreated = new Date();
private final ModuleURN mURN;
private final boolean mAutoStart;
}