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

org.coos.actorframe.Port 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.actorframe;

import org.coos.actorframe.messages.AFConstants;
import org.coos.javaframe.ActorAddress;
import org.coos.javaframe.ConnectorSpec;
import org.coos.javaframe.StateMachine;
import org.coos.javaframe.messages.AFPropertyMsg;
import org.coos.javaframe.messages.ActorMsg;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 * The Port class represents the UML 2.0 port construct. A Port object is owned
 * by an Actor instance.
 * 

* The Port object maintains connectors to other Port objects and is thus * responsible for requesting and removing these associations upon creation and * destruction. This is implemented in the execTrans() method. * Currently this is a state-less protocol for port-port and port-actor * communication. *

* The Port object routes ActorMsg messages between the connectors * upon message reception. This behavior is implemented by the * routeMessage method. The general behavior is such that messages * are routed according to the direction of the message, whether the message is * sent as a reply to a previous message (ActorMsg with replyStack) and the Port * flag isBehavior. For more details on this, see comments in * routeMessage(). *

* The Port is executed through it's exec(), and is called from * StateMachine.processPortMessage() upon reception of a message, * and from ActorSM.sendMessage(ActorMsg, String portName) upon * sending a message from usercode. *

* isBehavior: The Port is a behavior Port, and all messages received is * delivered to the owning actor. *

* hideInner: The Port masks the inner parts from other outer actors. * This is done by flushing the replyStack in the ActorMsg, and * setting this Port as the originator of the message. This enforces that a * reply must be sent through this Port on the way back. Furthermore, this * requires the Port to have well defined connectors to the inner parts, such * that a message received from an outer actor will be forwarded correctly to * the inner parts. *

* sessionEnabled: The Port enforces the use of sessions. A session is a * way of keeping unique state of the clients which are using the Port. This is * done when PortSM wants to make sure that messages are sent in a verifiable * manner with many clients connected to the Port. This requires an Actor which * uses a port with this flag to send a PathRequestMsg to the Port. This message * will propogate through the active path of connectors and reserve a session on * each of these Ports. Note that if sessionEnabled is used, all ports on the * path must have this flag enabled, otherwise the session-handling will be * rejected. *

* The Port can also have extended behavior through the PortSM * class. This is described by the ActorPortSpec as a portType attribute. If * this attribute is unspecified, the default PortSM implementation is used. * Otherwise it tries to use classloading to load the class specified. If the * full classname and packagename is specified by this string, this is loaded -- * otherwise it tries to search the actor.actotype package for the given * classname. *

* The Port class also implements some methods such as handling of Timers and * sending of ActorMsg which is to be used by the PortSM class and it's * successors. This is kept as equal as possible to the way the * StateMachine<->CompositeState combination is to be programmed by a user. * * @author Viggo Fredriksen */ public final class Port implements AFConstants { // Connector addresses private Vector outConnectors; // outer actor connectors. private Vector inConnectors; // inner actor connectors. private Vector requesterConnectors; // requesters of this port. private Vector createConnectors; // tmp for creation of connectors. private Vector routeQueue = new Vector(); // messages queued for routing. private Vector messageQueue = new Vector(); // messages queued for normal // sending private Hashtable createTimers; // timers created or removed during // execution. // Some enviromental addressing information public ActorAddress portAddress; public ActorAddress actorsAddress; public String name; private boolean isBehavior = false; // UML 2.0 isBehavior flag. private boolean hideInner = false; // Mask inner actors flag. private boolean sessionEnabled = false; // Use session flag private Hashtable clientSessions; // state data for sessions private PortSM protocolSM; // the port state machine private ActorAddress sender; // the senderAddress of PortCreateMsg and Add / // Remove connector /** * Default constructor for the Port. * * @param actorPortSpec Specification of the Port. * @param actorsAddress Owning Actor's address. */ public Port(ActorPortSpec actorPortSpec, ActorAddress actorsAddress) { // port information this.name = actorPortSpec.portName; this.isBehavior = actorPortSpec.isBehavior; this.hideInner = actorPortSpec.hideInner; this.portAddress = (ActorAddress) actorsAddress.clone(); this.portAddress.setActorPort(name); this.actorsAddress = actorsAddress; this.sessionEnabled = actorPortSpec.sessionEnabled; if (sessionEnabled) { clientSessions = new Hashtable(); } if (actorPortSpec.getPortType() != null) { // initiate a default port state machine. setPortSM(actorPortSpec.getPortType()); } else { setPortSM(new PortSM()); } } /** * Set the PortSM instance to be used by this Port. * * @param psm PortSM instance to be used by this Port */ protected void setPortSM(PortSM psm) { psm.initInstance(); // initialize the values psm.enterState(this); // initialize default state. protocolSM = psm; } /** * Set the PortSM instance to be used by this Port. This method uses * classloading to instanciate the PortSM class with the given className. * * @param className class name to use. */ protected void setPortSM(String className) { if ((className == null) || className.equals("")) { protocolSM = new PortSM(); } else { if (className.indexOf(".") == -1) { // not a full class name, use default actor. className = "actor." + actorsAddress.getActorType().toLowerCase() + "." + className; } try { Class c = Class.forName(className); Object o = c.newInstance(); if (!(o instanceof PortSM)) { protocolSM = new PortSM(); } else { protocolSM = (PortSM) o; } } catch (ClassNotFoundException e) { e.printStackTrace(); protocolSM = new PortSM(); } catch (IllegalAccessException e) { e.printStackTrace(); protocolSM = new PortSM(); } catch (InstantiationException e) { e.printStackTrace(); protocolSM = new PortSM(); } } protocolSM.initInstance(); // initialize the values protocolSM.enterState(this); // initialize default state. } /** * Called for reconfiguration of port instances. * * @param ps ActorPortSpec to use for reconfiguration. */ protected void reconfigurePort(ActorPortSpec ps) { this.name = ps.portName; this.isBehavior = ps.isBehavior; this.hideInner = ps.hideInner; } /** * Execute the port mechanisms. */ public void exec(ActorMsg sig, StateMachine curfsm) { if (curfsm.isTesting()) { curfsm.trace.addPortInputSignal(name, sig); } if (sig.isFrameworkMsg()) { // Executes the framework related messages for // setup and teardown of Connectors and sessions. execTrans(sig, curfsm); handleMessages(curfsm); handleTimers(curfsm); return; } // Execute the port state machine. protocolSM.execTrans(sig, protocolSM.getCurrentState(), this); // execute // the // transition handleMessages(curfsm); // handle messages for this execution handleTimers(curfsm); // handle timers for this execution } /** * Handle Timers used by this execution. * * @param curfsm current StateMachine instance. */ private void handleTimers(StateMachine curfsm) { if ((createTimers != null) && !createTimers.isEmpty()) { // create and stop timers in this execution. Enumeration e = createTimers.keys(); while (e.hasMoreElements()) { String timerid = (String) e.nextElement(); Long duration = ((Long) createTimers.get(timerid)); if (duration == null) { curfsm.stopTimer(timerid); // stop this timer } else { curfsm.startTimer(duration.longValue(), timerid, portAddress); // start // this // timer. } } createTimers.clear(); } } /** * Handle messages sent by this execution. * * @param curfsm current StateMachine instance. */ private void handleMessages(StateMachine curfsm) { if ((routeQueue != null) && !routeQueue.isEmpty()) { // send the messages produced by the execution. for (int i = 0; i < routeQueue.size(); i++) { routeMessage((ActorMsg) routeQueue.elementAt(i), curfsm); } routeQueue.removeAllElements(); } if ((messageQueue != null) && !messageQueue.isEmpty()) { // send the messages produced by the execution. for (int i = 0; i < messageQueue.size(); i++) { curfsm.sendMessage(((ActorMsg) messageQueue.elementAt(i))); } messageQueue.removeAllElements(); } } /** * The default sending of an ActorMsg, will route the message to the correct * destination. Used by PortSM developer when a message should propogate * according the Port configuration. * * @param msg ActorMsg to send. */ public void sendMessage(ActorMsg msg) { routeQueue.addElement(msg); } /** * Send the message to a given ActorAddress. * * @param msg message * @param aa actor address */ public void sendMessage(ActorMsg msg, ActorAddress aa) { msg.setReceiverRole(aa); messageQueue.addElement(msg); } /** * Send an ActorMsg to the given Collection of addresses. * * @param msg message * @param addressess vector of actor addresses */ public void sendMessage(ActorMsg msg, Vector addressess) { Enumeration enumer = addressess.elements(); while (enumer.hasMoreElements()) { ActorMsg tmpMsg = (ActorMsg) msg.cloneMsg(null); msg.setReceiverRole((ActorAddress) enumer.nextElement()); messageQueue.addElement(tmpMsg); } } /** * Start a timer with the given timerId. * * @param duration Time in ms. * @param timerId Id of the timer to start. */ public void startTimer(long duration, String timerId) { if (createTimers == null) createTimers = new Hashtable(); createTimers.put(timerId, new Long(duration)); } /** * Stop a timer with the given timerId. * * @param timerId Id of the timer to stop. */ public void stopTimer(String timerId) { if (createTimers == null) createTimers = new Hashtable(); createTimers.put(timerId, null); if (createTimers.isEmpty()) { createTimers = null; } } /** * Route a message to its correct destination. Rewrites the sender and * receiver of the ActorMsg according to configuration of the port before * sending it. * * @param msg actor message */ private void routeMessage(ActorMsg msg, StateMachine curfsm) { ActorAddress sender = msg.getSenderRole(); if (isBehavior) { // the senderAddress was not the actor, and the port is a // behavior port -- send the message to the owning actor. // msg.setSenderRole(msg.getFirstReply()); msg.setReceiverRole(actorsAddress); curfsm.sendMessage(msg); return; } if ((outConnectors == null || outConnectors.isEmpty()) && (inConnectors == null || inConnectors.isEmpty())) { if (curfsm.getScheduler().isTraceOn()) curfsm.trace.traceError(" No connectors to this port: " + portAddress + " Message: " + msg.toString()); return; } if ((sender == null) || sender.equals(actorsAddress)) { // the senderAddress was not set, assuming it was the owning // actor, or the senderAddress was the owning actor. Send to // all defined connectors. if (outConnectors != null && outConnectors.size() > 0) { curfsm.sendMessage(msg, outConnectors); } if (inConnectors != null && inConnectors.size() > 0) { ActorMsg sig = msg.getCopy(curfsm.getScheduler().getClassLoader()); curfsm.sendMessage(sig, inConnectors); } /*else { curfsm.sendMessage(msg, outConnectors); }*/ if (curfsm.isTesting()) { curfsm.trace.addPortOutputSignal(name, msg); } } else if (actorsAddress.isInnerActor(sender)) { /* * if (hideInner) { // the inner structure should be totally masked * // by this port -- meaning we flush the replyStack // of the * ActorMsg. Vector tmpStack = new Vector(); * tmpStack.addElement(portAddress); } */ curfsm.sendMessage(msg, outConnectors); // from inner to outer } else { curfsm.sendMessage(msg, inConnectors); // from outer to inner } } /** * Executes the framework related messages with regards to Ports. This * implements the port-port and port-actor protocols. * * @param sig actor message * @param curfsm state machine */ protected void execTrans(ActorMsg sig, StateMachine curfsm) { // if (sig instanceof PortCreateMsg) { if (sig.equals(PORT_CREATE_MSG)) { Vector connectors = (Vector) sig.getProperty(PORT_CREATE_MSG_CONNECTORS); // do not send ack message to senderAddress sender = null; // find and create new connectors. for (int i = 0; i < connectors.size(); i++) { ConnectorSpec cs = (ConnectorSpec) connectors.elementAt(i); ActorAddress address = (ActorAddress) cs.getTo().clone(); if (address.getActorPort() == null || address.getActorPort().equals("")) { address.setActorPort(DEFAULT_IN_PORT); } /*if (address.getActorPort() == DEFAULT_IN_PORT ) { address.setActorPort(null); }*/ Vector configuredConnectors = getConfiguredConnectors(); if (!configuredConnectors.contains(address)) { // the connector does not exist, request it. AFPropertyMsg crm = new AFPropertyMsg(CONNECTOR_REQUEST_MSG, true); crm.setBoolean(CONNECTOR_REQUEST_MSG_REQUEST_BIDIRECTIONAL, cs.getIsBidirectional()); crm.setSenderRole(portAddress); crm.setReceiverRole(address); crm.setFrameworkMsg(true); curfsm.sendMessage(crm); if (createConnectors == null) createConnectors = new Vector(); // createConnectors.addElement(address.key()); createConnectors.addElement(address); } } Enumeration ite = null; if (portAddress.isInnerActor(sig.getSenderRole())) { if (inConnectors != null) ite = inConnectors.elements(); } else { if (outConnectors != null) ite = outConnectors.elements(); } // remove if non-existent // ite = getConfiguredConnectors().elements(); while (ite != null && ite.hasMoreElements()) { ActorAddress ex = (ActorAddress) ite.nextElement(); if (ex.getActorPort() == DEFAULT_IN_PORT) { ex.setActorPort(null); } Vector conns = ConnectorSpec.getAddresses(connectors); if (!conns.contains(ex)) { // this should not exist, release it. // ConnectorReleaseMsg crm = new ConnectorReleaseMsg(); AFPropertyMsg crm = new AFPropertyMsg(CONNECTOR_RELEASE_MSG, true); crm.setSenderRole(portAddress); remove(ex); crm.setFrameworkMsg(true); crm.setReceiverRole(ex); curfsm.sendMessage(crm); } } if (createConnectors == null) createConnectors = new Vector(); if (createConnectors.isEmpty()) { // no connectors specified for this Port. AFPropertyMsg pcam = new AFPropertyMsg(PORT_CREATE_ACK_MSG, true); pcam.setFrameworkMsg(true); pcam.setReceiverRole(actorsAddress); pcam.setSenderRole(portAddress); curfsm.sendMessage(pcam); } return; } else if (sig.equals(CONNECTOR_REQUEST_MSG)) { // Got connector request, send confirm boolean bidir = sig.getBoolean(CONNECTOR_REQUEST_MSG_REQUEST_BIDIRECTIONAL); if (bidir) { // The request is for a bidirectional connector, allowing // the owning actor to send messages using this connector. if (actorsAddress.isInnerActor(sig.getSenderRole())) { if (inConnectors == null) { inConnectors = new Vector(); } addConnector(inConnectors, sig.getSenderRole()); // inner // actor // requested. } else { if (outConnectors == null) { outConnectors = new Vector(); } addConnector(outConnectors, sig.getSenderRole()); // outer // actor // requested. } } else { // The request is not bidirectional, and should thus be added // to the requester associations. if (requesterConnectors == null) { requesterConnectors = new Vector(); } addConnector(requesterConnectors, sig.getSenderRole()); } // ConnectorConfirmMsg ccm = new ConnectorConfirmMsg(); AFPropertyMsg ccm = new AFPropertyMsg(CONNECTOR_CONFIRM_MSG, true); ccm.setFrameworkMsg(true); ccm.setSenderRole(portAddress); ccm.setReceiverRole(sig.getSenderRole()); curfsm.sendMessage(ccm); return; } else if (sig.equals(CONNECTOR_CONFIRM_MSG)) { // Got confirmation on a request if (portAddress.isInnerActor(sig.getSenderRole())) { // Sender of confirm is an inner actor. if (inConnectors == null) inConnectors = new Vector(); addConnector(inConnectors, sig.getSenderRole()); } else { // Sender of confirm is an outer actor. if (outConnectors == null) outConnectors = new Vector(); addConnector(outConnectors, sig.getSenderRole()); } if (createConnectorsDone(sig.getSenderRole())) { // All connectors ack'ed. Port is configured, send Ack to owning // Actor. AFPropertyMsg pcam = new AFPropertyMsg(PORT_CREATE_ACK_MSG, true); pcam.setSenderRole(portAddress); pcam.setFrameworkMsg(true); pcam.setReceiverRole(actorsAddress); curfsm.sendMessage(pcam); // send the confirm mesage to senderAddress of the // AddConnector or PortCreate msg. This message is only send // if this is ack on add connector msg if (sender != null) { pcam = new AFPropertyMsg(PORT_CREATE_ACK_MSG, true); pcam.setSenderRole(portAddress); pcam.setFrameworkMsg(true); pcam.setReceiverRole(sender); curfsm.sendMessage(pcam); } } return; } else if (sig.equals(CONNECTOR_RELEASE_MSG)) { // Release the connector from this assoc. remove(sig.getSenderRole()); if (curfsm.scheduler.isTraceOn()) { curfsm.trace.traceTask("Connector released: " + sig.getSenderRole()); } return; } else if (sig.equals(PORT_TIME_OUT_MSG)) { // The actor has not received ack from this port. if ((createConnectors == null) || createConnectors.isEmpty()) { // should not happen } else { for (int i = 0; i < createConnectors.size(); i++) { // retry the request AFPropertyMsg crm = new AFPropertyMsg(CONNECTOR_REQUEST_MSG, true); crm.setSenderRole(portAddress); crm.setReceiverRole((ActorAddress) createConnectors.elementAt(i)); crm.setFrameworkMsg(true); curfsm.sendMessage(crm); } } return; } else if (sig.equals(PORT_REMOVE_MSG)) { // Remove all associations for this port. removePort(curfsm); return; } else if (sig.equals(CONNECTOR_REMOVE_MSG)) { // Remove a specific connector ConnectorSpec connectorSpec = (ConnectorSpec) sig.getProperty(CONNECTOR_REMOVE_MSG_CONNECTORS); if (sessionEnabled) { // clear the session path -- even if the actor has not requested // it. // PathRemoveMsg prm = new PathRemoveMsg(); AFPropertyMsg prm = new AFPropertyMsg(PATH_REMOVE_MSG, true); prm.setSenderRole(actorsAddress); prm.setFrameworkMsg(true); prm.setReceiverRole(connectorSpec.getFrom()); curfsm.sendMessage(prm); } if (curfsm.scheduler.isTraceOn()) { curfsm.trace.traceTask("Connector removed: " + connectorSpec); } AFPropertyMsg crm = new AFPropertyMsg(CONNECTOR_RELEASE_MSG, true); crm.setSenderRole(portAddress); crm.setFrameworkMsg(true); crm.setReceiverRole(connectorSpec.getFrom()); remove(connectorSpec.getTo()); curfsm.sendMessage(crm); return; } else if (sig.equals(CONNECTOR_Add_MSG)) { // Remove connector ConnectorSpec connectorSpec = (ConnectorSpec) sig.getProperty(CONNECTOR_ADD_MSG_CONNECTORS); // todo check what to do with session sender = sig.getSenderRole(); if (curfsm.scheduler.isTraceOn()) { curfsm.trace.traceTask("Connector added: " + connectorSpec); } ActorAddress conn = (ActorAddress) connectorSpec.getTo().clone(); if (conn.getActorPort() == null || conn.getActorPort().equals("")) { conn.setActorPort(DEFAULT_IN_PORT); } if (!getConfiguredConnectors().contains(conn)) { // the connector does not exist, request it. AFPropertyMsg crm = new AFPropertyMsg(CONNECTOR_REQUEST_MSG, true); crm.setBoolean(CONNECTOR_REQUEST_MSG_REQUEST_BIDIRECTIONAL, connectorSpec.getIsBidirectional()); crm.setSenderRole(portAddress); crm.setReceiverRole(conn); curfsm.sendMessage(crm); createConnectors = new Vector(); // createConnectors.addElement(conn.key()); createConnectors.addElement(conn); } return; } // allow other framework messages through routeMessage(sig, curfsm); } /** * Removes the port. All associations are released, before the port is * removed * * @param curfsm is teh reference to the state machine */ public void removePort(StateMachine curfsm) { if (sessionEnabled) { // clear the session path -- even if the actor has not requested it. AFPropertyMsg prm = new AFPropertyMsg(PATH_REMOVE_MSG, true); prm.setSenderRole(actorsAddress); prm.setFrameworkMsg(true); curfsm.sendMessage(prm, getConfiguredConnectors()); // send to all } AFPropertyMsg crm = new AFPropertyMsg(CONNECTOR_RELEASE_MSG, true); crm.setSenderRole(portAddress); crm.setFrameworkMsg(true); curfsm.sendMessage(crm, getAndClearAllAssociations()); } /** * Check if there are more connectors to create. Removes the supplied * ActorAddress from the createConnectors Vector. * * @param connectorAddress ActorAddress which was ack'ed. * @return boolean indicating whether more connectors are unack'ed. */ private boolean createConnectorsDone(ActorAddress connectorAddress) { if (createConnectors != null) { createConnectors.removeElement(connectorAddress); return createConnectors.isEmpty(); } else return true; } /** * Get a Vector containing all the asssociations which must be removed upon * tearing down this connector. The associations are cleared from their * respective Vectors. * * @return Vector of ActorAddresses with all associations. */ private Vector getAndClearAllAssociations() { Vector all = new Vector(); if (outConnectors != null) addVector(all, outConnectors); if (inConnectors != null) addVector(all, inConnectors); if (createConnectors != null) addVector(all, createConnectors); if (requesterConnectors != null) addVector(all, requesterConnectors); if (requesterConnectors != null) requesterConnectors.removeAllElements(); if (createConnectors != null) createConnectors.removeAllElements(); if (inConnectors != null) inConnectors.removeAllElements(); if (outConnectors != null) outConnectors.removeAllElements(); return all; } /** * Add an ActorAddress to the given ArrayList if it doesn't exist. * * @param connectors ArrayList to store the ActorAddress in. * @param aa ActorAddress to add. * @return boolean indicating whether ActorAddress was added */ private static boolean addConnector(Vector connectors, ActorAddress aa) { if (!connectors.contains(aa)) { connectors.addElement(aa); return true; } return false; } /** * Remove connector association from the Port. * * @param aa ActorAddress to remove. * @return removed boolean indicating whether the actoradress was removed */ private boolean remove(ActorAddress aa) { boolean removed = false; if (inConnectors != null && inConnectors.removeElement(aa)) { removed = true; } if (outConnectors != null && outConnectors.removeElement(aa)) { removed = true; } if (requesterConnectors != null && requesterConnectors.removeElement(aa)) { removed = true; } return removed; } private Vector getConfiguredConnectors() { Vector all = new Vector(); if (inConnectors != null) { Enumeration enumer = inConnectors.elements(); while (enumer.hasMoreElements()) { all.addElement(enumer.nextElement()); } } if (outConnectors != null && !outConnectors.isEmpty()) { addVector(all, outConnectors); } return all; } /** * Remove the data used for a session. * * @param sessionId key to remove. */ private void removeSessionInstance(String sessionId) { clientSessions.remove(sessionId); } private void addVector(Vector v1, Vector v2) { for (int i = 0; i < v2.size(); i++) { Object o = (Object) v2.elementAt(i); v1.addElement(o); } } public ActorAddress getPortAddress() { return portAddress; } public boolean isSessionEnabled() { return sessionEnabled; } public Vector getOutConnectors() { return outConnectors; } public Vector getInConnectors() { return inConnectors; } public ActorAddress getConnectorEndAdddress(Vector ends, String actorType) { for (int i = 0; i < ends.size(); i++) { ActorAddress address = (ActorAddress) ends.elementAt(i); if (address.getActorType().equals(actorType)) { return address; } } return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy