package com.github.atemerev.hollywood;
import com.github.atemerev.pms.listeners.MessageListener;
import java.util.concurrent.Executor;
/**
* Actor is the base class for all your own actors. You have to extend this class and provide <em>common interface</em>
* and <em>states</em> (with implementation of common interface methods).
* <p/>
* You can't instantiate an Actor on your own since it's <b>always</b> an abstract class and doesn't provide
* constructors. Instead, you should use following initialization call:
* <p/>
* <code>SomeActor actor = Hollywood.createActor(SomeActor.class)</code>
* <p/>
* Actor's base interface methods have the following signature:
* <p/>
* <code>public abstract Type method(args) [throws SomeException...]</code>
* <p/>
* An actor normally has multiple states (well, you could get with a single state, but then you don't really need
* a full-scaled actor model, right?)
* <p/>
* States are implemented via internal classes <b>extending the current Actor subclass</b>. Thus if you have a
* SomeActor implementation, your internal classes should have the following signature:
* <p/>
* <code>@State public static abstract class SomeInternalState extends SomeActor</code>
* <p/>
* (It's long and strange, but it has to be this way; it's Java, after all).
* <p/>
* One of the states should be marked with <code>@Initial</code> annotation -- it will be set right after the
* initalization is complete. It's there you should set actor's dependencies and properties -- and you don't want
* to define any additional behavior (as in implementing common interface methods -- except, maybe, the one method
* that changes the state (see <code>setState(SomeState.class)</code> method).
* <p/>
* Other states can implement other methods of common interface. If some methods are not implemented (which is
* possible since state subclasses are defined as abstract) -- they will throw UnsupportedOperationException on
* attempt to call it from this specific state.
* <p/>
* For convenience, common interface methods can be marked with <code>@AllowedStates</code> annotation with a list
* of state classes where this method is actually implemented. Currently, this annotation is ignored by Hollywood
* framework and serves for documentation purposes only.
* <p/>
* Within the state methods (and only there), state can be changed with setState() method. If additional dependencies
* should be set for specific state, it has to be done after <code>prepareState()</code> method call. Also, it's
* <em>highly</em> recommended to change state in the last statement of the method (except for return). Otherwise you
* have to be a certified magician.
* <p/>
* Commands which may change state <em>must</em> have the <code>synchrinized</code> modifier (to ensure that the order
* of state transitions corresponds to the order the commands were called). Basic actor thread-safety (i.e. state
* transition would wait for all running methods of the state to complete) is guaranteed by internal mechanisms, but
* you still have to do your actor-specific synchronization manually if necessary. Synchronization bugs in Actors can
* be really weird, so you are warned.
*
* @author Alexander Temerev, Alexander Kuklev
* @version $Id$
*/
public abstract class Actor extends RootState implements MessageListener {
/**
* TODO
*
* @return Actor object instance.
*/
protected abstract Actor me(); // Method will be hot-swapped in run-time.
/**
* Get a dummy object with current state class so it can be checked with <code>instanceof</code> if needed.
* Do not attempt to call business methods from this object -- it won't work!
*
* @return Dummy object with current state class.
*/
public abstract RootState state(); // Method will be hot-swapped in run-time.
/**
* Get dummy object with <em>previous</em> state class to be checked with <code>instanceof</code>.
*
* @return Dummy object with previous state class.
*/
public abstract RootState prevState(); // Method will be hot-swapped in run-time.
/**
* Initialize state from specified state class.
* This method is necessary if you need to set state dependencies before switching to it.
*
* @param stateClass State class.
* @return State object.
*/
protected abstract <T extends RootState> T prepareState(Class<T> stateClass);
// Method will be hot-swapped in run-time.
/**
* Sets new state. This method can be called only within the state itself.
* <p/>
* The state object should first be retrieved with <code>prepareState</code> method
* and all dependencies has to be set beforehand:
* <pre>
* newState = prepareState(SomeState.class);
* newState.field = value;
* newState.otherField = othervalue;
* setState(newState);
* return;
* </pre>
* <p/>
* Note that fields which are common in present state and target state by some common ancestor
* will be transported automatically.
* <p/>
* After the new state is set, you should immediately return, because the running context of the method
* is invalidated. You may return some constant, some variable, or the result of some method in the new
* state. If you want to conclude state change by some other actions, write a member in the new state
* and call it by
* <pre>
* return setState(SomeState.class).methodConclusion();
* </pre>
* If you don't want strange heisenbugs, please obey this rule.
*
* @param state Initialized state object.
* @return State set. The same as argument in most cases, null if the state was changed while onExit()s and onEnter()s were called.
*/
protected abstract <T extends RootState> T setState(T state); // Method will be hot-swapped in run-time.
/**
* Sets new state. Used when no fields of the new state must be initialized, i.e. just shorthand for
* <code>setState(prepareState(state))</code>
*
* @param state State class to set.
* @return State set.
*/
protected final <T extends RootState> T setState(Class<T> state) {
return setState(prepareState(state));
}
/**
* States can react to PMS messages. Actor is a DispatchListener itself, and can act as listener delegate
* in a common way. See PMS documentation for details.
*
* @param o Message object.
*/
public abstract void processMessage(Object o); // Method will be hot-swapped in run-time.
/**
* You can provide your own executor for PMS message handling by overriding this method.
*
* @return An Executor instance.
*/
protected Executor asyncListenerExecutor() {
return null;
}
}