/*****************************************************************
JADE - Java Agent DEvelopment Framework is a framework to develop
multi-agent systems in compliance with the FIPA specifications.
Copyright (C) 2000 CSELT S.p.A.
GNU Lesser General Public License
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,
version 2.1 of the License.
This library 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 library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*****************************************************************/
package jade.core;
import java.util.Vector;
import jade.util.leap.List;
import jade.util.leap.LinkedList;
import jade.util.leap.Iterator;
import jade.util.leap.EnumIterator;
import jade.util.leap.Serializable;
import jade.core.behaviours.Behaviour;
/**
@author Giovanni Rimassa - Universita' di Parma
@version $Date: 2005-12-16 16:37:25 +0100 (ven, 16 dic 2005) $ $Revision: 5844 $
*/
/**************************************************************
Name: Scheduler
Responsibility and Collaborations:
+ Selects the behaviour to execute.
(Behaviour)
+ Holds together all the behaviours of an agent.
(Agent, Behaviour)
+ Manages the resources needed to synchronize and execute agent
behaviours, such as thread pools, locks, etc.
****************************************************************/
class Scheduler implements Serializable {
//#MIDP_EXCLUDE_BEGIN
protected List readyBehaviours = new LinkedList();
protected List blockedBehaviours = new LinkedList();
//#MIDP_EXCLUDE_END
/*#MIDP_INCLUDE_BEGIN
protected Vector readyBehaviours = new Vector();
protected Vector blockedBehaviours = new Vector();
#MIDP_INCLUDE_END*/
/**
@serial
*/
private Agent owner;
/**
@serial
*/
private int currentIndex;
public Scheduler(Agent a) {
owner = a;
currentIndex = 0;
}
// Add a behaviour at the end of the behaviours queue.
// This can never change the index of the current behaviour.
// If the behaviours queue was empty notifies the embedded thread of
// the owner agent that a behaviour is now available.
public synchronized void add(Behaviour b) {
//#MIDP_EXCLUDE_BEGIN
readyBehaviours.add(b);
//#MIDP_EXCLUDE_END
/*#MIDP_INCLUDE_BEGIN
readyBehaviours.addElement(b);
#MIDP_INCLUDE_END*/
notify();
//#MIDP_EXCLUDE_BEGIN
owner.notifyAddBehaviour(b);
//#MIDP_EXCLUDE_END
}
// Moves a behaviour from the ready queue to the sleeping queue.
public synchronized void block(Behaviour b) {
if (removeFromReady(b)) {
//#MIDP_EXCLUDE_BEGIN
blockedBehaviours.add(b);
//#MIDP_EXCLUDE_END
/*#MIDP_INCLUDE_BEGIN
blockedBehaviours.addElement(b);
#MIDP_INCLUDE_END*/
//#MIDP_EXCLUDE_BEGIN
owner.notifyChangeBehaviourState(b, Behaviour.STATE_READY, Behaviour.STATE_BLOCKED);
//#MIDP_EXCLUDE_END
}
}
// Moves a behaviour from the sleeping queue to the ready queue.
public synchronized void restart(Behaviour b) {
if (removeFromBlocked(b)) {
//#MIDP_EXCLUDE_BEGIN
readyBehaviours.add(b);
//#MIDP_EXCLUDE_END
/*#MIDP_INCLUDE_BEGIN
readyBehaviours.addElement(b);
#MIDP_INCLUDE_END*/
notify();
//#MIDP_EXCLUDE_BEGIN
owner.notifyChangeBehaviourState(b, Behaviour.STATE_BLOCKED, Behaviour.STATE_READY);
//#MIDP_EXCLUDE_END
}
}
/**
Restarts all behaviours. This method simply calls
Behaviour.restart() on every behaviour. The
Behaviour.restart() method then notifies the agent (with the
Agent.notifyRestarted() method), causing Scheduler.restart() to
be called (this also moves behaviours from the blocked queue to
the ready queue --> we must copy all behaviours into a temporary
buffer to avoid concurrent modification exceptions).
Why not restarting only blocked behaviours?
Some ready behaviour can be a ParallelBehaviour with some of its
children blocked. These children must be restarted too.
*/
public synchronized void restartAll() {
Behaviour[] behaviours = new Behaviour[readyBehaviours.size()];
int counter = 0;
//#MIDP_EXCLUDE_BEGIN
for(Iterator it = readyBehaviours.iterator(); it.hasNext();)
//#MIDP_EXCLUDE_END
/*#MIDP_INCLUDE_BEGIN
for(Iterator it = new EnumIterator(readyBehaviours.elements()); it.hasNext();)
#MIDP_INCLUDE_END*/
behaviours[counter++] = (Behaviour)it.next();
for(int i = 0; i < behaviours.length; i++) {
Behaviour b = behaviours[i];
b.restart();
}
behaviours = new Behaviour[blockedBehaviours.size()];
counter = 0;
//#MIDP_EXCLUDE_BEGIN
for(Iterator it = blockedBehaviours.iterator(); it.hasNext();) {
//#MIDP_EXCLUDE_END
/*#MIDP_INCLUDE_BEGIN
for(Iterator it = new EnumIterator(blockedBehaviours.elements()); it.hasNext();) {
#MIDP_INCLUDE_END*/
//#DOTNET_EXCLUDE_BEGIN
behaviours[counter++] = (Behaviour)it.next();
//#DOTNET_EXCLUDE_END
/*#DOTNET_INCLUDE_BEGIN
Object tmpB = null;
try // Hack: sometimes .NET inserts into this array a non-Behaviour object
{
tmpB = it.next();
behaviours[counter++] = (Behaviour)tmpB;
}
catch(ClassCastException cce)
{
System.out.println("Found an object of type "+tmpB.getClass().getName()+" instead of Behaviour");
cce.printStackTrace();
}
#DOTNET_INCLUDE_END*/
}
for(int i = 0; i < behaviours.length; i++) {
Behaviour b = behaviours[i];
/*#DOTNET_INCLUDE_BEGIN
if (b != null)
#DOTNET_INCLUDE_END*/
b.restart();
}
}
/**
Removes a specified behaviour from the scheduler
*/
public synchronized void remove(Behaviour b) {
boolean found = removeFromBlocked(b);
if(!found) {
found = removeFromReady(b);
}
if (found) {
//#MIDP_EXCLUDE_BEGIN
owner.notifyRemoveBehaviour(b);
//#MIDP_EXCLUDE_END
}
}
/**
Selects the appropriate behaviour for execution, with a trivial
round-robin algorithm.
*/
public synchronized Behaviour schedule() throws InterruptedException {
while(readyBehaviours.isEmpty()) {
owner.idle();
}
//#MIDP_EXCLUDE_BEGIN
Behaviour b = (Behaviour)readyBehaviours.get(currentIndex);
//#MIDP_EXCLUDE_END
/*#MIDP_INCLUDE_BEGIN
Behaviour b = (Behaviour)readyBehaviours.elementAt(currentIndex);
#MIDP_INCLUDE_END*/
currentIndex = (currentIndex + 1) % readyBehaviours.size();
return b;
}
//#MIDP_EXCLUDE_BEGIN
// Helper method for persistence service
public synchronized Behaviour[] getBehaviours() {
Behaviour[] result = new Behaviour[blockedBehaviours.size() + readyBehaviours.size()];
Iterator itReady = readyBehaviours.iterator();
Iterator itBlocked = blockedBehaviours.iterator();
for(int i = 0; i < result.length; i++) {
Behaviour b = null;
if(itReady.hasNext()) {
b = (Behaviour)itReady.next();
}
else {
b = (Behaviour)itBlocked.next();
}
result[i] = b;
}
return result;
}
// Helper method for persistence service
public void setBehaviours(Behaviour[] behaviours) {
readyBehaviours.clear();
blockedBehaviours.clear();
for(int i = 0; i < behaviours.length; i++) {
Behaviour b = behaviours[i];
if(b.isRunnable()) {
readyBehaviours.add(b);
}
else {
blockedBehaviours.add(b);
}
}
// The current index is not saved when persisting an agent
currentIndex = 0;
}
//#MIDP_EXCLUDE_END
// Removes a specified behaviour from the blocked queue.
private boolean removeFromBlocked(Behaviour b) {
//#MIDP_EXCLUDE_BEGIN
return blockedBehaviours.remove(b);
//#MIDP_EXCLUDE_END
/*#MIDP_INCLUDE_BEGIN
return blockedBehaviours.removeElement(b);
#MIDP_INCLUDE_END*/
}
// Removes a specified behaviour from the ready queue.
// This can change the index of the current behaviour, so a check is
// made: if the just removed behaviour has an index lesser than the
// current one, then the current index must be decremented.
private boolean removeFromReady(Behaviour b) {
int index = readyBehaviours.indexOf(b);
if(index != -1) {
//#MIDP_EXCLUDE_BEGIN
readyBehaviours.remove(b);
//#MIDP_EXCLUDE_END
/*#MIDP_INCLUDE_BEGIN
readyBehaviours.removeElement(b);
#MIDP_INCLUDE_END*/
if(index < currentIndex)
--currentIndex;
//if(currentIndex < 0)
// currentIndex = 0;
else if (index == currentIndex && currentIndex == readyBehaviours.size())
currentIndex = 0;
}
return index != -1;
}
}