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

org.coos.javaframe.SchedulerImpl 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.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.coos.actorframe.application.Container;
import org.coos.actorframe.application.Session;
import org.coos.actorframe.messages.AFConstants;
import org.coos.javaframe.messages.AFPropertyMsg;
import org.coos.javaframe.messages.ActorMsg;
import org.coos.javaframe.messages.ActorRouterRegMsg;
import org.coos.javaframe.messages.ActorRouterUnRegMsg;
import org.coos.javaframe.messages.JFConstants;
import org.coos.javaframe.messages.Message;
import org.coos.javaframe.messages.RouterMsg;
import org.coos.util.serialize.AFClassLoader;

/**
 * Specifies an general simulator that is extended to a specific application. It
 * is used to keep the reference to the different state machines. It has a
 * message queue that is used to send actor message between actors. The queue is
 * a FIFO queue.
 * 
 * @author Geir Melby, Tellu AS
 */
public class SchedulerImpl implements Scheduler, Runnable, AFConstants, AFClassLoader {
	/**
	 * SchedulerData contains common instance date for a set of actors. It is
	 * configured at the start up of the system
	 */
	private SchedulerData schedulerData;

	/**
	 * If true all logged information logged and stored
	 */
	boolean traceOn = true;
	boolean traceError = true;

	/**
	 * Contains the actor instances belonging to this scheduler
	 */
	public Hashtable mySchedulables;
	protected static Logger logger = LoggerFactory.getLogger("org.coos.javaframe");

	private TraceObject trace = new TraceObject(); // trace output on the
	// screen;

	// object use to synchronized access to non reentrent code in different
	// threads (ServerUDP, SenderUDP)
	public static Object semafor = new Object();
	private boolean running = true;
	private int threads = 1;
	private String name = "default";
	private Thread thread;
	protected AFClassLoader classLoader;

	/**
	 * Default constructor
	 */
	public SchedulerImpl() {
		mySchedulables = new Hashtable();
	}

	/**
	 * @param name
	 *            - the name of the constructor
	 */
	public SchedulerImpl(String name) {
		mySchedulables = new Hashtable();
	}

	/**
	 * @param schedulerData
	 *            - the system data that the scheduler is initialized with, i.e.
	 *            all the actors in this system
	 */
	public SchedulerImpl(SchedulerData schedulerData) {
		this.schedulerData = schedulerData;

		if (schedulerData.getDefaultScheduler() == null) {
			schedulerData.setDefaultScheduler(this);
		}

		mySchedulables = new Hashtable();
	}

	/**
	 * @param name
	 *            - the name of the scheduler
	 * @param schedulerData
	 *            the system data that the scheduler is initialized with, i.e.
	 *            all the actors in this system
	 */
	public SchedulerImpl(String name, SchedulerData schedulerData) {
		this.schedulerData = schedulerData;

		if (schedulerData.getDefaultScheduler() == null) {
			schedulerData.setDefaultScheduler(this);
		}

		mySchedulables = new Hashtable();
	}

	public SchedulerData getSchedulerData() {
		return schedulerData;
	}

	public void setSchedulerData(SchedulerData schedulerData) {
		this.schedulerData = schedulerData;

		if (schedulerData.getDefaultScheduler() == null) {
			schedulerData.setDefaultScheduler(this);
		}
	}

	/**
	 * Adds an instance of a state machine to the schedulers list of created
	 * state machines. The hash key is the string representation of actor
	 * address as "ping@Ping".
	 * 
	 * @param sm
	 *            is the state machine to be added
	 * @param aa
	 *            is the actor address of the state machine
	 */
	public void addSchedulable(Schedulable sm, ActorAddress aa) {
		schedulerData.getMySystem().put(aa.keyWithOutPortAndRole(), sm);
		mySchedulables.put(aa.keyWithOutPortAndRole(), sm);
		sm.setScheduler(this);

		if (aa.key().indexOf(".") == -1) {
			sm.init(); // Initiate State machine added instead of call to
			// sm.ejbcreate
		}
	}

	public void remove(ActorAddress aa) {
		schedulerData.getMySystem().remove(aa.keyWithOutPortAndRole());
		mySchedulables.remove(aa.keyWithOutPortAndRole());
	}

	/**
	 * Adds the router to the scheduler. This used when the router shal be run
	 * as part of a scheduler,
	 * 
	 * @param session
	 */
	public void addRouter(Session session) {
		schedulerData.setTheRouterSession(session);
		session.setScheduler(this);
	}

	/**
	 * Adds the router to the scheduler. This used when the router shal be run
	 * as part of a scheduler,
	 */
	public void removeRouter() {
		schedulerData.setTheRouterSession(null);
	}

	/**
	 * This method is used to configure the types of scedulable object that this
	 * scheduler will handle
	 * 
	 * @param objectType
	 *            of the scedulable object
	 */
	public void configure(String objectType) {
		schedulerData.getSchedulerConfig().put(objectType, this);
	}

	public void start() {
		this.thread = new Thread(this, this.name);
		thread.start();
	}

	/**
	 * The run method that the thread starts with
	 */
	public void run() {
		this.execute();
	}

	/**
	 * the infinite loop for execution of scedulable objects. It waits for
	 * messages in the mailbox queue. If no messages it calls the "wait" until
	 * it's notified that a message is placed in the mailbox
	 */
	void execute() {
		Schedulable sm = null;
		ActorMsg msg = null;

		while (running) {
			Enumeration en = mySchedulables.elements();

			// Iterate alle schedulables
			while (en.hasMoreElements()) {
				Object o = en.nextElement();

				if (o instanceof Schedulable) {
					sm = (Schedulable) o;
					// remove the first message of the schedulable
					msg = (ActorMsg) sm.getMailbox().removeFirst(); // remove
					// the
					// message
					// read
					// If it is not null process it

					if (msg != null) {
						if (msg.getReceiverRole().isValied()) {
							processMessage(msg, sm);

						} else {
							if (isTraceOn()) {
								trace.traceError("execute: Error in receiver role, message dropped!"
										+ msg.getReceiverRole());
							}
						}
					}
				}
			}

			// The first message of all schedulables attached to this scheduler
			// is now processed,
			// now we have to check if the queues contains more messages to be
			// processed
			synchronized (this) {
				boolean emptyqueues = true;
				Enumeration enum1 = mySchedulables.elements();

				while (enum1.hasMoreElements()) {
					Object ob = enum1.nextElement();

					if (ob instanceof Schedulable) {
						sm = (Schedulable) ob;

						// One of the queues is not empty
						if (!sm.getMailbox().isEmpty()) {
							emptyqueues = false;
						}
					}
				}

				// If all queues are empty the sceduler waits
				if (emptyqueues) {
					try {
						wait();
					} catch (InterruptedException e) {
						if (isTraceOn()) {
							trace.traceError("execute: wait() interruptexception" + e.getMessage());
						}
					}
				}
			}
		}
	}

	protected void processMessage(ActorMsg msg, Schedulable sm) {
		sm.processMessage(msg);

		if (sm.isReadyToBeDeleted()) {

			remove(sm.getMyActorAddress());

			if (isTraceOn()) {
				logger.log(TraceConstants.tlInfo, "execute: ActorStateMachine deleted: " + sm);
			}
		}
	}

	/**
	 * Check if the state machine with address aa is already created
	 * 
	 * @param aa
	 *            is the address of the state machine
	 * @return true if the actor exists already
	 */
	private boolean isActorCreated(ActorAddress aa) {
		return schedulerData.getMySystem().get(aa.keyWithOutPortAndRole()) != null;
	}

	/**
	 * If the message is an Router message, a new actor message ia created out
	 * of the payload of the router message. Then the out method of the
	 * scheduler is called that puts the message into the mailbox of the
	 * receiving actor
	 * 
	 * @param rm
	 *            is the message
	 * @param curfsm
	 *            reference to the schedulable caller
	 * @throws IOException
	 */
	public void postMessageToScheduler(Message rm, Schedulable curfsm) throws IOException {
		ActorMsg msg;

		if (rm instanceof RouterMsg) {
			msg = ((RouterMsg) rm).deSerializeMessage(classLoader);
		} else {
			msg = (ActorMsg) rm;
		}
		// store the last received message to avoid sending the same message
		// back,
		// discard the message
		schedulerData.setLastMsgFromRouter(msg);

		// Special handling of RESTART message, send it to all state machines
		if (msg.equals(JFConstants.ROLE_RESTART_MSG)) {
			if (msg.getReceiverRole() == null) {
				// broadcast it to all state machines
				Enumeration en = getSchedulerData().getMySystem().elements();
				while (en.hasMoreElements()) {
					Object o = en.nextElement();
					if (o instanceof StateMachine) {
						StateMachine sm = (StateMachine) o;
						ActorAddress address = sm.getMyActorAddress();
						ActorMsg am = msg.getCopy(getClassLoader());
						am.setReceiverRole(address);
						out(am, curfsm);
					}

				}
				return;
			}
		}

		out(msg, curfsm);

	}

	/**
	 * Post the message to the input queue of the receiving scheduable.
	 * 
	 * @param sig
	 *            is the output signal to be send
	 * @param curfsm
	 *            is a reference to the state machine
	 */
	public void output(ActorMsg sig, Schedulable curfsm) {
		out(sig, curfsm);

		if (curfsm != null) {
			if (isTraceOn()) {
				curfsm.getScheduler().getTraceObject().traceOutput(sig);
			}
		}
	}

	/**
	 * The output method that every schedulable object must call in order to
	 * send messages to other schedulable objects. The trace is special here,
	 * because this method is static, the traceobject of the running scheduable
	 * instance has to be used curfsm.getScheduler().getTraceObject()
	 * 
	 * @param sig
	 *            the message
	 * @param curfsm
	 *            the schedulable object
	 */
	protected final void out(ActorMsg sig, Schedulable curfsm) {
		ActorAddress receiver = sig.getReceiverRole();

		if (receiver == null || !receiver.isValied()) {
			if (isTraceOn())
				trace.traceError("Scheduler.out: Illegal receiver address: " + receiver);
			return;
		}

		// check if the sig is an create message and the
		boolean created = isActorCreated(receiver);
		if (!created && isCreateMsg(sig) && (sig.getProperty("targetActor") == null)) {
			// The message is either a RolePlay or a RoleCreate message
			Scheduler sched = (Scheduler) schedulerData.getSchedulerConfig().get(sig.getReceiverRole().getActorType());

			if (sched == null) {
				sched = schedulerData.getDefaultScheduler();
			}

			StateMachine sm = sched.createActor(sig, curfsm);

			if (sm != null) {
				// the message is send as part of the create actor mthod
				sched.notifyScheduler();
			} else {
				if (isTraceOn())
					trace.traceError("Scheduler.out: ERROR Creation of actor Failed: " + receiver.getActorType());
			}

			return;
		}

		// Send all other messages if the receiver exists
		// check if mySystem contains the state machine
		Object o = schedulerData.getMySystem().get(sig.getReceiverRole().keyWithOutPortAndRole());

		if (o != null) {
			Schedulable sm = (Schedulable) o;

			if (sm != null) {
				sm.getMailbox().addMessage(sig);
				sm.getScheduler().notifyScheduler();
			}
		} else {
			if (!schedulerData.getContainer().isRouterRunning() && schedulerData.getTheRouterSession() == null) {

				if ((curfsm != null) && isTraceOn()) {
					trace.traceError("Scheduler.out: Router is not available, theRouterSession = null");
				}

				return;
			}

			synchronized (schedulerData.getTheRouterSession().getScheduler()) {
				if (schedulerData.getLastMsgFromRouter() == null || schedulerData.getLastMsgFromRouter() != sig) {
					// the last message i send from the local actor
					// schedulerData, it safe to send it to the router
					if (sig.getProxyAddress() != null) {
						ActorAddress aa = sig.getProxyAddress();
						sig.setProxyAddress(sig.getReceiverRole());
						sig.setReceiverRole(aa);
					}
					schedulerData.getTheRouterSession().processMessage(sig);
				} else {
					if (isTraceOn()) {
						trace.traceError("Scheduler.out: circulating message is skipped:" + sig);
					}
				}
			}
		}
	}

	/**
	 * Used to check if the incomming message is a message that shall create an
	 * new state machine instance, before the transition is executed. This
	 * routine is overrided by the sub class. Default is set to false, which
	 * will disable the possiblillity to create new instances.
	 * 
	 * @param msg
	 *            is the message to checked if it is a RoleCreateMsg
	 */

	/*
	 * protected boolean isCreateMsg(ActorMsg msg) { return false; }
	 */
	protected static boolean isCreateMsg(ActorMsg msg) {
		if (msg instanceof AFPropertyMsg) {
			AFPropertyMsg am = (AFPropertyMsg) msg;

			return (am.equals(AFConstants.ROLE_CREATE_MSG) || am.equals(AFConstants.ROLE_PLAY_MSG));
		} else {
			return false;
		}
	}

	static final String title = "ActorStateMachine creation: ";

	/**
	 * creates an new actor instance.
	 * 
	 * @param curfsm
	 * @return the actor state machine
	 */
	public StateMachine createActor(ActorMsg rsm, Schedulable curfsm) {
		String className = null;
		ActorSpec as = getSchedulerData().getApplicationSpec().getActorSpec(rsm.getReceiverRole().getActorType());

		if ((as != null) && rsm instanceof AFPropertyMsg) {
			className = as.getActorClassName();
		}

		if ((className == null) || className.equals("")) {
			if (isTraceOn()) {
				trace.traceError(title + "failed,  Class name: " + className);
			}

			return null;
		} else {
			// creates the state machine
			StateMachine sm = null;

			try {
				sm = createClass(className);
				if (sm != null) {

					// this variables has to be set before the state machine is
					// inititated, instance created and added
					// to the mySystem, which contains all state machine
					// instances
					String actorType = rsm.getReceiverRole().getActorType();
					sm.setMyActorId(rsm.getReceiverRole().getActorID());
					sm.setMyActorType(actorType);
					sm.setMyActorDomain(schedulerData.getActorDomainName());
					sm.setCurrentMessage(rsm);
					sm.trace = this.trace;
					sm.setVisible(rsm.getBoolean(ROLE_CREATE_MSG_VISIBLE));
					sm.setTraceLevel(rsm.getInt(TRACE_LEVEL_PROP));

					if (isTraceOn()) {
						logger.log(TraceConstants.tlInfo, title + "Success: " + "ActorStateMachine: " + sm
								+ " Scheduler: " + getName() + " Class: " + className);
					}

					this.addSchedulable(sm, rsm.getReceiverRole());
					sm.mailbox.addMessage(rsm);
				}
			} catch (InstantiationException e) {
				if (isTraceOn()) {
					if (isTraceOn()) {
						trace.traceError(title + "failed, InstantiationException occurred" + " Class: " + className);
					}
				}
			} catch (IllegalAccessException e) {
				if (isTraceOn()) {
					trace.traceError("failed, IllegalAccessException occurred" + " Class: " + className);
				}
			} catch (ClassNotFoundException e) {
				// the class was not found, could be an error
				if (isTraceOn()) {
					trace.traceError(title + "failed, class name: " + className + " not found");
				}
			}

			return sm;
		}
	}

	protected StateMachine createClass(String className) throws InstantiationException, IllegalAccessException,
			ClassNotFoundException {
		Class clazz = classLoader.loadClass(className);
		if (clazz != null) {
			return (StateMachine) clazz.newInstance();
		}
		return null;
	}

	/**
	 * Return those actors that has its flag to be visible in the router system
	 * 
	 * @return a vector of actor address
	 */
	public Vector getVisibleStateMachines() {
		Vector v = new Vector();
		Enumeration en = schedulerData.getMySystem().elements();

		while (en.hasMoreElements()) {
			Object o = en.nextElement();

			if (o instanceof StateMachine) {
				// check if the state machine is visible
				StateMachine sm = (StateMachine) o;

				if (sm.isVisible()) {
					v.addElement(sm.getMyActorAddress());
				}
			}
		}

		return v;
	}

	/**
	 * Sends an ActorStateMachine Reg message to the local router, to update the
	 * router with visble actors
	 * 
	 * @param sm
	 *            is the actor
	 */
	public void upDateVisibleActors(StateMachine sm) {
		ActorRouterRegMsg arrm = new ActorRouterRegMsg(getVisibleStateMachines(), 0);
		arrm.setReceiverRole(schedulerData.getApplicationSpec().getRouterAddress()); // set
		// the
		// senderAddress
		// of
		// this
		// message

		if (sm != null) {
			arrm.setSenderRole(new ActorAddress(sm.myActorId, sm.myActorType)); // set
			// the
			// senderAddress
			// of
			// this
		} else {
			arrm.setSenderRole(new ActorAddress("null", "null")); // set dumy
			// actor
			// address
			// (is is
			// not used)
		}

		Session rs = schedulerData.getTheRouterSession();

		if (rs != null && schedulerData.getContainer().isRouterRunning()) {
			schedulerData.getTheRouterSession().processMessage(arrm);
		}

		// out(arrm, sm);
	}

	/**
	 * Sends an ActorStateMachine Reg message to the local router, to update the
	 * router with visble actors
	 * 
	 * @param aa
	 *            is the actor
	 */
	public void unRegVisibleActor(ActorAddress aa) {
		Vector v = new Vector();
		v.addElement(aa.clone());

		ActorRouterUnRegMsg arrm = new ActorRouterUnRegMsg(v, 0);

		arrm.setReceiverRole(schedulerData.getApplicationSpec().getRouterAddress()); // set
		// the
		// senderAddress
		// of
		// this
		// message
		arrm.setSenderRole(aa); // set the senderAddress of this

		Session rs = schedulerData.getTheRouterSession();

		if (rs != null && schedulerData.getContainer().isRouterRunning()) {
			rs.processMessage(arrm);
		}
	}

	/**
	 * Cheks if the statemachine that is equal to the actor address
	 * 
	 * @param aa
	 *            the actor address of the state machine to be checked
	 * @return true if the state machine exists
	 */
	public boolean containsStateMachine(ActorAddress aa) {
		if (schedulerData.getMySystem().containsKey(aa.key())) {
			return true;
		}

		return false;
	}

	/**
	 * All threads has to be deleted. All actors are also called to enable the
	 * actors to stop local threads
	 */
	public void destroyApp() {
		// all threads are stopped first
		Enumeration en = schedulerData.getMySystem().elements();

		while (en.hasMoreElements()) {
			Schedulable sched = (Schedulable) en.nextElement();
			sched.destroy();
			sched.getScheduler().setStopFlag();
			sched.getScheduler().interrupt();
		}

		// clear the scheduler data
		schedulerData.getMySystem().clear();
		// schedulerData.getMyBundles().clear();
		schedulerData.getSchedulerConfig().clear();
		// schedulerData.setDefaultScheduler(null);
	}

	public void interrupt() {
		this.thread.interrupt();
	}

	public void setStopFlag() {
		running = false;
	}

	/**
	 * uases all scheduable components. This method is called each time the
	 * container calls the destroy method
	 */
	public void pauseApp() {
		Enumeration en = schedulerData.getMySystem().elements();

		while (en.hasMoreElements()) {
			Schedulable sched = (Schedulable) en.nextElement();
			sched.pause();
		}

		// todo pause all threads here
	}

	public void addStateMachine(StateMachine sm, String type) {
		// To change body of implemented methods use File | Settings | File
		// Templates.
	}

	public TraceObject getTraceObject() {
		return trace;
	}

	public void clearLastMsgFromRouter() {
		schedulerData.setLastMsgFromRouter(null);
	}

	public int getThreads() {
		return threads;
	}

	public void setThreads(int threads) {
		this.threads = threads;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setClassLoader(Container classLoader) {
		this.classLoader = classLoader;
	}

	public AFClassLoader getClassLoader() {
		return classLoader;
	}

	public Class loadClass(String className) throws ClassNotFoundException {
		return classLoader.loadClass(className);
	}

	public boolean isTraceOn() {
		return traceOn;
	}

	public void setTrace(boolean on) {
		traceOn = on;
	}

	public boolean isTraceError() {
		return traceError;
	}

	public void setTraceError(boolean on) {
		traceError = on;
	}

	public synchronized void notifyScheduler() {
		notify();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy