[jade-develop] failure when setting arguments (bugfix)

Pavel Kryl p.kryl@sh.cvut.cz
Thu, 23 May 2002 20:22:23 +0200


Hello JADE team,
   I have not written any message for a long time! So I am here with a
small 
bugfix. When I have tried following (simplified):

	masterCtrl=(jade.wrapper.Agent)
getContainerController().createNewAgent(
						MasterAgent.NAME,
	
maInfo.getAgentClassname(),
						Object[] {null});

StaleProxyException has been thrown, so I took a lookat the sources of 
AgentContainer than Agent and ... bugfix is attached (the point was you
tried to 
convert elemnts in the arg-array to String using toString() method but
did not 
check whether it is 'null' or not).

							Bye


-- 
=======================================================================
krylpavel@users.sourceforge.net
http://genetica.sourceforge.net
12413AEB2ED4FA5E6F7D78E78BEDE8209450920F923A40EE10E510CC98D444AA08E1324
"One OS to rule them all, One OS to find them,
One OS to bring them all and in the darkness bind them..."
ICQ: 100453972
/*****************************************************************
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.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

import java.io.IOException;
import java.io.InterruptedIOException;

import java.util.Vector;


import jade.util.leap.Serializable;

import jade.util.leap.Iterator;
import jade.util.leap.Map;
import jade.util.leap.HashMap;
import jade.util.leap.List;
import jade.util.leap.ArrayList;

import jade.core.behaviours.Behaviour;

import jade.lang.Codec;
import jade.lang.acl.*;

import jade.onto.Frame;
import jade.onto.Ontology;
import jade.onto.OntologyException;

import jade.domain.AMSService;
// Concepts from fipa-agent-management ontology
import jade.domain.FIPAAgentManagement.AMSAgentDescription;

import jade.domain.FIPAException;

import jade.content.ContentManager;

//__SECURITY__BEGIN
import jade.security.Authority;
import jade.security.AuthException;
import jade.security.AgentPrincipal;
import jade.security.DelegationCertificate;
import jade.security.IdentityCertificate;
import jade.security.CertificateFolder;
import jade.security.PrivilegedExceptionAction;
//__SECURITY__END

/**
   The <code>Agent</code> class is the common superclass for user
   defined software agents. It provides methods to perform basic agent
   tasks, such as:
   <ul>
   <li> <b> Message passing using <code>ACLMessage</code> objects,
   both unicast and multicast with optional pattern matching. </b>
   <li> <b> Complete Agent Platform life cycle support, including
   starting, suspending and killing an agent. </b>
   <li> <b> Scheduling and execution of multiple concurrent activities. </b>
   <li> <b> Simplified interaction with <em>FIPA</em> system agents
   for automating common agent tasks (DF registration, etc.). </b>
   </ul>

   Application programmers must write their own agents as
   <code>Agent</code> subclasses, adding specific behaviours as needed
   and exploiting <code>Agent</code> class capabilities.
   
   @author Giovanni Rimassa - Universita` di Parma
   @version $Date: 2002/04/05 16:26:12 $ $Revision: 2.73 $
 */

public class Agent implements Runnable, Serializable, TimerListener {
	protected boolean traceSentMSG=false,
		traceRecvdMSG=false;

  private final static short UNPROTECTMYPOINTER = 0;
  /**
  @serial
  */

  // This inner class is used to force agent termination when a signal
  // from the outside is received
  private class AgentDeathError extends Error {

    AgentDeathError() {
      super("Agent " + Thread.currentThread().getName() + " has been terminated.");
    }

  }

  private static class AgentInMotionError extends Error {
    AgentInMotionError() {
      super("Agent " + Thread.currentThread().getName() + " is about to move or be cloned.");
    }
  }

  // This class manages bidirectional associations between Timer and
  // Behaviour objects, using hash tables. This class is fully
  // synchronized because is accessed both by agent internal thread
  // and high priority Timer Dispatcher thread.
  private static class AssociationTB {
    private Map BtoT = new HashMap();
    private Map TtoB = new HashMap();

    public synchronized void addPair(Behaviour b, Timer t) {
      BtoT.put(b, t);
      TtoB.put(t, b);
    }

    public synchronized void removeMapping(Behaviour b) {
      Timer t = (Timer)BtoT.remove(b);
      if(t != null) {
	TtoB.remove(t);
      }
    }

    public synchronized void removeMapping(Timer t) {
      Behaviour b = (Behaviour)TtoB.remove(t);
      if(b != null) {
	BtoT.remove(b);
      }
    }

    public synchronized Timer getPeer(Behaviour b) {
      return (Timer)BtoT.get(b);
    }

    public synchronized Behaviour getPeer(Timer t) {
      return (Behaviour)TtoB.get(t);
    }

    public synchronized Iterator timers() {
      return TtoB.keySet().iterator();
    }

  } // End of AssociationTB class


  // A simple class for a boolean condition variable
  private static class CondVar {
    private boolean value = false;

    public synchronized void waitOn() throws InterruptedException {
      while(!value) {
	wait();
      }
    }

    public synchronized void set() {
      value = true;
      notifyAll();
    }

  } // End of CondVar class



  /**
     Schedules a restart for a behaviour, after a certain amount of
     time has passed.
     @param b The behaviour to restart later.
     @param millis The amount of time to wait before restarting
     <code>b</code>.
     @see jade.core.behaviours.Behaviour#block(long millis)
  */
  public void restartLater(Behaviour b, long millis) {
    if (millis <= 0) 
    	return;
    Timer t = new Timer(System.currentTimeMillis() + millis, this);
    // The following block of code must be synchronized with the operations
  	// carried out by the TimerDispatcher. In fact it could be the case that
  	// 1) A behaviour blocks for a very short time --> A Timer is added
    // to the TimerDispatcher
  	// 2) The Timer immediately expires and the TimerDispatcher try to 
    // restart the behaviour before the pair (b, t) is added to the 
    // pendingTimers of this agent.
  	synchronized (theDispatcher) {
	    t = theDispatcher.add(t);
  	  pendingTimers.addPair(b, t);
    }
  }

  /**
     Restarts the behaviour associated with t. 
     This method runs within the time-critical Timer Dispatcher thread and
     is not intended to be called by users. It is defined public only because
     is part of the <code>TimerListener</code> interface.
   */
  public void doTimeOut(Timer t) {
    Behaviour b = pendingTimers.getPeer(t);
    if(b != null) {
      b.restart();
    }
    else {
    	System.out.println("Warning: No mapping found for expired timer "+t.expirationTime());
    }
  }

  /**
     Notifies this agent that one of its behaviours has been restarted
     for some reason. This method clears any timer associated with
     behaviour object <code>b</code>, and it is unneeded by
     application level code. To explicitly schedule behaviours, use
     <code>block()</code> and <code>restart()</code> methods.
     @param b The behaviour object which was restarted.
     @see jade.core.behaviours.Behaviour#restart()
  */
  public void notifyRestarted(Behaviour b) {
    Timer t = pendingTimers.getPeer(b);
    if(t != null) {
      pendingTimers.removeMapping(b);
      theDispatcher.remove(t);
    }

    // Did this restart() cause the root behaviour to become runnable ?
    // If so, put the root behaviour back into the ready queue.
    Behaviour root = b.root();
    if(root.isRunnable())
      myScheduler.restart(root);
  }

  /**
     Out of band value for Agent Platform Life Cycle states.
  */
  public static final int AP_MIN = 0;   // Hand-made type checking

  /**
     Represents the <em>initiated</em> agent state.
  */
  public static final int AP_INITIATED = 1;

  /**
     Represents the <em>active</em> agent state.
  */
  public static final int AP_ACTIVE = 2;

  /**
     Represents the <em>idle</em> agent state.
  */
  public static final int AP_IDLE = 3;

  /**
     Represents the <em>suspended</em> agent state.
  */
  public static final int AP_SUSPENDED = 4;

  /**
     Represents the <em>waiting</em> agent state.
  */
  public static final int AP_WAITING = 5;

  /**
     Represents the <em>deleted</em> agent state.
  */
  public static final int AP_DELETED = 6;

  /**
     Represents the <code>transit</code> agent state.
  */
  public static final int AP_TRANSIT = 7;

  // Non compliant states, used internally. Maybe report to FIPA...
  /**
     Represents the <code>copy</code> agent state.
  */
  static final int AP_COPY = 8;

  /**
     Represents the <code>gone</code> agent state. This is the state
     the original instance of an agent goes into when a migration
     transaction successfully commits.
  */
  static final int AP_GONE = 9;

  /**
     Out of band value for Agent Platform Life Cycle states.
  */
  public static final int AP_MAX = 10;    // Hand-made type checking

  private static final AgentState[] STATES = new AgentState[] { 
    new AgentState("Illegal MIN state"),
    new AgentState("Initiated"),
    new AgentState("Active"),
    new AgentState("Idle"),
    new AgentState("Suspended"),
    new AgentState("Waiting"),
    new AgentState("Deleted"),
    new AgentState("Transit"),
    new AgentState("Copy"),
    new AgentState("Gone"),
    new AgentState("Illegal MAX state")
  };

  /**
     These constants represent the various Domain Life Cycle states
  */

  /**
     Out of band value for Domain Life Cycle states.
  */
  public static final int D_MIN = 9;     // Hand-made type checking

  /**
     Represents the <em>active</em> agent state.
  */
  public static final int D_ACTIVE = 10;

  /**
     Represents the <em>suspended</em> agent state.
  */
  public static final int D_SUSPENDED = 20;

  /**
     Represents the <em>retired</em> agent state.
  */
  public static final int D_RETIRED = 30;

  /**
     Represents the <em>unknown</em> agent state.
  */
  public static final int D_UNKNOWN = 40;

  /**
     Out of band value for Domain Life Cycle states.
  */
  public static final int D_MAX = 41;    // Hand-made type checking




  /**
     The Agent ID for the AMS of this platform.
   */
  static AID AMS;

  /**
     The Agent ID for the Default DF of this platform.
   */
  static AID DEFAULT_DF;

  /**
     Get the Agent ID for the platform AMS.
     @return An <code>AID</code> object, that can be used to contact
     the AMS of this platform.
  */
  public static final AID getAMS() {
    return AMS;
  }

  /**
     Get the Agent ID for the platform default DF.
     @return An <code>AID</code> object, that can be used to contact
     the default DF of this platform.
  */
  public static final AID getDefaultDF() {
    return DEFAULT_DF;
  }

  private int       msgQueueMaxSize = 0;
  private transient MessageQueue msgQueue = new MessageQueue(msgQueueMaxSize);
  private transient List o2aQueue;
  private int o2aQueueSize;
  private transient Map o2aLocks = new HashMap();
  private transient AgentToolkit myToolkit;

  /**
  @serial
  */
  private String myName = null;
  
  /**
  @serial
  */
  private AID myAID = null;

  /**
  @serial
  */
  private String myHap = null;

  private transient Object stateLock = new Object(); // Used to make state transitions atomic
  private transient Object waitLock = new Object();  // Used for agent waiting
  private transient Object suspendLock = new Object(); // Used for agent suspension
  private transient Object principalLock = new Object(); // Used to make principal transitions atomic

  private transient Thread myThread;
  private transient TimerDispatcher theDispatcher;

  /**
  @serial
  */
  private Scheduler myScheduler;

  private transient AssociationTB pendingTimers = new AssociationTB();

  // Free running counter that increments by one for each message
  // received.
  /**
  @serial
  */
  private int messageCounter = 0 ;


  // Individual agent capabilities
  private transient Map languages = new HashMap();
  private transient Map ontologies = new HashMap();

  /**
     The <code>Behaviour</code> that is currently executing.
     @see jade.core.behaviours.Behaviour
     @serial
  */
  protected Behaviour currentBehaviour;

  /**
     Last message received.
     @see jade.lang.acl.ACLMessage
     @serial
  */
  protected ACLMessage currentMessage;

  // This variable is 'volatile' because is used as a latch to signal
  // agent suspension and termination from outside world.
  /**
  @serial
  */
  private volatile int myAPState;
  
//__SECURITY__BEGIN
  private Authority authority;
  private String ownership = jade.security.JADEPrincipal.NONE;
  private AgentPrincipal principal = null;
  private CertificateFolder certs = new CertificateFolder();
//__SECURITY__END
  
  /**
     This flag is used to distinguish the normal AP_ACTIVE state from
     the particular case in which the agent state is set to AP_ACTIVE
     during agent termination to allow it to deregister with the AMS. 
     In this case in fact a call to <code>doDelete()</code>, 
     <code>doMove()</code>, <code>doClone()</code> and <code>doSuspend()</code>
     should have no effect.
  */
  private boolean terminating = false;

  // These two variables are used as temporary buffers for
  // mobility-related parameters
  private transient Location myDestination;
  private transient String myNewName;

  // Temporary buffer for agent suspension
  /**
  @serial
  */
  private int myBufferedState = AP_MIN;

  /**
     Default constructor.
  */
  public Agent() {
    setState(AP_INITIATED);
    myScheduler = new Scheduler(this);
    Runtime rt = Runtime.instance();
    theDispatcher = rt.getTimerDispatcher();
  }
    
    
    /** Declared transient because the container changes in case
     * of agent migration.
     **/
    private transient jade.wrapper.AgentContainer myContainer = null;

   /**
    * Return a controller for this agents container. 
    * @return jade.wrapper.AgentContainer The proxy container for this agent.
    */
   public final jade.wrapper.AgentContainer getContainerController() {
     if (myContainer == null) {  // first time called
       myContainer = new jade.wrapper.AgentContainer((AgentContainerImpl)myToolkit, getHap());
     }
     return myContainer;
   }



  /**
  * This method must be overridden by programmers in order to pass 
  * arguments to the agent.
  * Otherwise, to pass argument to the agent by command line or using the RMA GUI
  * see the programmer's guide for a better documentation.
  *
  * @param args an array of string (as passed on the command line - Unix-like syntax).
  * @deprecated use the method <code>getArguments</code> instead
  */
    public void setArguments(String args[]) {}
  
    private transient Object[] arguments = null;  // array of arguments
    /**
     * Called by AgentContainerImpl in order to pass arguments to a
     * just created Agent.
     **/
    public final void setArguments(Object args[]) {
	// I have declared the method final otherwise getArguments would not work!
	arguments=args;
	if (arguments != null) { //FIXME. This code goes away with the depcreated setArguments(String[]) method
	    String sargs[] = new String[args.length];
	    for (int i=0; i<args.length; i++) {
		if(args[i]!=null) sargs[i]=args[i].toString();
	    }
	    setArguments(sargs);
	}
    }

    /**
     * Return the array of arguments as they were set by the previous 
     * call of the method <code>setArguments</code>.
     * <p> Take care that the arguments are transient and they do not
     * migrate with the agent neither are cloned with the agent!
     **/
    protected Object[] getArguments() {
	return arguments;
    }
    

  /**
     Method to query the agent local name.
     @return A <code>String</code> containing the local agent name
     (e.g. <em>peter</em>).
  */
  public final String getLocalName() {
    return myName;
  }

  /**
     Method to query the agent complete name (<em><b>GUID</b></em>).
     @return A <code>String</code> containing the complete agent name
     (e.g. <em>peter@fipa.org:50</em>).
  */
  public final String getName() {
    return myName + '@' + myHap;
  }

  /**
     Method to query the private Agent ID. Note that this Agent ID is
     <b>different</b> from the one that is registered with the
     platform AMS.
     @return An <code>Agent ID</code> object, containing the complete
     agent GUID, addresses and resolvers.
  */
  public final AID getAID() {
    return myAID;
  }

  /**
     This method adds a new platform address to the AID of this Agent.
     It is called by the container when a new MTP is activated
     in the platform (in the local container - installMTP() -  
     or in a remote container - updateRoutingTable()) to keep the 
     Agent AID updated.
   */
  synchronized void addPlatformAddress(String address) { // Mutual exclusion with Agent.powerUp()
		if (myAID != null) {
			// Cloning the AID is necessary as the agent may be using its AID.
			// If this is the case a ConcurrentModificationException would be thrown
			myAID = (AID)myAID.clone(); 
			myAID.addAddresses(address);
		}
  }
  
  /**
     This method removes an old platform address from the AID of this Agent.
     It is called by the container when a new MTP is deactivated
     in the platform (in the local container - uninstallMTP() -  
     or in a remote container - updateRoutingTable()) to keep the 
     Agent AID updated.
   */
  synchronized void removePlatformAddress(String address) { // Mutual exclusion with Agent.powerUp()
		if (myAID != null) {
			// Cloning the AID is necessary as the agent may be using its AID.
			// If this is the case a ConcurrentModificationException would be thrown
			myAID = (AID)myAID.clone(); 
			myAID.removeAddresses(address);
		}
  }

  /**
     Method to query the agent home address. This is the address of
     the platform where the agent was created, and will never change
     during the whole lifetime of the agent.

     @return A <code>String</code> containing the agent home address
     (e.g. <em>iiop://fipa.org:50/acc</em>).
  */
  public final String getHap() {
    return myHap;
  }

  /**
     Method to retrieve the location this agent is currently at.
     @return A <code>Location</code> object, describing the location
     where this agent is currently running.
   */
  public Location here() {
    return myToolkit.here();
  }
  
  public Authority getAuthority() {
  	if (myToolkit == null)
  		return null;
    return myToolkit.getAuthority();
  }

  static void initReservedAIDs(AID amsID, AID defaultDfID) {
    AMS = amsID;
    DEFAULT_DF = defaultDfID;
  }

  /**
     Adds a Content Language codec to the agent capabilities. When an
     agent wants to provide automatic support for a specific content
     language, it must use an implementation of the <code>Codec</code>
     interface for the specific content language, and add it to its
     languages table with this method.
     @param languageName The symbolic name to use for the language.
     @param translator A translator for the specific content language,
     able to translate back and forth between text strings and Frame
     objects.
     @see jade.core.Agent#deregisterLanguage(String languageName)
     @see jade.lang.Codec
  */
  public void registerLanguage(String languageName, Codec translator) {
    languages.put(new CaseInsensitiveString(languageName), translator);
  }


  /**
     Looks a content language up into the supported languages table.
     @param languageName The name of the desired content language.
     @return The translator for the given language, or
     <code>null</code> if no translator was found.
   */
  public Codec lookupLanguage(String languageName) {
    Codec result = (Codec)languages.get(new CaseInsensitiveString(languageName));
    return result;
  }

  /**
     Removes a Content Language from the agent capabilities.
     @param languageName The name of the language to remove.
     @see jade.core.Agent#registerLanguage(String languageName, Codec translator)
   */
  public void deregisterLanguage(String languageName) {
    languages.remove(new CaseInsensitiveString(languageName));
  }

  /**
     Adds an Ontology to the agent capabilities. When an agent wants
     to provide automatic support for a specific ontology, it must use
     an implementation of the <code>Ontology</code> interface for the
     specific ontology and add it to its ontologies table with this
     method.
     @param ontologyName The symbolic name to use for the ontology
     @param o An ontology object, that is able to convert back and
     forth between Frame objects and application specific Java objects
     representing concepts.
     @see jade.core.Agent#deregisterOntology(String ontologyName)
     @see jade.onto.Ontology
   */
  public void registerOntology(String ontologyName, Ontology o) {
    ontologies.put(new CaseInsensitiveString(ontologyName), o);
  }

  /**
     Looks an ontology up into the supported ontologies table.
     @param ontologyName The name of the desired ontology.
     @return The given ontology, or <code>null</code> if no such named
     ontology was found.
   */
  public Ontology lookupOntology(String ontologyName) {
    Ontology result = (Ontology)ontologies.get(new CaseInsensitiveString(ontologyName));
    return result;
  }

  /**
     Removes an Ontology from the agent capabilities.
     @param ontologyName The name of the ontology to remove.
     @see jade.core.Agent#registerOntology(String ontologyName, Ontology o)
   */
  public void deregisterOntology(String ontologyName) {
    ontologies.remove(new CaseInsensitiveString(ontologyName));
  }

  //__JADE_ONLY__BEGIN
  /**
     Builds a Java object out of an ACL message. This method uses the
     <code>:language</code> slot to select a content language and the
     <code>:ontology</code> slot to select an ontology. Then the
     <code>:content</code> slot is interpreted according to the chosen
     language and ontology, to build an object of a user defined class.
     @param msg The ACL message from which a suitable Java object will
     be built.
     @return A new list of Java objects, each object representing an element
     of the t-uple of the the message content in the
     given content language and ontology.
     @exception jade.domain.FIPAException If some problem related to
     the content language or to the ontology is detected.
     @see jade.core.Agent#registerLanguage(String languageName, Codec translator)
     @see jade.core.Agent#registerOntology(String ontologyName, Ontology o)
     @see jade.core.Agent#fillContent(ACLMessage msg, java.util.List content)
		 @deprecated This support to message-content (both <code>fillContent</code> 
		 and <code>extractContent</code>) will not 
		 be ported into the CLDC-J2ME environment. In the long-term, it will be
		 replaced with the new message-content support implemented
		 by jade.content.ContentManager. In the short-term, 
		 <ul>
		 <li> in the J2SE environment this deprecated method can 
		 temporarily continue to be used
		 <li> in the PersonalJava environment the equivalent methods 
		 <code>fillMsgContent</code> and <code>extractMsgContent</code>
		 should be instead used that use
		 jade.util.leap.List instead of java.util.List, the latter being not supported in PersonalJava 
		 </ul>
   */
  public java.util.List extractContent(ACLMessage msg) throws FIPAException {
  	ArrayList l = (ArrayList) extractMsgContent(msg);
  	return l.toList();
  }

  /**
    Fills the <code>:content</code> slot of an ACL message with the string
    representation of a t-uple of user defined ontological objects. Each 
    Java object in the given list
    is first converted into a <code>Frame</code> object according to the
    ontology present in the <code>:ontology</code> message slot, then the
    <code>Frame</code> is translated into a <code>String</code> using the codec
    for the content language indicated by the <code>:language</code> message
    slot.
    <p>
    Notice that this method works properly only if in the Ontology each
    Java class has been registered to play just one role, otherwise
    ambiguity of role playing cannot be solved automatically.
    @param msg The ACL message whose content will be filled.
    @param content A list of Java objects that will be converted into a string and
    written inti the <code>:content</code> slot. This object must be an instance
    of a class registered into the ontology named in the <code>:ontology</code>
    message slot.
    @exception jade.domain.FIPAException This exception is thrown if the
    <code>:language</code> or <code>:ontology</code> message slots contain an
    unknown name, or if some problem occurs during the various translation steps.
    @see jade.core.Agent#extractContent(ACLMessage msg)
    @see jade.core.Agent#registerLanguage(String languageName, Codec translator)
    @see jade.core.Agent#registerOntology(String ontologyName, Ontology o)
		 @deprecated This support to message-content (both <code>fillContent</code> 
		 and <code>extractContent</code>) will not 
		 be ported into the CLDC-J2ME environment. In the long-term, it will be
		 replaced with the new message-content support implemented
		 by jade.content.ContentManager. In the short-term, 
		 <ul>
		 <li> in the J2SE environment this deprecated method can 
		 temporarily continue to be used
		 <li> in the PersonalJava environment the equivalent methods 
		 <code>fillMsgContent</code> and <code>extractMsgContent</code>
		 should be instead used that use
		 jade.util.leap.List instead of java.util.List, the latter being not supported in PersonalJava 
		 </ul>
   */
  public void fillContent(ACLMessage msg, java.util.List content) throws FIPAException {
    ArrayList l = new ArrayList();
    l.fromList(content);
    fillMsgContent(msg, l);
  }
  //__JADE_ONLY__END
  	

  /**
     Builds a Java object out of an ACL message. This method uses the
     <code>:language</code> slot to select a content language and the
     <code>:ontology</code> slot to select an ontology. Then the
     <code>:content</code> slot is interpreted according to the chosen
     language and ontology, to build an object of a user defined class.
     <br>
		 <i>This support to message-content (both <code>fillContent</code> 
		 and <code>extractContent</code>) will not 
		 be ported into the CLDC-J2ME environment. In the long-term, it will be
		 replaced with the new message-content support implemented
		 by jade.content.ContentManager. In the short-term, 
		 <ul>
		 <li> in the J2SE environment this deprecated method can 
		 temporarily continue to be used
		 <li> in the PersonalJava environment the equivalent methods 
		 <code>fillMsgContent</code> and <code>extractMsgContent</code>
		 should be instead used that use
		 jade.util.leap.List instead of java.util.List, the latter being not supported in PersonalJava 
		 </ul>
		 </i>
     @param msg The ACL message from which a suitable Java object will
     be built.
     @return A new list of Java objects, each object representing an element
     of the t-uple of the the message content in the
     given content language and ontology.
     @exception jade.domain.FIPAException If some problem related to
     the content language or to the ontology is detected.
     @see jade.core.Agent#registerLanguage(String languageName, Codec translator)
     @see jade.core.Agent#registerOntology(String ontologyName, Ontology o)
     @see jade.core.Agent#fillMsgContent(ACLMessage msg, List content)
   */
  public List extractMsgContent(ACLMessage msg) throws FIPAException {
    Codec c = lookupLanguage(msg.getLanguage());
    if(c == null)
      throw new FIPAException("Unknown Content Language");
    Ontology o = lookupOntology(msg.getOntology());
    if(o == null)
      throw new FIPAException("Unknown Ontology");
    try {
      List tuple = c.decode(msg.getContent(), o);
      return o.createObject(tuple);
    }
    catch(Codec.CodecException cce) {
      cce.getNested().printStackTrace();
      throw new FIPAException("Codec error: " + cce.getMessage());
    }
    catch(OntologyException oe) {
      oe.printStackTrace();
      throw new FIPAException("Ontology error: " + oe.getMessage());
    }

  }

  /**
    Fills the <code>:content</code> slot of an ACL message with the string
    representation of a t-uple of user defined ontological objects. Each 
    Java object in the given list
    is first converted into a <code>Frame</code> object according to the
    ontology present in the <code>:ontology</code> message slot, then the
    <code>Frame</code> is translated into a <code>String</code> using the codec
    for the content language indicated by the <code>:language</code> message
    slot.
    <p>
    Notice that this method works properly only if in the Ontology each
    Java class has been registered to play just one role, otherwise
    ambiguity of role playing cannot be solved automatically.
     <br>
		 <i>This support to message-content (both <code>fillContent</code> 
		 and <code>extractContent</code>) will not 
		 be ported into the CLDC-J2ME environment. In the long-term, it will be
		 replaced with the new message-content support implemented
		 by jade.content.ContentManager. In the short-term, 
		 <ul>
		 <li> in the J2SE environment this deprecated method can 
		 temporarily continue to be used
		 <li> in the PersonalJava environment the equivalent methods 
		 <code>fillMsgContent</code> and <code>extractMsgContent</code>
		 should be instead used that use
		 jade.util.leap.List instead of java.util.List, the latter being not supported in PersonalJava 
		 </ul>
		 </i>
    @param msg The ACL message whose content will be filled.
    @param content A list of Java objects that will be converted into a string and
    written inti the <code>:content</code> slot. This object must be an instance
    of a class registered into the ontology named in the <code>:ontology</code>
    message slot.
    @exception jade.domain.FIPAException This exception is thrown if the
    <code>:language</code> or <code>:ontology</code> message slots contain an
    unknown name, or if some problem occurs during the various translation steps.
    @see jade.core.Agent#extractMsgContent(ACLMessage msg)
    @see jade.core.Agent#registerLanguage(String languageName, Codec translator)
    @see jade.core.Agent#registerOntology(String ontologyName, Ontology o)
   */
  public void fillMsgContent(ACLMessage msg, List content) throws FIPAException {
    Codec c = lookupLanguage(msg.getLanguage());
    if(c == null)
      throw new FIPAException("Unknown Content Language");
    Ontology o = lookupOntology(msg.getOntology());
    if(o == null)
      throw new FIPAException("Unknown Ontology");
    try {
      List l = new ArrayList();
      Frame f;
      for (int i=0; i<content.size(); i++) {
	f = o.createFrame(content.get(i), o.getRoleName(content.get(i).getClass()));
	l.add(f);
      }
      String s = c.encode(l, o);
      msg.setContent(s);
    }
    catch(OntologyException oe) {
      oe.printStackTrace();
      throw new FIPAException("Ontology error: " + oe.getMessage());
    }

  }

  // This is used by the agent container to wait for agent termination
  void join() {
    try {
      myThread.join();
    }
    catch(InterruptedException ie) {
      ie.printStackTrace();
    }

  }

  /**
     Set message queue size. This method allows to change the number
     of ACL messages that can be buffered before being actually read
     by the agent or discarded.
     @param newSize A non negative integer value to set message queue
     size to. Passing 0 means unlimited message queue.  When the number of 
     buffered
     messages exceeds this value, older messages are discarded
     according to a <b><em>FIFO</em></b> replacement policy.
     @throws IllegalArgumentException If <code>newSize</code> is negative.
     @see jade.core.Agent#getQueueSize()
  */
  public void setQueueSize(int newSize) throws IllegalArgumentException {
    msgQueue.setMaxSize(newSize);
    msgQueueMaxSize = newSize;
  }

  /**
     Reads message queue size. A zero value means that thee message
     queue is unbounded (its size is limited only by amount of
     available memory).
     @return The actual size of the message queue.
     @see jade.core.Agent#setQueueSize(int newSize)
  */
  public int getQueueSize() {
    return msgQueue.getMaxSize();
  }

	public void setOwnership(String ownership) {
	  this.ownership = ownership;
	}

	//__SECURITY__BEGIN
	public static String extractUsername(String ownership) {
		int dot2 = ownership.indexOf(':');
		return (dot2 != -1) ?
				ownership.substring(0, dot2) : ownership;
	}

	public static byte[] extractPassword(String ownership) {
		int dot2 = ownership.indexOf(':');
		return (dot2 != -1 && dot2 < ownership.length() - 1) ?
				ownership.substring(dot2 + 1, ownership.length()).getBytes() : new byte[] {};
	}

	public void setPrincipal(CertificateFolder certs) {
		AgentPrincipal old = getPrincipal();
		synchronized (principalLock) {
			this.certs = certs;
			this.principal = (AgentPrincipal)certs.getIdentityCertificate().getSubject();
			notifyChangedAgentPrincipal(old, certs);
		}
	}

	public AgentPrincipal getPrincipal() {
		AgentPrincipal p = null;
		if (myToolkit != null) {
			synchronized (principalLock) {
				Authority authority = getAuthority();
				if (principal == null) {
					String user = extractUsername(ownership);
					principal = authority.createAgentPrincipal(myAID, user);
				}
				p = principal;
			}
		}
		return p;
	}
	
	public CertificateFolder getCertificateFolder() {
		return certs;
	}
	
	private void doPrivileged(PrivilegedExceptionAction action) throws Exception {
		getAuthority().doAsPrivileged(action, getCertificateFolder());
	}
	//__SECURITY__END

  private void setState(int state) {
    synchronized (stateLock) {
      int oldState = myAPState;
      myAPState = state;
      notifyChangedAgentState(oldState, myAPState);
    }
  }

  /**
     Read current agent state. This method can be used to query an
     agent for its state from the outside.
     @return the Agent Platform Life Cycle state this agent is currently in.
   */
  public int getState() {
    int state;
    synchronized(stateLock) {
      state = myAPState;
    }
    return state;
  }

  AgentState getAgentState() {
    return STATES[getState()];
  }

  // State transition methods for Agent Platform Life-Cycle

  /**
     Make a state transition from <em>initiated</em> to
     <em>active</em> within Agent Platform Life Cycle. Agents are
     started automatically by JADE on agent creation and 
     this method should not be
     used by application developers, unless creating some kind of
     agent factory. This method starts the embedded thread of the agent.
     <b> It is highly descouraged the usage of this method </b> because it
     does not guarantee agent autonomy; soon this policy will
     be enfored by removing or restricting the scope of the method
     @param name The local name of the agent.
  */
  public void doStart(String name) {

    // FIXME: Temporary hack for JSP example
      if(myToolkit == null) {
	  Runtime rt = Runtime.instance();
	  setToolkit(rt.getDefaultToolkit());
	  theDispatcher = rt.getTimerDispatcher();
      }

    if(myToolkit == null)
      throw new InternalError("Trying to start an agent without proper runtime support.");
    myToolkit.handleStart(name, this);
  }

  /**
     Make a state transition from <em>active</em> to
     <em>transit</em> within Agent Platform Life Cycle. This method
     is intended to support agent mobility and is called either by the
     Agent Platform or by the agent itself to start a migration process.
     @param destination The <code>Location</code> to migrate to.
  */
  public void doMove(Location destination) {
    synchronized(stateLock) {
      if(((myAPState == AP_ACTIVE)||(myAPState == AP_WAITING)||(myAPState == AP_IDLE)) && !terminating) {
	myBufferedState = myAPState;
	setState(AP_TRANSIT);
	myDestination = destination;

	// Real action will be executed in the embedded thread
	if(!myThread.equals(Thread.currentThread()))
	  myThread.interrupt();
      }
    }

  }

  /**
     Make a state transition from <em>active</em> to
     <em>copy</em> within Agent Platform Life Cycle. This method
     is intended to support agent mobility and is called either by the
     Agent Platform or by the agent itself to start a clonation process.
     @param destination The <code>Location</code> where the copy agent will start.
     @param newName The name that will be given to the copy agent.
  */
  public void doClone(Location destination, String newName) {
    synchronized(stateLock) {
      if(((myAPState == AP_ACTIVE)||(myAPState == AP_WAITING)||(myAPState == AP_IDLE)) && !terminating) {
	myBufferedState = myAPState;
	setState(AP_COPY);
	myDestination = destination;
	myNewName = newName;

	// Real action will be executed in the embedded thread
	if(!myThread.equals(Thread.currentThread()))
	  myThread.interrupt();
      }
    }
  }

  /**
     Make a state transition from <em>transit</em> or
     <code>copy</code> to <em>active</em> within Agent Platform Life
     Cycle. This method is intended to support agent mobility and is
     called by the destination Agent Platform when a migration process
     completes and the mobile agent is about to be restarted on its
     new location.
  */
  void doExecute() {
    synchronized(stateLock) {
      // FIXME: Hack to manage agents moving while in AP_IDLE state,
      // but with pending timers. The correct solution would be to
      // restore all pending timers.
      if(myBufferedState == AP_IDLE)
	myBufferedState = AP_ACTIVE;

      setState(myBufferedState);
      myBufferedState = AP_MIN;
      activateAllBehaviours();
    }
  }

  /**
     Make a state transition from <em>transit</em> to <em>gone</em>
     state. This state is only used to label the original copy of a
     mobile agent which migrated somewhere.
  */
  void doGone() {
    synchronized(stateLock) {
      setState(AP_GONE);
    }
  }

  /**
     Make a state transition from <em>active</em> or <em>waiting</em>
     to <em>suspended</em> within Agent Platform Life Cycle; the
     original agent state is saved and will be restored by a
     <code>doActivate()</code> call. This method can be called from
     the Agent Platform or from the agent iself and stops all agent
     activities. Incoming messages for a suspended agent are buffered
     by the Agent Platform and are delivered as soon as the agent
     resumes. Calling <code>doSuspend()</code> on a suspended agent
     has no effect.
     @see jade.core.Agent#doActivate()
  */
  public void doSuspend() {
    synchronized(stateLock) {
      if(((myAPState == AP_ACTIVE)||(myAPState == AP_WAITING)||(myAPState == AP_IDLE)) && !terminating) {
	myBufferedState = myAPState;
	setState(AP_SUSPENDED);
      }
    }
    if(myAPState == AP_SUSPENDED) {
      if(myThread.equals(Thread.currentThread())) {
	waitUntilActivate();
      }
    }
  }

  /**
     Make a state transition from <em>suspended</em> to
     <em>active</em> or <em>waiting</em> (whichever state the agent
     was in when <code>doSuspend()</code> was called) within Agent
     Platform Life Cycle. This method is called from the Agent
     Platform and resumes agent execution. Calling
     <code>doActivate()</code> when the agent is not suspended has no
     effect.
     @see jade.core.Agent#doSuspend()
  */
  public void doActivate() {
    synchronized(stateLock) {
      if(myAPState == AP_SUSPENDED) {
	setState(myBufferedState);
      }
    }
    if(myAPState != AP_SUSPENDED) {
      switch(myBufferedState) {
      case AP_ACTIVE:
	activateAllBehaviours();
	synchronized(suspendLock) {
	  myBufferedState = AP_MIN;
	  suspendLock.notifyAll();
	}
	break;
      case AP_WAITING:
	doWake();
	break;
      }
    }
  }

  /**
     Make a state transition from <em>active</em> to <em>waiting</em>
     within Agent Platform Life Cycle. This method can be called by
     the Agent Platform or by the agent itself and causes the agent to
     block, stopping all its activities until some event happens. A
     waiting agent wakes up as soon as a message arrives or when
     <code>doWake()</code> is called. Calling <code>doWait()</code> on
     a suspended or waiting agent has no effect.
     @see jade.core.Agent#doWake()
  */
  public void doWait() {
    doWait(0);
  }

  /**
     Make a state transition from <em>active</em> to <em>waiting</em>
     within Agent Platform Life Cycle. This method adds a timeout to
     the other <code>doWait()</code> version.
     @param millis The timeout value, in milliseconds.
     @see jade.core.Agent#doWait()
  */
  public void doWait(long millis) {
    synchronized(stateLock) {
      if(myAPState == AP_ACTIVE)
	setState(AP_WAITING);
    }
    if(myAPState == AP_WAITING) {
      if(myThread.equals(Thread.currentThread())) {
	waitUntilWake(millis);
      }
    }
  }

  /**
     Make a state transition from <em>waiting</em> to <em>active</em>
     within Agent Platform Life Cycle. This method is called from
     Agent Platform and resumes agent execution. Calling
     <code>doWake()</code> when an agent is not waiting has no effect.
     @see jade.core.Agent#doWait()
  */
  public void doWake() {
    synchronized(stateLock) {
      if((myAPState == AP_WAITING) || (myAPState == AP_IDLE)) {
	setState(AP_ACTIVE);
      }
    }
    if(myAPState == AP_ACTIVE) {
      activateAllBehaviours();
      synchronized(waitLock) {
        waitLock.notifyAll(); // Wakes up the embedded thread
      }
    }
  }

  // This method handles both the case when the agents decides to exit
  // and the case in which someone else kills him from outside.

  /**
     Make a state transition from <em>active</em>, <em>suspended</em>
     or <em>waiting</em> to <em>deleted</em> state within Agent
     Platform Life Cycle, thereby destroying the agent. This method
     can be called either from the Agent Platform or from the agent
     itself. Calling <code>doDelete()</code> on an already deleted
     agent has no effect.
  */
  public void doDelete() {
    synchronized(stateLock) {
      if(myAPState != AP_DELETED && !terminating) {
	setState(AP_DELETED);
	if(!myThread.equals(Thread.currentThread()))
	  myThread.interrupt();
      }
    }
  }

  // This is to be called only by the scheduler
  void doIdle() {
    synchronized(stateLock) {
      if(myAPState != AP_IDLE)
	setState(AP_IDLE);
    }
  }

  /**
     Write this agent to an output stream; this method can be used to
     record a snapshot of the agent state on a file or to send it
     through a network connection. Of course, the whole agent must
     be serializable in order to be written successfully.
     @param s The stream this agent will be sent to. The stream is
     <em>not</em> closed on exit.
     @exception IOException Thrown if some I/O error occurs during
     writing.
     @see jade.core.Agent#read(InputStream s)
  */
  public void write(OutputStream s) throws IOException {
    ObjectOutput out = new ObjectOutputStream(s);
    out.writeUTF(myName);
    out.writeObject(this);
  }

  /**
     Read a previously saved agent from an input stream and restarts
     it under its former name. This method can realize some sort of
     mobility through time, where an agent is saved, then destroyed
     and then restarted from the saved copy.
     @param s The stream the agent will be read from. The stream is
     <em>not</em> closed on exit.
     @exception IOException Thrown if some I/O error occurs during
     stream reading.
     @see jade.core.Agent#write(OutputStream s)
  */
  public static void read(InputStream s) throws IOException {
    try {
      ObjectInput in = new ObjectInputStream(s);
      String name = in.readUTF();
      Agent a = (Agent)in.readObject();
      a.doStart(name);
    }
    catch(ClassNotFoundException cnfe) {
      cnfe.printStackTrace();
    }
  }

  /**
     Read a previously saved agent from an input stream and restarts
     it under a different name. This method can realize agent cloning
     through streams, where an agent is saved, then an exact copy of
     it is restarted as a completely separated agent, with the same
     state but with different identity and address.
     @param s The stream the agent will be read from. The stream is
     <em>not</em> closed on exit.
     @param agentName The name of the new agent, copy of the saved
     original one.
     @exception IOException Thrown if some I/O error occurs during
     stream reading.
     @see jade.core.Agent#write(OutputStream s)
  */
  public static void read(InputStream s, String agentName) throws IOException {
    try {
      ObjectInput in = new ObjectInputStream(s);
      String name = in.readUTF();
      Agent a = (Agent)in.readObject();
      a.doStart(agentName);
    }
    catch(ClassNotFoundException cnfe) {
      cnfe.printStackTrace();
    }
  }

  /**
     This method reads a previously saved agent, replacing the current
     state of this agent with the one previously saved. The stream
     must contain the saved state of <b>the same agent</b> that it is
     trying to restore itself; that is, <em>both</em> the Java object
     <em>and</em> the agent name must be the same.
     @param s The input stream the agent state will be read from.
     @exception IOException Thrown if some I/O error occurs during
     stream reading.
     <em>Note: This method is currently not implemented</em>
  */
  public void restore(InputStream s) throws IOException {
    // FIXME: Not implemented
  }

  /**
     This method should not be used by application code. Use the
     same-named method of <code>jade.wrapper.Agent</code> instead.
     @see jade.wrapper.Agent#putO2AObject(Object o, boolean blocking)
   */
  public void putO2AObject(Object o, boolean blocking) throws InterruptedException {
    // Drop object on the floor if object-to-agent communication is
    // disabled.
    if(o2aQueue == null)
      return;

    // If the queue has a limited capacity and it is full, discard the
    // first element
    if((o2aQueueSize != 0) && (o2aQueue.size() == o2aQueueSize))
      o2aQueue.remove(0);

    o2aQueue.add(o);

    // Reactivate the agent
    activateAllBehaviours();

    // Synchronize the calling thread on a condition associated to the
    // object
    if(blocking) {
      CondVar cond = new CondVar();

      // Store lock for later, when getO2AObject will be called
      o2aLocks.put(o, cond);

      // Sleep on the condition
      cond.waitOn();

    }

  }

  /**
     This method picks an object (if present) from the internal
     object-to-agent communication queue. In order for this method to
     work, the agent must have declared its will to accept objects
     from other software components running within its JVM. This can
     be achieved by calling the
     <code>jade.core.Agent.setEnabledO2ACommunication()</code> method.
     If the retrieved object was originally inserted by an external
     component using a blocking call, that call will return during the
     execution of this method.
     @return the first object in the queue, or <code>null</code> if
     the queue is empty.
     @see jade.wrapper.Agent#putO2AObject(Object o, boolean blocking)
     @see jade.core.Agent#setEnabledO2ACommunication(boolean enabled, int queueSize)
   */
  public Object getO2AObject() {

    // Return 'null' if object-to-agent communication is disabled
    if(o2aQueue == null)
      return null;

    if(o2aQueue.isEmpty())
      return null;

    // Retrieve the first object from the object-to-agent
    // communication queue
    Object result = o2aQueue.remove(0);

    // If some thread issued a blocking putO2AObject() call with this
    // object, wake it up
    CondVar cond = (CondVar)o2aLocks.remove(result);
    if(cond != null) {
      cond.set();
    }

    return result;
    
  }


  /**
     This method declares this agent attitude towards object-to-agent
     communication, that is, whether the agent accepts to communicate
     with other non-JADE components living within the same JVM.
     @param enabled Tells whether Java objects inserted with
     <code>putO2AObject()</code> will be accepted.
     @param queueSize If the object-to-agent communication is enabled,
     this parameter specifiies the maximum number of Java objects that
     will be queued. If the passed value is 0, no maximum limit is set
     up for the queue.

     @see jade.wrapper.Agent#putO2AObject(Object o, boolean blocking)
     @see jade.core.Agent#getO2AObject()

   */
  public void setEnabledO2ACommunication(boolean enabled, int queueSize) {
    if(enabled) {
      if(o2aQueue == null)
	o2aQueue = new ArrayList(queueSize);

      // Ignore a negative value
      if(queueSize >= 0)
	o2aQueueSize = queueSize;
    }
    else {

      // Wake up all threads blocked in putO2AObject() calls
      Iterator it = o2aLocks.values().iterator();
      while(it.hasNext()) {
	CondVar cv = (CondVar)it.next();
	cv.set();
      }

      o2aQueue = null;
    }

  }

  /**
     This method is the main body of every agent. It can handle
     automatically <b>AMS</b> registration and deregistration and
     provides startup and cleanup hooks for application programmers to
     put their specific code into.
     @see jade.core.Agent#setup()
     @see jade.core.Agent#takeDown()
  */
  public final void run() {

    try {
      AMSAgentDescription amsd = new AMSAgentDescription();
      amsd.setName(myAID);
      amsd.setOwnership(ownership);
      amsd.setState(AMSAgentDescription.ACTIVE);
      switch(myAPState) {
      case AP_INITIATED:
	setState(AP_ACTIVE);
	// No 'break' statement - fall through
      case AP_ACTIVE:
	if (myAID.equals(getAMS())) //special version for the AMS to avoid deadlock
	  ((jade.domain.ams)this).AMSRegister(amsd, myAID);
	else
	  AMSService.register(this, amsd);
        notifyStarted();
	setup();
	break;
      case AP_TRANSIT:
	doExecute();
	afterMove();
	break;
      case AP_COPY:
	doExecute();
	if (myAID.equals(getAMS())) //special version for the AMS to avoid deadlock
	  ((jade.domain.ams)this).AMSRegister(amsd, myAID);
	else
	  AMSService.register(this, amsd);
	afterClone();
	break;
      }

      mainLoop();

    }
    catch(InterruptedException ie) {
      // Do Nothing, since this is a killAgent from outside
    }
    catch(InterruptedIOException iioe) {
      // Do nothing, since this is a killAgent from outside
    }
    catch(Exception e) {
      System.err.println("***  Uncaught Exception for agent " + myName + "  ***");
      e.printStackTrace();
    }
    catch(AgentDeathError ade) {
      // Do Nothing, since this is a killAgent from outside
    }
    finally {
      switch(myAPState) {
      case AP_DELETED:
      	terminating = true;
	int savedState = getState();
	setState(AP_ACTIVE);
	takeDown();
	destroy();
	setState(savedState);
	break;
      case AP_GONE:
	break;
      default:
      	terminating = true;
	System.out.println("ERROR: Agent " + myName + " died without being properly terminated !!!");
	System.out.println("State was " + myAPState);
	savedState = getState();
	setState(AP_ACTIVE);
	takeDown();
	destroy();
	setState(savedState);
      }
    }

  }

  /**
     This protected method is an empty placeholder for application
     specific startup code. Agent developers can override it to
     provide necessary behaviour. When this method is called the agent
     has been already registered with the Agent Platform <b>AMS</b>
     and is able to send and receive messages. However, the agent
     execution model is still sequential and no behaviour scheduling
     is active yet.

     This method can be used for ordinary startup tasks such as
     <b>DF</b> registration, but is essential to add at least a
     <code>Behaviour</code> object to the agent, in order for it to be
     able to do anything.
     @see jade.core.Agent#addBehaviour(Behaviour b)
     @see jade.core.behaviours.Behaviour
  */
  protected void setup() {}

  /**
     This protected method is an empty placeholder for application
     specific cleanup code. Agent developers can override it to
     provide necessary behaviour. When this method is called the agent
     has not deregistered itself with the Agent Platform <b>AMS</b>
     and is still able to exchange messages with other
     agents. However, no behaviour scheduling is active anymore and
     the Agent Platform Life Cycle state is already set to
     <em>deleted</em>.

     This method can be used for ordinary cleanup tasks such as
     <b>DF</b> deregistration, but explicit removal of all agent
     behaviours is not needed.
  */
  protected void takeDown() {}

  /**
    Actions to perform before moving. This empty placeholder method can be
    overridden by user defined agents to execute some actions just before
    leaving an agent container for a migration.
  */
  protected void beforeMove() {}

  /**
    Actions to perform after moving. This empty placeholder method can be
    overridden by user defined agents to execute some actions just after
    arriving to the destination agent container for a migration.
  */
  protected void afterMove() {}

  /**
    Actions to perform before cloning. This empty placeholder method can be
    overridden by user defined agents to execute some actions just before
    copying an agent to another agent container.
  */
  protected void beforeClone() {}

  /**
    Actions to perform after cloning. This empty placeholder method can be
    overridden by user defined agents to execute some actions just after
    creating an agent copy to the destination agent container.
  */
  protected void afterClone() {}

  // This method is used by the Agent Container to fire up a new agent for the first time
  void powerUp(AID id, ResourceManager rm) {

    // Set this agent's name and address and start its embedded thread
    if ((myAPState == AP_INITIATED) || (myAPState == AP_TRANSIT) || (myAPState == AP_COPY)) {
      myName = id.getLocalName();
      myHap = id.getHap();
      
      synchronized (this) { // Mutual exclusion with Agent.addPlatformAddress()
		    myAID = id;
				myToolkit.setPlatformAddresses(myAID);
      }

      myThread = rm.getThread(ResourceManager.USER_AGENTS, getLocalName(), this);    
      myThread.start();
    }
  }

  private void writeObject(ObjectOutputStream out) throws IOException {
  	// Updates the queue maximum size field, before serialising
  	msgQueueMaxSize = msgQueue.getMaxSize();

    out.defaultWriteObject();
  }

  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();

    // Restore transient fields (apart from myThread, which will be set by doStart())
    msgQueue = new MessageQueue(msgQueueMaxSize);
    stateLock = new Object();
    suspendLock = new Object();
    waitLock = new Object();
    principalLock = new Object();
    pendingTimers = new AssociationTB();
    languages = new HashMap();
    ontologies = new HashMap();
    theDispatcher = Runtime.instance().getTimerDispatcher();
  }

  private void mainLoop() throws InterruptedException, InterruptedIOException {
    while(myAPState != AP_DELETED) {
      try {

	// Check for Agent state changes
	switch(myAPState) {
	case AP_WAITING:
	  waitUntilWake(0);
	  break;
	case AP_SUSPENDED:
	  waitUntilActivate();
	  break;
	case AP_TRANSIT:
	  notifyMove();
	  if(myAPState == AP_GONE) {
	    beforeMove();
	    return;
	  }
	  break;
	case AP_COPY:
	  beforeClone();
	  notifyCopy();
	  doExecute();
	  break;
	case AP_ACTIVE:
	  try {
	    // Select the next behaviour to execute
	    int oldState = myAPState;
	    currentBehaviour = myScheduler.schedule();
	    if((myAPState != oldState) &&(myAPState != AP_DELETED))
	      setState(oldState);

	    // Remember how many messages arrived
	    int oldMsgCounter = messageCounter;

	    // Just do it!
	    currentBehaviour.actionWrapper();

	    // If the current Behaviour is blocked and more messages
	    // arrived, restart the behaviour to give it another chance
	    if((oldMsgCounter != messageCounter) && (!currentBehaviour.isRunnable()))
	      currentBehaviour.restart();


	    // When it is needed no more, delete it from the behaviours queue
	    if(currentBehaviour.done()) {
	      currentBehaviour.onEnd();
	      myScheduler.remove(currentBehaviour);
	      currentBehaviour = null;
	    }
	    else {
	      synchronized(myScheduler) {
		// Need synchronized block (Crais Sayers, HP): What if
		// 1) it checks to see if its runnable, sees its not,
		//    so it begins to enter the body of the if clause
		// 2) meanwhile, in another thread, a message arrives, so
		//    the behaviour is restarted and moved to the ready list.
		// 3) now back in the first thread, the agent executes the
		//    body of the if clause and, by calling block(), moves
		//   the behaviour back to the blocked list.
		if(!currentBehaviour.isRunnable()) {
		  // Remove blocked behaviour from ready behaviours queue
		  // and put it in blocked behaviours queue
		  myScheduler.block(currentBehaviour);
		  currentBehaviour = null;
		}
	      }
	    }
	    break;
	  }
	  // Someone interrupted the agent. It could be a kill or a
	  // move/clone request...
	  catch(InterruptedException ie) {
	    switch(myAPState) {
	    case AP_DELETED:
	      throw new AgentDeathError();
	    case AP_TRANSIT:
	    case AP_COPY:
	      throw new AgentInMotionError();
	    case AP_ACTIVE:
	      System.out.println("WARNING: Spurious wakeup for agent " + getLocalName() + " in AP_ACTIVE state.");
	      break;
	    case AP_IDLE:
	      System.out.println("WARNING: Spurious wakeup for agent " + getLocalName() + " in AP_IDLE state.");
	      break;
	    }
	  }


	  // Remember how many messages arrived
	  int oldMsgCounter = messageCounter;

	  // Just do it!
	  currentBehaviour.actionWrapper();

	  // If the current Behaviour is blocked and more messages
	  // arrived, restart the behaviour to give it another chance
	  if((oldMsgCounter != messageCounter) && (!currentBehaviour.isRunnable()))
	    currentBehaviour.restart();


	  // When it is needed no more, delete it from the behaviours queue
	  if(currentBehaviour.done()) {
	  	currentBehaviour.onEnd();
	    myScheduler.remove(currentBehaviour);
	    currentBehaviour = null;
	  }
	  else {
	      synchronized(myScheduler) {
		  // Need syncrhonzied block (Crais Sayers, HP): What if
		  // 1) it checks to see if its runnable, sees its not,
		  //    so it begins to enter the body of the if clause
		  // 2) meanwhile, in another thread, a message arrives, so
		  //    the behaviour is restarted and moved to the ready list.
		  // 3) now back in the first thread, the agent executes the
		  //    body of the if clause and, by calling block(), moves
		  //   the behaviour back to the blocked list.
		  if(!currentBehaviour.isRunnable()) {
		      // Remove blocked behaviour from ready behaviours queue
		      // and put it in blocked behaviours queue
		      myScheduler.block(currentBehaviour);
		      currentBehaviour = null;
		  }
	      }
	  }
	  break;
	}

	// Now give CPU control to other agents
	Thread.yield();
      }
      catch(AgentInMotionError aime) {
	// Do nothing, since this is a doMove() or doClone() from the outside.
      }
    }

  }

  private void waitUntilWake(long millis) {
    synchronized(waitLock) {

      long timeToWait = millis;
      while(myAPState == AP_WAITING) {
	try {

	  long startTime = System.currentTimeMillis();
	  waitLock.wait(timeToWait); // Blocks on waiting state monitor for a while
	  long elapsedTime = System.currentTimeMillis() - startTime;

	  // If this was a timed wait, update time to wait; if the
	  // total time has passed, wake up.
	  if(millis != 0) {
	    timeToWait -= elapsedTime;

	    if(timeToWait <= 0)
	    setState(AP_ACTIVE);
	  }

	}
	catch(InterruptedException ie) {
	  switch(myAPState) {
	  case AP_DELETED:
	    throw new AgentDeathError();
	  case AP_TRANSIT:
	  case AP_COPY:
	    throw new AgentInMotionError();
	  }
	}
      }
    }
  }

  private void waitUntilActivate() {
    synchronized(suspendLock) {
      while(myAPState == AP_SUSPENDED) {
  try {
	  suspendLock.wait(); // Blocks on suspended state monitor
	}
	catch(InterruptedException ie) {
	  switch(myAPState) {
	  case AP_DELETED:
	    throw new AgentDeathError();
	  case AP_TRANSIT:
	  case AP_COPY:
	    // Undo the previous clone or move request
	    setState(AP_SUSPENDED);
	  }
	}
      }
    }
  }

	private void destroy() { 
		try {
			if (myAID.equals(getAMS())) {
				//special version for the AMS to avoid deadlock 
				AMSAgentDescription amsd = new AMSAgentDescription();
				amsd.setName(getAID());
				((jade.domain.ams)this).AMSDeregister(amsd, myAID);
			}
			else {
				AMSService.deregister(this);
			}
		}
		catch (FIPAException fe) {
			fe.printStackTrace();
		}
		catch (AuthException ae) {
			ae.printStackTrace();
		}

		// Remove all pending timers
		Iterator it = pendingTimers.timers();
		while (it.hasNext()) {
			Timer t = (Timer)it.next();
			theDispatcher.remove(t);
		}

		notifyDestruction();
	}

  /**
     This method adds a new behaviour to the agent. This behaviour
     will be executed concurrently with all the others, using a
     cooperative round robin scheduling.  This method is typically
     called from an agent <code>setup()</code> to fire off some
     initial behaviour, but can also be used to spawn new behaviours
     dynamically.
     @param b The new behaviour to add to the agent.
     @see jade.core.Agent#setup()
     @see jade.core.behaviours.Behaviour
  */
  public void addBehaviour(Behaviour b) {
    b.setAgent(this);
    myScheduler.add(b);
  }

  /**
     This method removes a given behaviour from the agent. This method
     is called automatically when a top level behaviour terminates,
     but can also be called from a behaviour to terminate itself or
     some other behaviour.
     @param b The behaviour to remove.
     @see jade.core.behaviours.Behaviour
  */
  public void removeBehaviour(Behaviour b) {
    b.setAgent(null);
    myScheduler.remove(b);
  }

	/*
	//!!!
	class SendAction implements jade.security.PrivilegedExceptionAction, jade.util.leap.Serializable {
		ACLMessage msg;
		
		public SendAction(ACLMessage msg) {
			this.msg = msg;
		}
		
		public void setMessage(ACLMessage msg) {
			this.msg = msg;
		}
		
		public Object run() throws AuthException {
			notifySend(msg);
			return null;
		}
	}
	
	SendAction sendAction = new SendAction();
	*/

	/**
		Send an <b>ACL</b> message to another agent. This methods sends
		a message to the agent specified in <code>:receiver</code>
		message field (more than one agent can be specified as message
		receiver).
		@param msg An ACL message object containing the actual message to
		send.
		@see jade.lang.acl.ACLMessage
	*/
	public final void send(final ACLMessage msg) {
		if(traceSentMSG)
			System.out.println("sending message="+msg+" probably from me="+getName());
		try {
			if (msg.getSender().getName().length() < 1)
				msg.setSender(myAID);
		}
		catch (NullPointerException e) {
			msg.setSender(myAID);
		}
		try {
			// Notify send
			doPrivileged(new jade.security.PrivilegedExceptionAction() {
				public Object run() throws AuthException {
					notifySend(msg);
					return null;
				}
			});
		}
		catch (Exception e) {
			e.printStackTrace();
		} 
	}

	/**
		Receives an <b>ACL</b> message from the agent message
		queue. This method is non-blocking and returns the first message
		in the queue, if any. Therefore, polling and busy waiting is
		required to wait for the next message sent using this method.
		@return A new ACL message, or <code>null</code> if no message is
		present.
		@see jade.lang.acl.ACLMessage
	*/
	public final ACLMessage receive() {
		return receive(null);
		/*
		synchronized(waitLock) {
			if(msgQueue.isEmpty()) {
				return null;
			}
			else {
				currentMessage = msgQueue.removeFirst();
				try {
				    //notifyReceived(currentMessage);
				    doPrivileged(new ReceiveAction(currentMessage));
				}
				catch (Exception e) {
					e.printStackTrace();
					//!!! discard the message
					return receive();
				}
				return currentMessage;
			}
		}
		*/
	}

	/**
		Receives an <b>ACL</b> message matching a given template. This
		method is non-blocking and returns the first matching message in
		the queue, if any. Therefore, polling and busy waiting is
		required to wait for a specific kind of message using this method.
		@param pattern A message template to match received messages
		against.
		@return A new ACL message matching the given template, or
		<code>null</code> if no such message is present.
		@see jade.lang.acl.ACLMessage
		@see jade.lang.acl.MessageTemplate
	*/
	public final ACLMessage receive(MessageTemplate pattern) {
		ACLMessage msg = null;
		synchronized (waitLock) {
			for (Iterator messages = msgQueue.iterator(); messages.hasNext(); ) {
				final ACLMessage cursor = (ACLMessage)messages.next();
				if (pattern == null || pattern.match(cursor)) {
					try {
						messages.remove(); //!!! msgQueue.remove(msg);
						// Notify receive
						notifyReceived(cursor);
						/*doPrivileged(new jade.security.PrivilegedExceptionAction() {
							public Object run() throws AuthException {
								notifyReceived(cursor);
								return null;
							}
						});*/
						currentMessage = cursor;
						msg = cursor;
						break; // Exit while loop
					}
					catch (Exception e) {
						// Continue loop, discard message
					}
				}
			}
		}
		return msg;
	}

  /**
     Receives an <b>ACL</b> message from the agent message
     queue. This method is blocking and suspends the whole agent until
     a message is available in the queue. JADE provides a special
     behaviour named <code>ReceiverBehaviour</code> to wait for a
     message within a behaviour without suspending all the others and
     without wasting CPU time doing busy waiting.
     @return A new ACL message, blocking the agent until one is
     available.
     @see jade.lang.acl.ACLMessage
     @see jade.core.behaviours.ReceiverBehaviour
  */
  public final ACLMessage blockingReceive() {
    ACLMessage msg = null;
    while(msg == null) {
      msg = blockingReceive(0);
    }
    return msg;
  }

  /**
     Receives an <b>ACL</b> message from the agent message queue,
     waiting at most a specified amount of time.
     @param millis The maximum amount of time to wait for the message.
     @return A new ACL message, or <code>null</code> if the specified
     amount of time passes without any message reception.
   */
  public final ACLMessage blockingReceive(long millis) {
    synchronized(waitLock) {
      ACLMessage msg = receive();
      if(msg == null) {
	doWait(millis);
	msg = receive();
      }
      return msg;
    }
  }

  /**
     Receives an <b>ACL</b> message matching a given message
     template. This method is blocking and suspends the whole agent
     until a message is available in the queue. JADE provides a
     special behaviour named <code>ReceiverBehaviour</code> to wait
     for a specific kind of message within a behaviour without
     suspending all the others and without wasting CPU time doing busy
     waiting.
     @param pattern A message template to match received messages
     against.
     @return A new ACL message matching the given template, blocking
     until such a message is available.
     @see jade.lang.acl.ACLMessage
     @see jade.lang.acl.MessageTemplate
     @see jade.core.behaviours.ReceiverBehaviour
  */
  public final ACLMessage blockingReceive(MessageTemplate pattern) {
    ACLMessage msg = null;
    while(msg == null) {
      msg = blockingReceive(pattern, 0);
    }
    return msg;
  }


  /**
     Receives an <b>ACL</b> message matching a given message template,
     waiting at most a specified time.
     @param pattern A message template to match received messages
     against.
     @param millis The amount of time to wait for the message, in
     milliseconds.
     @return A new ACL message matching the given template, or
     <code>null</code> if no suitable message was received within
     <code>millis</code> milliseconds.
     @see jade.core.Agent#blockingReceive()
  */
  public final ACLMessage blockingReceive(MessageTemplate pattern, long millis) {
    ACLMessage msg = null;
    synchronized(waitLock) {
      msg = receive(pattern);
      long timeToWait = millis;
      while(msg == null) {
	long startTime = System.currentTimeMillis();
	doWait(timeToWait);
	long elapsedTime = System.currentTimeMillis() - startTime;

	msg = receive(pattern);

	if(millis != 0) {
	  timeToWait -= elapsedTime;
	  if(timeToWait <= 0)
	    break;
	}

      }
    }
    return msg;
  }

  /**
     Puts a received <b>ACL</b> message back into the message
     queue. This method can be used from an agent behaviour when it
     realizes it read a message of interest for some other
     behaviour. The message is put in front of the message queue, so
     it will be the first returned by a <code>receive()</code> call.
     @see jade.core.Agent#receive()
  */
  public final void putBack(ACLMessage msg) {
    synchronized(waitLock) {
      msgQueue.addFirst(msg);
    }
  }



  final void setToolkit(AgentToolkit at) {
    myToolkit = at;
  }

  final void resetToolkit() {
    myToolkit = null;
  }

  /**
    This method blocks until the agent has finished its start-up phase
    (i.e. until just before its setup() method is called.
    When this method returns, the target agent is registered with the
   AMS and the JADE platform is aware of it.
  */
  public synchronized void waitUntilStarted() {
    while(getState() == AP_INITIATED) {
      try {
        wait();
      }
      catch(InterruptedException ie) {
        // Do nothing...
      }
    }
  }
  
  // Event firing methods

  // Notify creator that the start-up phase has completed
  private synchronized void notifyStarted() {
    notifyAll();
  }

  // Notify toolkit that a message was posted in the message queue
  private void notifyPosted(ACLMessage msg) throws AuthException {
    myToolkit.handlePosted(myAID, msg);
  }

  // Notify toolkit that a message was extracted from the message
  // queue
  private void notifyReceived(ACLMessage msg) throws AuthException {
    myToolkit.handleReceived(myAID, msg);
  }

  // Notify toolkit of the need to send a message
  private void notifySend(ACLMessage msg) throws AuthException {
  	myToolkit.handleSend(msg);
  }

  // Notify toolkit of the destruction of the current agent
  private void notifyDestruction() {
    myToolkit.handleEnd(myAID);
  }

  // Notify toolkit of the need to move the current agent
  private void notifyMove() {
    myToolkit.handleMove(myAID, myDestination);
  }

  // Notify toolkit of the need to copy the current agent
  private void notifyCopy() {
    myToolkit.handleClone(myAID, myDestination, myNewName);
  }

  // Notify toolkit that the current agent has changed its state
  private void notifyChangedAgentState(int oldState, int newState) {
    AgentState from = STATES[oldState];
    AgentState to = STATES[newState];
    if (myToolkit != null)
      myToolkit.handleChangedAgentState(myAID, from, to);
  }

//__SECURITY__BEGIN
  // Notify toolkit that the current agent has changed its principal
  private void notifyChangedAgentPrincipal(AgentPrincipal from, CertificateFolder certs) {
    if (myToolkit != null)
      myToolkit.handleChangedAgentPrincipal(myAID, from, certs);
  }
//__SECURITY__END

  private void activateAllBehaviours() {
    myScheduler.restartAll();
  }

	/**
		Put a received message into the agent message queue. The message
		is put at the back end of the queue. This method is called by
		JADE runtime system when a message arrives, but can also be used
		by an agent, and is just the same as sending a message to oneself
		(though slightly faster).
		@param msg The ACL message to put in the queue.
		@see jade.core.Agent#send(ACLMessage msg)
	*/
	public final void postMessage(final ACLMessage msg) {
		if(traceRecvdMSG)
			System.out.println("arrived message="+msg+" probably for me="+getName());
		synchronized (waitLock) {
			/*
			try {
				java.io.FileWriter f = new java.io.FileWriter("logs/" + getLocalName(), true);
				f.write("waitLock taken in postMessage() [thread " + Thread.currentThread().getName() + "]\n");
				f.write(msg.toString());
				f.close();
			}
			catch(java.io.IOException ioe) {
				System.out.println(ioe.getMessage());
			}
			*/

			if (msg != null) {
				try {
					doPrivileged(new PrivilegedExceptionAction() {
						public Object run() throws AuthException {
							// notification appens first so, if an exception
							// is thrown, then message isn't appended to queue
							notifyPosted(msg);
							msgQueue.addLast(msg);
							return null;
						}
					});
				}
				catch (Exception e) {
					e.printStackTrace();
				}
			}
			doWake();
			messageCounter++;
		}

		/*
		try {
			java.io.FileWriter f = new java.io.FileWriter("logs/" + getLocalName(), true);
			f.write("waitLock dropped in postMessage() [thread " + Thread.currentThread().getName() + "]\n");
			f.close();
		}
		catch(java.io.IOException ioe) {
			System.out.println(ioe.getMessage());
		}
		*/
	}

  Iterator messages() {
    return msgQueue.iterator();
  }

	private ContentManager theContentManager = new ContentManager();

	/**
	* Retrieves the content manager 
	*
	* @return The content manager.
	*/
	public ContentManager getContentManager() {
		return theContentManager;
	} 
}