All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.coos.javaframe.StateMachine Maven / Gradle / Ivy

/**
 * COOS - Connected Objects Operating System (www.connectedobjects.org).
 *
 * Copyright (C) 2009 Telenor ASA and Tellu AS. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 *
 * You may also contact one of the following for additional information:
 * Telenor ASA, Snaroyveien 30, N-1331 Fornebu, Norway (www.telenor.no)
 * Tellu AS, Hagalokkveien 13, N-1383 Asker, Norway (www.tellu.no)
 */
package org.coos.javaframe;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import org.coos.actorframe.application.Container;
import org.coos.javaframe.messages.AFPropertyMsg;
import org.coos.javaframe.messages.ActorMsg;
import org.coos.javaframe.messages.JFConstants;

/**
 * The StateMachine is an implementation of finite machine on the EJB platform.
 * This class is an abstract super class, that should not be changed by the
 * application developer (AD). The main function for this class is to receive
 * JMS messages from an attached JMS queue, read the state data for the receving
 * state machine instance from the entity bean, execute the transition, write
 * back the state data and at last make an trace object of the transition.
 * 

*

* The class contains a set of methods that must be implemented by the subclass: *

    *

    *

    * Following methods may be used by the application designer *

      *
    • {@link #sendMessage(ActorMsg,ActorAddress)} *
    • {@link #stopTimer(String)} *
    *

    * Following methods may be overloaded by the application designer. The * overloaded methods must call its super.method *

      *
    • {@link #initStateMachine()} *
    • {@link #initInstance()} *
    *

    * * @author Geir Melby, Tellu AS * @see State * @see CompositeState */ // to change actor class public class StateMachine implements Schedulable, Actor, JFConstants { public int noOfRRMessages = 3; public ActorAddress senderOfReportRequest; // Start of definition of persistentSession state data that needs to be // stored in entity beans public String myActorId; // the name of actor instance, used also as the // primary key for the corresponding entity bean public String myActorType; // The type of the state macghine public String myActorDomain; public MailBox saveQueue; // Queue of signals saved in currentState. // When State::nextState() is called, the signals in saveQueue are // moved back in front of the messageBox. protected String currentStateId; // An unique id of the state machine instance public Hashtable activeTimers; // Reference to each active timer is stored in this table hashed with an id // that the // user can use to cancel a timer // Temporary variables // protected State currentState; // current state of the startPage actor // instance protected State nextState; protected ActorMsg currentMessage; // keeps the reference to the entity bean that keeps the state data for // current state machine protected State initialState; // the initial state that all instances of this state machine share set in // the State class "enterstate" method public TraceObject trace; // trace of the transistion public Scheduler scheduler; // reference to the scheduler boolean readyToBeDeleted = false; // set during the transition if the state machine shall be deleted after the // transition is finished public boolean performExitIsDone = false; // Set in performExit method and // used in exec() method to // check inconsistency public boolean sameStateIsDone = false; // Set in sameState() method and // used in save() method to check if // it shall not save public boolean exitStateIsDone = false; // set in the exitState method to // flag that this method has been // called, used by framework public boolean saveDone = false; // this flagf is set during the save // operation to avoid further signal // trigging in sub types public int entryNo = 0; // used in the entering the next state // Variables that are common for all instances of the State Machine. These // are set at initiation time of the // message bean and are therefore equal for all mesage beans of the same // type. protected CompositeState myCompositeState; // my outermost composite state // context for the message bean, set in the {@link // #setMessageDrivenContext(MessageDrivenContext)} // Queue of signals used by the simulator, is set the {@link // #setMailBox(MailBox)} called by the {@link Scheduler} public MailBox mailbox; private static Timer jfTimer; // private boolean visible = false; // added properties from ActorSM public ActorSpec actorSpec; public Hashtable ports = new Hashtable(); // contains the port address that // this ActorStateMachine may // contain public static int UNIQ_ID = 0; // unique actor id used when new actors are // generating with no id specified public ActorContext context; // ActorContext object of this ActorSM // properties copied from Context class // private ActorAddress myAddress; private boolean isPersistent = false; // used for creation of parts and ports private int noOfTrialsLeft = 3; // initially set to 3 private boolean visible; private int traceLevel; private boolean traceParts; /** * Containes the references to the composite states installed. */ private Hashtable stateMachines; /** * Contains current state for each composite machine installed. */ private Hashtable currentStates; /** * True if the state machine is under Junit test. This because then output * messages are send also when no connectors are set. Makes the testing * easier */ private boolean testing = false; private static ActorAddress proxyAddress = null; /** * Constructor to create an state machine */ public StateMachine() { currentStates = new Hashtable(); stateMachines = new Hashtable(); currentMessage = null; currentStateId = null; saveQueue = new MailBox(); mailbox = new MailBox(); activeTimers = new Hashtable(); } public StateMachine(CompositeState compositeState) { this(); setBehaviorClass(compositeState); } // ************************ System utitillity methods , not used by the // programmer public boolean useProxy() { return proxyAddress != null; } public void setProxyAddress(ActorAddress proxyAddress) { this.proxyAddress = proxyAddress; } public ActorAddress getProxyAddress() { return proxyAddress; } public void setBehaviorClass(CompositeState myCompositeState) { this.myCompositeState = myCompositeState; } public CompositeState getBehaviorClass() { return myCompositeState; } public boolean isTesting() { return testing; } public void setTesting(boolean testing) { this.testing = testing; } public ActorSpec getActorSpec() { return actorSpec; } public void setActorSpec(ActorSpec actorSpec) { this.actorSpec = actorSpec; } public ActorAddress getMyActorAddress() { ActorAddress adr = new ActorAddress(myActorId, myActorType); if (adr.getActorDomain() == null) adr.setActorDomain(myActorDomain); return adr; } protected static Timer createTimer() { if (jfTimer == null) { jfTimer = new Timer(); } return jfTimer; } /** * Returns the id used as key in the statemachines and currentStates * * @param actorId is the actor id of this state machine * @return the key */ protected String getStateMachineId(String actorId) { int index = actorId.indexOf("."); if (index == -1) { return actorId; } return actorId.substring(index + 1); } public boolean containsStateMachine(String id) { return stateMachines.containsKey(id); } protected boolean isRole(String actorId) { return actorId.indexOf(".") != -1; } public CompositeState getStateMachine() { String str = getStateMachineId(myActorId); CompositeState cs = (CompositeState) stateMachines.get(str); if (cs == null) { throw new RuntimeException("CompositeState (behaviour class) not set. ActorId = " + myActorId); } return (CompositeState) stateMachines.get(str); } public CompositeState getStateMachine(String stateMachineId) { return (CompositeState) stateMachines.get(stateMachineId); } public void addStateMachine(String stateMachineId, RoleCS stateMachine) { if (!stateMachines.containsKey(stateMachineId)) { stateMachine.setParentAddress(getMyActorAddress()); stateMachines.put(stateMachineId, stateMachine); } } public String getMyActorDomain() { return myActorDomain; } public void setMyActorDomain(String myActorDomain) { this.myActorDomain = myActorDomain; } public void setMyActorId(String myActorId) { this.myActorId = myActorId; } public void setMyActorType(String myActorType) { this.myActorType = myActorType; } public TraceObject getTrace() { return trace; } public void setVisible(boolean visible) { this.visible = visible; } public void setNextState(State state) { currentStates.put(getStateMachineId(myActorId), state); } public void setCurrentState(CompositeState state) { currentStates.put(getStateMachineId(myActorId), state); } public Hashtable getStateMachines() { return stateMachines; } public Container getContainer() { return scheduler.getSchedulerData().getContainer(); } /** * Returns the specification of the application * * @return the application spec for this application */ public ApplicationSpec getApplicationSpec() { return scheduler.getSchedulerData().getApplicationSpec(); } /** * /** Create the context for an instance of this state machine in current * context. * * @return A context string that makes the actorid unique */ public String getContextString() { if (this.context.getActorAddress() != null) // return this.context.getActorAddress().getActorID() + "/"; return myActorId + "/"; else return "/"; } protected boolean isCreateMsg(ActorMsg msg) { return msg instanceof AFPropertyMsg && msg.equals(ROLE_CREATE_MSG) || msg.equals(ROLE_PLAY_MSG); } public ActorContext getContext() { return context; } public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; this.trace = scheduler.getTraceObject(); } public MailBox getMailbox() { return mailbox; // To change body of implemented methods use File | // Settings | File Templates. } public Scheduler getScheduler() { return scheduler; // To change body of implemented methods use File | // Settings | File Templates. } public boolean isSaveDone() { return saveDone; } public boolean isReadyToBeDeleted() { return readyToBeDeleted; } /** * This methos is called from createActor method in SchedulerImpl. The basic * properties are set as actorId, actorType */ public void init() { ApplicationSpec appSpec = scheduler.getSchedulerData().getApplicationSpec(); actorSpec = appSpec.getClonedActorSpec(getType()); scheduler.setTrace(appSpec.isTraceEnabled()); if (myCompositeState != null && myCompositeState instanceof RoleCS) { addStateMachine(myActorId, (RoleCS) myCompositeState); } initStateMachine(); // Initiate State machine execStartTransition(); // execute the start transition as enter state initNewInstance(); // initiate local variables unique for each instance } /** * Called from the scheduler, when the application is deleted. May be * overridden to to take special actions such as closing resources, deleting * threads etc. */ public void destroy() { stopAllTimers(); } /** * Called from the scheduler, when the application is paused. May be * overridden to to take special actions such as pausing threads etc. */ public void pause() { } public CompositeState getCompositeState() { return getStateMachine(); } /** * Reads the trace level which specifies what level of information that * shall be reported. The trace level is debug, info, warning and error. * * @return * @see org.coos.javaframe.TraceObject */ public int getTraceLevel() { return traceLevel; } /** * Sets the traceLevel as explained in {@link #getTraceLevel} * * @param traceLevel */ public void setTraceLevel(int traceLevel) { this.traceLevel = traceLevel; } /** * Writes out the instance identification and the the actor type as * "ole@user" */ public String toString() { return myActorId + "@" + myActorType; } public class JFTimerTask extends TimerTask { AFPropertyMsg thisMsg; public JFTimerTask(AFPropertyMsg thisMsg) { super(); // ActorAddress aa = new ActorAddress(myActorId, myActorType); // thisMsg.setReceiverRole(aa); this.thisMsg = thisMsg; } public void run() { sendMessage(thisMsg); if (thisMsg instanceof AFPropertyMsg) { activeTimers.remove(thisMsg.getString(JFConstants.TIMER_ID)); } } } public void startTimer(long duration, String timerId, ActorAddress receiver) { AFPropertyMsg tm = new AFPropertyMsg(TIMER_MSG, true); // todo forlag til endring AFPropertyMsg tm = new // AFPropertyMsg(timerId); tm.setProperty(JFConstants.TIMER_ID, timerId); tm.setReceiverRole(receiver); tm.setLong(JFConstants.DURATION, duration); startTimer(tm, duration, timerId); } public void startTimer(AFPropertyMsg tm, long duration, String timerId) { if (timerId == null || timerId.equals("")) { timerId = tm.toString(); } tm.setReceiverRole(getMyActorAddress()); tm.setProperty(JFConstants.TIMER_ID, timerId); if (activeTimers.containsKey(timerId)) { stopTimer(timerId); } JFTimerTask jfTT = new JFTimerTask(tm); activeTimers.put(timerId, jfTT); Timer timer = createTimer(); timer.schedule(jfTT, duration); trace.traceTask("Timer: " + timerId + " Duration: " + duration / 1000 + " seconds"); return; } public void startTimer(long duration, String timerId) { startTimer(new AFPropertyMsg(TIMER_MSG, true), duration, timerId); // todo forlag til endring startTimer(new AFPropertyMsg(timerId), // duration, timerId); return; } public boolean stopTimer(String timerId) { boolean returnValue = false; if (timerId == null || timerId.equals("")) { trace.traceWarning("Scheduler.JFTimerTask.stopTimer: Timer: " + timerId + " illegal timerId, timer not removed"); } else { // tider id ok JFTimerTask t = (JFTimerTask) activeTimers.remove(timerId); if (t == null) { trace.traceTask("Scheduler.JFTimerTask.stopTimer: Timer: " + timerId + " timer does exist"); } else { // timer task exists if (t.cancel()) { trace.traceTask("Timer: " + timerId.trim() + " removed"); returnValue = true; } else { trace.traceTask("Scheduler.JFTimerTask.stopTimer: Timer: " + timerId.trim() + " not removed"); } } } return returnValue; } public Hashtable getActiveTimers() { return activeTimers; } /** * All messages in the savequeue are resent to the state machine instance if * the savequeue is not empty and the state is changed. */ void runSaveQueue() { if (nextState != null) { MailBox tmp = new MailBox(); if (saveQueue != null) { tmp.moveMailBox(saveQueue); // move all messages to this this // tmp queue } ActorMsg am = (ActorMsg) tmp.removeFirst(); // get first message while (am != null) { processMessage(am); am = (ActorMsg) tmp.removeFirst(); } } } /** * Process an incomming message to an incomming port for this actor. The * message is send to the actor address. The hashtable ports contains an * actoradress for each port. Each port can only have one address, that * means only one connector. * * @param msg is the message to be send */ protected boolean processPortMessage(ActorMsg msg) { boolean ok = false; myActorId = msg.getReceiverRole().getActorID(); myActorType = msg.getReceiverRole().getActorType(); String portName = msg.getReceiverRole().getActorPort(); ActorAddress receiverAddress = (ActorAddress) ports.get(portName); // if (receiverAddress != null && // context.getChildrenRole(msg.getReceiverRole().getActorID()) != null) // { if (receiverAddress != null) { // ActorAddress aa = // context.getChildrenRole(receiverAddress.getActorID()); msg.setReceiverRole(receiverAddress); ok = output(msg); } else { trace .traceOut(TraceConstants.tlError, trace.getTraceHeader() + "The port " + portName + " does not exists"); } return ok; } /** * Process the incoming message. If the actor message RoleCreateMsg is * received, an new actor instance is created before the exec method is * called which will treat the message. * * @param msg is the actor message. * @see ActorMsg */ public boolean processMessage(ActorMsg msg) { currentMessage = msg; myActorId = msg.getReceiverRole().getActorID(); // tHe instance variable myActorType = msg.getReceiverRole().getActorType(); readyToBeDeleted = false; // reset to false before transition Changed // 08.10.2003 trace.traceInit(this); trace.setInputSignal(msg); /* Added trace requests here */ // check if receiver is a role / state machine State ps = getCurrentState(); if (ps == null) { CompositeState cs = getStateMachine(getStateMachineId(myActorId)); if (cs != null) { cs.enterState(this); } else { if (scheduler.isTraceOn()) trace.traceError("No state machine found for id: <" + myActorId + ">"); return false; } } ps = getCurrentState(); if (ps != null) { try { exec(msg, this); // execute the transition } catch (Exception e) { e.printStackTrace(); if (scheduler.isTraceOn()) trace.traceError("Something went seriously wrong during execution. Performing Rollback: " + e.toString() + " " + e.getMessage()); getContainer().displayError(e); if (performExitIsDone) { // Exception occurred after the perforExit was called, Then // the curr state will be wrong, and exception next time // will occur setNextState(ps); } return false; } if (scheduler.isTraceOn() && trace.traceLevel <= TraceConstants.tlDebug) { trace.traceOut(TraceConstants.tlDebug, trace.toString()); } } else { if (scheduler.isTraceOn()) trace.traceError(" Current state is not set"); return false; } currentMessage = null; runSaveQueue(); return true; } /** * Checks if the Statemachine is visible. The sub type of State machine * shoud override this method. An actor has a visible flag that is set to * true if the actors shoiuld be visblie otside current domain * * @return true if the actor shall be visible in the router system */ public boolean isVisible() { return visible; } /** * Overloaded by substate machine to initialize specific attributes of a new * instance. This method is called when a new instance is created. */ protected void initInstance() { } protected void destroyInstance() { } /** * Called from process message when new instance is created */ private void initNewInstance() { this.context = new ActorContext(new ActorAddress(myActorId, myActorType)); this.context.setMyParentAddress(new ActorAddress(myActorId, myActorType)); // set // the // parent // address // gn initInstance(); } public String getType() { return this.myActorType; } public String getID() { return this.myActorId; } /** * Prints out the state data of an instance excluded the framework data * * @return a string of the state data */ public String printStateData() { return ""; } /** * Initiate this syte machine, may be overridden by the sub states */ protected void initStateMachine() { } /** * Get the current State of this StateMachine. * * @return The current State */ public final State getCurrentState() { return (State) currentStates.get(getStateMachineId(myActorId)); } protected Hashtable getCurrentStates() { return currentStates; } protected final void setInitialState(State state) { currentStates.put(getStateMachineId(myActorId), state); } public void setCurrentMessage(ActorMsg currentMessage) { this.currentMessage = currentMessage; } /** * Get the current ActorMsg of this StateMachine. * * @return The current ActorMsg */ public final ActorMsg getCurrentMessage() { return currentMessage; } public void setPartSpecs(Vector partSpecs) { this.actorSpec.setPartDesc(partSpecs); } public void setPortSpecs(Vector portSpecs) { this.actorSpec.setPortDesc(portSpecs); } /** * Code to be executed at startup of this StateMachine. */ protected void execStartTransition() { getStateMachine().enterState(this); initialState = getCurrentState(); // initiate the default initial state currentStateId = initialState.stateName(); } /** * Restart this StateMachine. Note: the content of the messageBox is kept. */ public void restart() { do { getCurrentState().exit(this); setNextState(getCurrentState().enclosingState); } while (null != getCurrentState()); } /** * The treatment by this StateMachine of the next message from the mailbox. * Default action is skipping of the signal. An invariant is that the signal * != null * * @param sig is the input message to this state machine instance. */ protected void exec(ActorMsg sig, StateMachine curfsm) { nextState = null; entryNo = 0; // set default entry no, if nor changed by nextState(), // default enterState is called State currState = getCurrentState(); State st = currState; trace.setCurrentState(currState.stateName()); CompositeState enclSt = currState.enclosingState; if (enclSt == null) { if (scheduler.isTraceError()) trace.traceOut(TraceConstants.tlError, trace.getTraceHeader() + "State is null!"); return; } saveDone = false; do { performExitIsDone = false; sameStateIsDone = false; exitStateIsDone = false; enclSt.curfsm = this; enclSt.execTrans(sig, currState, curfsm); // execute the transition // check first if exitState has been called, if so call the exit() // method if (exitStateIsDone) { if (performExitIsDone) { currState.exit(curfsm); } else { throw new ActorFrameException("CurState: " + currState.getFullStateName() + " PerformExit() is not called before exitState() is called in execTrans()"); } } // check if nextState has been set if (nextState != null && performExitIsDone) { // call the enterstate method dependent of type of the next // state // setNextState(nextState); if (nextState instanceof CompositeState && entryNo != 0) { ((CompositeState) nextState).enterState(entryNo, this); } else { nextState.enterState(this); } if (currState == null || currState instanceof CompositeState) { if (scheduler.isTraceOn()) throw new ActorFrameException("Inconsisent current state of state machine id: " + (sig.getReceiverRole()).toString() + " Probably a mismatch of performExit() and enterState()"); } if (scheduler.isTraceOn()) trace.setNewState(nextState.stateName()); if (isPersistent()) try { storePersistentData(); } catch (IOException e) { e.printStackTrace(); } return; // sig has triggered a transistion (or save) } else { if (nextState == null && performExitIsDone) { trace.setNewState(currState.stateName()); throw new ActorFrameException("CurState: " + currState.getFullStateName() + " NextState != null, but performExit is not run"); } else if (nextState != null && !performExitIsDone) { String str = "CurState: " + currState.getFullStateName() + " NextState != null, but performExit is not run"; if (scheduler.isTraceOn()) trace.traceOut(TraceConstants.tlInfo, trace.getTraceHeader() + str); trace.setNewState(currState.stateName()); throw new ActorFrameException(str); } else if (sameStateIsDone) { trace.setNewState(currState.stateName()); return; } // the sigal has not triggered a transition st = enclSt; enclSt = st.enclosingState; } } while (enclSt != null && !saveDone); trace.setNewState(currState.stateName()); } /** * Sends the message to an ActorStateMachine. The actor address of the * sending actor is set in the message * * @param sig */ public final boolean output(ActorMsg sig) { if (sig.getReceiverRole() != null && sig.getReceiverRole().getActorType() != null) { if (sig.getSenderRole() == null) { sig.setSenderRole(getMyActorAddress()); // set the senderAddress // of this message } if (useProxy()) { sig.getReceiverRole().setProxyAddress(getProxyAddress()); //sig.setProxyAddress(getProxyAddress()); } scheduler.clearLastMsgFromRouter(); return scheduler.output(sig, this); } else { trace.traceWarning("Illegal Receiver: " + sig.messageContent()); scheduler.output(sig, this); } return false; } /** * Sends a message to the receiver address of the message. Utility routine * to be used by the action specification in the state machine. * * @param sig invariant: The receiver and sender addresses of sig are set */ public boolean sendMessage(ActorMsg sig) { return output(sig); } /** * Sends a message to the specified actor address. Utility routine to be * used by the action specification in the state machine. * * @param sig * @param aa The actor address that the message is sent to */ public boolean sendMessage(ActorMsg sig, ActorAddress aa) { if (aa == null) { throw new RuntimeException("ActorAddress is null"); } else { sig.setReceiverRole((ActorAddress) aa.clone()); // set the // senderAddress of // this message sig.setSenderRole(getMyActorAddress()); // set the senderAddress of // this return output(sig); } } /** * Sends a message to the specified actor address. Utility routine to be * used by the action specification in the state machine. * * @param signaName is the name of the message * @param aa The actor address that the message is sent to */ public boolean sendMessage(String signaName, ActorAddress aa) { AFPropertyMsg sig = new AFPropertyMsg(signaName); sig.setReceiverRole((ActorAddress) aa.clone()); // set the senderAddress // of this message sig.setSenderRole(getMyActorAddress()); // set the senderAddress of this return output(sig); } /** * Sends a message to the specified actor address. Utilility routine to be * used by the action specification in the state machine. * * @param sig * @param aa The actor address that the message is sent to * @param protocol specifies the transport protocol UDP or TCP to be used */ public boolean sendMessage(ActorMsg sig, ActorAddress aa, String protocol) { sig.setReceiverRole((ActorAddress) aa.clone()); // set the senderAddress // of this message sig.getReceiverRole().setProtocol(protocol); sig.setSenderRole(getMyActorAddress()); // set the senderAddress of this sig.getSenderRole().setProtocol(protocol); return output(sig); } /** * Sends the message to a vector of actor addresses. * * @param sigName is the name of actor message that hall be send * @param receiver is the receiver address. if not valid assume that the * receiver is a port */ public boolean sendMessage(String sigName, String receiver) { if (receiver.indexOf("@") != -1) { return sendMessage(new AFPropertyMsg(sigName), new ActorAddress(receiver)); } else { return sendMessage(new AFPropertyMsg(sigName), receiver); } } /** * Sends the message to a vector of actor addresses. * * @param sigName is the name of actor message that hall be send * @param v is the vector of actor addresses */ public boolean sendMessage(String sigName, Vector v) { return sendMessage(new AFPropertyMsg(sigName), v); } /** * Sends the message to a vector of actor addresses. * * @param sig is the actor message that hall be send * @param v is the vector of actor addresses */ public boolean sendMessage(ActorMsg sig, Vector v) { boolean res = true; if (v != null && v.size() > 0) { if (v.size() == 1) { ActorAddress aa = (ActorAddress) v.firstElement(); sig.setReceiverRole(aa); res = output(sig); } else { Enumeration e = v.elements(); while (e.hasMoreElements()) { ActorAddress aa = (ActorAddress) e.nextElement(); ActorMsg msg = sig.getCopy(scheduler.getClassLoader()); msg.setReceiverRole(aa); boolean ok = output(msg); res = res || ok; } } } return res; } /** * Sends the message to an arry of actor addresses. * * @param sig is the actor message that hall be send * @param actorAddresses is an arry of actor addresses */ public boolean sendMessage(ActorMsg sig, ActorAddress[] actorAddresses) { boolean res = true; if (actorAddresses != null) { if (actorAddresses.length == 1) { ActorAddress aa = actorAddresses[0]; sig.setReceiverRole(aa); res = output(sig); } else { for (int i = 0; i < actorAddresses.length; i++) { ActorAddress address = actorAddresses[i]; ActorMsg msg = sig.getCopy(scheduler.getClassLoader()); msg.setReceiverRole(address); boolean ok = output(msg); res = res || ok; } } } else res = false; return res; } /** * Sends the message to an arry of actor addresses. * * @param sig is the actor message that hall be send * @param actorAddresses is an arry of actor addresses */ public boolean sendMessage(ActorMsg sig, Object[] actorAddresses) { boolean res = true; try { for (int i = 0; i < actorAddresses.length; i++) { ActorAddress actorAddress = (ActorAddress) actorAddresses[i]; boolean ok = sendMessage(sig.getCopy(getScheduler().getClassLoader()), actorAddress); ; res = res || ok; } } catch (ClassCastException e) { throw new ClassCastException("Array of objects is not Actor addresses" + e.getMessage()); } return res; } /** * Sends the message to an arry of actor addresses. * * @param name is the name of message that hall be send * @param actorAddresses is an arry of actor addresses */ public boolean sendMessage(String name, Object[] actorAddresses) { return sendMessage(new AFPropertyMsg(name), actorAddresses); } /** * Sends a message to another actor via a port name. The port is set up at * initiation of actor, normaly using actor deployment descriptors. * * @param am ActorMsg to send. * @param portName the port id. This has to be the same name as defined in the * descriptor file. */ public boolean sendMessage(ActorMsg am, String portName) { ActorAddress portAddress = getPortAddress(portName); if (portAddress == null) { if (scheduler.isTraceOn()) trace.traceError("Illegal port name: " + portName + " Message: " + am.getMsgId()); return false; } if (portAddress.getActorID() == null) { // no specific instance specified, get the first instance of the // specified type // get all instances Enumeration en = scheduler.getSchedulerData().getMySystem().elements(); while (en.hasMoreElements()) { Schedulable sm = (Schedulable) en.nextElement(); String type = sm.getMyActorAddress().getActorType(); if (type.equals(portAddress.getActorType())) { portAddress.setActorID(sm.getMyActorAddress().getActorID()); break; } } } // send the mesaeg only if the receiver address is legal if (portAddress.isValied()) { am.setSenderRole(getMyActorAddress()); return sendMessage(am, portAddress); } return false; } /** * Sets a flag ton indeicate that this instance of the state machine shall * be deleted when the transition is finished. */ public void setReadyToBeDeleted() { readyToBeDeleted = true; } /** * Reads the parts specification for this actor * * @return the actor description */ public ActorSpec readActorDescription() { if (getType() != null) { return scheduler.getSchedulerData().getApplicationSpec().getActorSpec(getType()); } return null; } /** * Search the PartSpecs table for a specific part. If it exist the partSpec * is returned. This method is called from the ActorStateMachine protocol * when new roles are requested. Not used by the application developer. * * @param roleType The actor type to be serched for. * @param roleSpecs Contains the specification of inner parts for a specific actor * type * @return The partspec for this roleType. */ public PartSpec findRoleSpec(String roleType, Vector roleSpecs) { for (int i = 0; i < roleSpecs.size(); i++) { PartSpec parttemp = (PartSpec) roleSpecs.elementAt(i); String tmp = parttemp.getRoleType(); if (roleType.equals(tmp)) { return parttemp; } } return null; } public Vector getPartSpecs() { return actorSpec.getPartDesc(); } public Vector getPortSpecs() { return actorSpec.getPortDesc(); } /** * Creates the inner parts (actors) that shall exists at creation time of * the enclosing actor. Not used by the application developers. Create the * part only if it */ public boolean createParts() { boolean childrenCreated = false; String s = "INNER PARTS: "; Vector partSpecs = actorSpec.getPartDesc(); for (int i = 0; i < partSpecs.size(); i++) { PartSpec ps = (PartSpec) partSpecs.elementAt(i); s = s + ", " + ps.toString(); ActorAddress aa; // Check if the actor spec exists ActorSpec spec = getApplicationSpec().getActorSpec(ps.getRoleType()); if (spec == null) { if (scheduler.isTraceOn()) trace.traceOut(TraceConstants.tlError, " Scheduler.createParts:" + " ActorStateMachine spec" + ps.getRoleType() + " does not exists"); continue; } // if the bind property is set (Instance id for the actor that // implements this part), // an instance of the actor shall not be created if (ps.getBind() != null) { if (scheduler.isTraceOn()) trace.traceOut(TraceConstants.tlInfo, " Scheduler.createParts:" + " ActorStateMachine spec: " + ps.getRoleType() + " is bind to: " + ps.getBind()); continue; } int noOfExistingRoles = context.getChildrenRoles(ps.getRoleType()).size(); if (ps.getLow() - noOfExistingRoles > 0) { for (int j = noOfExistingRoles; j < ps.getLow(); j++) { Vector ports = getApplicationSpec().getActorSpec(ps.getRoleType()).getPortNames(); AFPropertyMsg pm = createRoleCreateMsg(ps, ports); // check also if it exists a role name for that particular // role instance if (ps.getRoleNames() == null || ps.getRoleNames().length <= j) { aa = new ActorAddress( getContextString() + ps.getRoleType() + UNIQ_ID++ + "Set" + ps.getSetNo(), ps .getRoleType()); } else { aa = new ActorAddress(getContextString() + ps.getRoleNames()[j], ps.getRoleType()); } // added GM 18.11.04 // set remote actor, null if it not exists, read from the // actor descriptors // Send only the creation msg to actors that if (ps.getActorDomain() != null) { aa = ps.getActorDomain(); String id = getContextString() + ps.getRoleNames()[j]; ActorAddress targetActor = new ActorAddress(id, ps.getRoleType()); pm.setProperty(ROLE_CREATE_MSG_TARGET_ACTOR, targetActor); context.getCreationOfChildren().addElement(targetActor); } else { context.getCreationOfChildren().addElement(aa); } Vector v = actorSpec.getConnectorDesc(ps.getRoleType(), null); v = rewriteContextualConnectors(v); pm.setProperty(ROLE_CREATE_MSG_CONNECTORS, v); this.sendMessage(pm, aa); // send the message childrenCreated = true; } } } if (scheduler.isTraceOn()) trace.traceOut(TraceConstants.tlInfo, trace.getTraceHeader() + s); return childrenCreated; } /** * Create parts listed as strings of format in * vector v. The actor id and actor type has to be read from this string. * * @param v contains strings of format which is * the key of ActorAddresses of actors that has not responed on * RoleCreate message * @return true if parts has been created */ public boolean createParts(Vector v) { boolean childrenCreated = false; for (int i = 0; i < actorSpec.getPartDesc().size(); i++) { PartSpec ps = (PartSpec) actorSpec.getPartDesc().elementAt(i); // check if v contains this actor type for (int j = 0; j < v.size(); j++) { // Read the key string, which has to be converted to // ActorAddress Vector ports = getApplicationSpec().getActorSpec(ps.getRoleType()).getPortNames(); ActorAddress aa; AFPropertyMsg pm = createRoleCreateMsg(ps, ports); aa = (ActorAddress) v.elementAt(j); String s = aa.key(); String actorId; if (s.endsWith(ps.getRoleType())) { // the key ends with actortype, calculate the id string actorId = s.substring(0, s.indexOf("@")); aa = new ActorAddress(actorId, ps.getRoleType()); if (ps.getActorDomain() != null) { aa = ps.getActorDomain(); String id = getContextString() + ps.getRoleNames()[j]; ActorAddress targetActor = new ActorAddress(id, ps.getRoleType()); pm.setProperty(ROLE_CREATE_MSG_TARGET_ACTOR, targetActor); // add the actor only if it is not there again // context.getCreationOfChildren().addElement(targetActor); } Vector con = actorSpec.getConnectorDesc(ps.getRoleType(), null); v = rewriteContextualConnectors(con); pm.setProperty(ROLE_CREATE_MSG_CONNECTORS, con); sendMessage(pm, aa); // send the message childrenCreated = true; } } } return childrenCreated; } /** * Creates an RoleCreateMsg * * @param ps is the part spec of the part * @param ports is the port names for the part * @return an RoleCreateMsg */ protected AFPropertyMsg createRoleCreateMsg(PartSpec ps, Vector ports) { AFPropertyMsg pm = new AFPropertyMsg(ROLE_CREATE_MSG, true); pm.setProperty(ROLE_CREATE_MSG_PORTS, ports); pm.setProperty(ROLE_CREATE_MSG_CONNECTORS, actorSpec.getConnectorDesc(ps.getRoleType(), null)); pm.setBoolean(ROLE_CREATE_MSG_VISIBLE, ps.isVisible()); pm.setInt(TRACE_LEVEL_PROP, ps.getTraceLev()); return pm; } /** * Create the Port instances belonging to a StateMachine and add addresses * to target Port/actor for each of them. If the port exists already the * port is not changed * * @param connectors */ public boolean createPorts(Vector connectors) { if (ports == null) { ports = new Hashtable(); } // Vector con = rewriteContextualConnectors(connectors); boolean createdPorts = false; // this.ports.clear(); // Create all port instances with their configuration Enumeration enumer = connectors.elements(); while (enumer.hasMoreElements()) { ConnectorSpec connectorSpec = (ConnectorSpec) enumer.nextElement(); // Add it to the port list [from StateMachine] if (!ports.containsKey(connectorSpec.getFrom())) { if (connectorSpec.getFrom().getActorPort() != null) { ports.put(connectorSpec.getFrom().getActorPort(), connectorSpec.getTo()); createdPorts = true; } } } return createdPorts; } /** * Rewrite an actor address to be correct in this context * * @param connAddr the addrss to be rewritten to right context * @param childrenAddrs are all the children inside this context * @return */ protected ActorAddress getContextAddress(ActorAddress connAddr, Vector childrenAddrs) { ActorAddress res = (ActorAddress) connAddr.clone(); if (res.getActorID() == null) { // Undefined ActorID, try to find one. if (connAddr.getActorType().equals(myActorType)) { // bind to this actor. res.setActorID(myActorId); } else { // search through the children to be created // to find a correct instance. Enumeration children = childrenAddrs.elements(); while (children.hasMoreElements()) { ActorAddress child = (ActorAddress) children.nextElement(); if (child.getActorType().equals(connAddr.getActorType())) { // found matching actor. use this actorId res.setActorID(child.getActorID()); break; } } } } else { // include the context res.setActorID(getContextString() + connAddr.getActorID()); } if (res.getActorPort() == null || res.getActorPort().equals("")) { res.setActorPort(null); // "defaultInPort" } return res; } /** * Rewrites the ActorAddresses in the Vector to include contextual * information in the ActorID field. If the ActorID is null it searches a * list of inner parts for this actor with correct ActorType. * * @param connectors Vector of CONNECTORS for connector requests * @return conectors bound to this context */ public Vector rewriteContextualConnectors(Vector connectors) { Vector childrenAddrs = getAllChildrenOfThisType(myActorType); Vector res = new Vector(); for (int i = 0; i < connectors.size(); i++) { ConnectorSpec cs = (ConnectorSpec) connectors.elementAt(i); // check if the bind property is set. If so, use this as the target // actor instance id ActorAddress to = cs.getTo(); PartSpec toPart = actorSpec.getPartSpec(to.getActorType()); if (toPart != null && toPart.getBind() != null) { // use the bind poroperty. The receiving part is already running to.setActorID(toPart.getBind()); } else { to = getContextAddress(cs.getTo(), childrenAddrs); } ActorAddress from = getContextAddress(cs.getFrom(), childrenAddrs); ConnectorSpec newSpec = new ConnectorSpec(from, to, cs.isBidirectional()); res.addElement(newSpec); } return res; } private Vector getAllChildrenOfThisType(String actorType) { // get all children of this type. Vector all = context.getChildrenRoles(); Enumeration enumer = context.getCreationOfChildren().elements(); while (enumer.hasMoreElements()) { all.addElement(enumer.nextElement()); } return all; } // Check if creation of a new instance is permitted public boolean checkMaxLimit(PartSpec p) { if (context.sizeOfChildrenRoles(p.getRoleType()) < p.getHigh()) return true; else return false; } /** * releaseAllAssociations releases all association which involves this role. * A RoleReleaseMsg is sent on each association. */ public void releaseAllAssociations() { Vector req = context.getRequestorRoles(); for (Enumeration e = req.elements(); e.hasMoreElements();) { ActorAddress aa = (ActorAddress) e.nextElement(); this.sendMessage(new AFPropertyMsg(ROLE_RELEASE_MSG, true), aa); } req = context.getRequestedRoles(); for (Enumeration e = req.elements(); e.hasMoreElements();) { ActorAddress aa = (ActorAddress) e.nextElement(); this.sendMessage(new AFPropertyMsg(ROLE_RELEASE_MSG, true), aa); } context.getRequestedRoles().removeAllElements(); } /** * releaseChildren releases all children roles of this ActorSM. A * RoleResetMsg is sent to each children role, and unless the children role * is persistent, it is removed from the ActorContext of this actor. */ public void releaseChildren() { Vector children = context.getChildrenRoles(); for (Enumeration e = children.elements(); e.hasMoreElements();) { ActorAddress childAA = (ActorAddress) e.nextElement(); if (context.isPersistentChildrenRole(childAA)) { AFPropertyMsg pm = new AFPropertyMsg(ROLE_RESET_MSG, true); pm.setSenderRole(new ActorAddress(myActorId, myActorType)); this.sendMessage(pm, childAA); /* * RoleResetMsg resm = new RoleResetMsg(); pm.setSenderRole(new * ActorAddress(myActorId, myActorType)); this.sendMessage(resm, * childAA); */ } else { AFPropertyMsg pm = new AFPropertyMsg(ROLE_REMOVE_MSG, true); this.sendMessage(pm, childAA); // RoleRemoveMsg resm = new RoleRemoveMsg(); pm.setSenderRole(new ActorAddress(myActorId, myActorType)); this.sendMessage(pm, childAA); context.remove(childAA); } } } /** * Sends RoleRequestMsg to an actor for chatting a role with shall be named * roleType and shall be of type roleType. If is does not exist, it will be * created. Called by application developer. * * @param aa ActorAddress to send the request to * @param roleId The actor id to request. * @param roleType The actor type that shall play the role. * @throws Exception */ public void sendRoleRequest(ActorAddress aa, String roleId, String roleType) throws Exception { AFPropertyMsg rrm = new AFPropertyMsg(ROLE_REQUEST_MSG, true); rrm.setProperty(ActorAddress.ROLE_ID, roleId); rrm.setProperty(ActorAddress.ROLE_TYPE, roleType); this.sendMessage(rrm, aa); } /** * Check if this actor can be removed. If it is not a persistent actor or it * contains no other associations, it can be deleted */ public boolean removeIfPossible() { if (context.isEmpty() && !isPersistent()) { destroyInstance(); sendMessage(new AFPropertyMsg(ROLE_PLAY_ENDED_MSG, true), context.getParentAddress()); stopAllTimers(); return true; } return false; } public void stopAllTimers() { if (!(activeTimers.isEmpty())) { Enumeration tmp = activeTimers.keys(); while (tmp.hasMoreElements()) { String s = (String) tmp.nextElement(); stopTimer(s); } } } /** * puts the message into the input queue of this state machine and notify * the sheduler to start the state machine * * @param am is the message send by the window "manager" */ public void executeTheEvent(ActorMsg am) { am.setReceiverRole(context.getActorAddress()); if (am.getSenderRole() == null) { am.setSenderRole(context.getActorAddress()); } synchronized (getScheduler()) { getMailbox().addMessage(am); getScheduler().notifyScheduler(); } } /** * puts the message into the input queue of this state machine and notify * the sheduler to start the state machine * * @param msgName is the message name send by the window "manager" */ public void executeTheEvent(String msgName) { executeTheEvent(new AFPropertyMsg(msgName)); } /** * Gets the history state. * * @param cs is the composite state * @return cs */ public State getHistoryState(CompositeState cs) { return cs.findCurrentState(context.getHistoryStateId()); } /** * Sets the historyState to be the provided State */ public void setHistoryState(State st) { if (st == null) { context.setHistoryStateId(null); } else { context.setHistoryStateId(st.stateName()); } } public String stripContext(String s) { return s.substring(s.lastIndexOf('/') + 1); } public boolean validateCredentials(AFPropertyMsg rrm) { return false; } // added methods from Context public boolean isPersistent() { return isPersistent; } /** * Sets this actor instance to be persistent or not. This method is called * by the {@link StateMachineCS} to force it to be deleted by setting the * persistent flag to false. This actor instance will then be deleted. * * @param persistent is true if the actor instance is persistent */ public void setPersistent(boolean persistent) { isPersistent = persistent; } private ActorAddress getPortAddress(String portName) { return (ActorAddress) ports.get(portName); } public int getNoOfTrialsLeft() { return noOfTrialsLeft; } public void setNoOfTrialsLeft(int noOfTrialsLeft) { this.noOfTrialsLeft = noOfTrialsLeft; } public void routerUpdate() { // This validateLicense through the midlet router, that search trough // the Sheduler.mySystem table for // visible actors /* * if (isVisible()) { Vector actors = new Vector(); * actors.addElement(getMyActorAddress()); sendMessage(new * ActorRouterRegMsg(actors, 0), new RouterAddress("ar", "ActorRouter", * "udp")); startTimer(new RouterUpdateTimerMsg(), * ROUTER_UPDATE_INTERVAL_LENGTH, ROUTER_UPDATE_INTERVAL_ID); } */ } public boolean isTraceParts() { return traceParts; } public void setTraceParts(boolean traceParts) { this.traceParts = traceParts; } protected byte[] storePersistentData() throws IOException { return new byte[0]; } protected ByteArrayInputStream restorePersistentData(byte[] data) throws IOException { return new ByteArrayInputStream(data); } public boolean isTraceOn() { return scheduler.isTraceOn(); } public boolean isTraceError() { return scheduler.isTraceError(); } /** * Sends the messages to children with acor addfress * @param am is the actor message * @param child is the actor address without the context * @return true if success */ public boolean sendToChild(ActorMsg am, ActorAddress child) { ActorAddress aa = context.getChildrenRoleIgnoreContext(child); if (aa != null) { sendMessage(am, aa); return true; } else { return false; } } /** * Sends the messages to all children * @param am is the actor message * @return true if success */ public boolean sendToChildren(ActorMsg am) { if (!context.getChildrenRoles().isEmpty()) { return sendMessage(am, context.getChildrenRoles()); } else { return false; } } public boolean sendToChild(ActorMsg am, String childType) { Vector v = context.getChildrenRoles(childType); if (v.size() == 1) { sendMessage(am, (ActorAddress) v.elementAt(0)); return true; } else { return false; } } public ActorMsg getCopyMsg(ActorMsg msg) { return msg.getCopy(getScheduler().getClassLoader()); } public AFPropertyMsg createRoleRequestMsg(String actorid, String actorType) { AFPropertyMsg pm = new AFPropertyMsg(ROLE_REQUEST_MSG, true); pm.setProperty(ROLE_REQUEST_MSG_ROLE_ID, actorid); pm.setProperty(ROLE_REQUEST_MSG_ROLE_TYPE, actorType); return pm; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy