/*
 * Copyright (C) 2001 - 2025 ScalAgent Distributed Technologies
 * Copyright (C) 1996 - 2000 BULL
 * Copyright (C) 1996 - 2000 INRIA
 *
 * 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 2.1 of the License, or any later version.
 * 
 * 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 fr.dyade.aaa.agent;

import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

import fr.dyade.aaa.common.Debug;
import fr.dyade.aaa.common.encoding.Decoder;
import fr.dyade.aaa.common.encoding.Encodable;
import fr.dyade.aaa.common.encoding.EncodableHelper;
import fr.dyade.aaa.common.encoding.Encoder;
import fr.dyade.aaa.util.Transaction;
import fr.dyade.aaa.util.management.MXWrapper;

/**
 * The <code>Agent</code> class represents the basic component in our model.
 * <i>agents</i> are "reactive" objects which behave according to 
 * "event/reaction"model: an event embodies a significant state change
 * which one or many agents may react to.<p>
 * Class <code>Agent</code> defines the generic interface and the common
 * behavior for all agents; every agent is an object of a class deriving
 * from class Agent. Agents are the elementary programming and execution
 * entities; they only communicate using notifications through the message
 * bus, and are controlled by the execution engine.<p>
 * The reactive behavior is implemented by function member React, which
 * defines the reaction of the agent when receiving a notification; this
 * function member is called by the execution engine.<p>
 * Agents are persistent objects, and the Agent class realizes a
 * "swap-in/swap-out" mechanism which allows loading (or finding) in main
 * memory the agents to activate, and unloading the agents idle since a while.
 * <hr>
 * Agents must be created in two steps: 
 * <ul>
 * <li>locally creating the object in memory (via constructor),
 * <li>configure it (for example via get/set methods),
 * <li>the deploy it .
 * </ul>
 * <p>
 * The following code would then create a simple agent and deploy it:
 * <blockquote><pre>
 *     Agent ag = new Agent();
 *     ag.deploy();
 * </pre></blockquote>
 * 
 * @see Notification
 * @see Engine
 * @see Channel
 */
public abstract class Agent implements AgentMBean, Serializable, Encodable {
  /** Define serialVersionUID for interoperability. */
  static final long serialVersionUID = 1L;
  
  protected transient Logger logmon = null;  
  protected static final boolean DEBUG = Debug.getLogger(Agent.class.getName()).isLoggable(BasicLevel.DEBUG);

  /**
   * <code>true</code> if the agent state has changed.
   * <p>
   * This field value is initialized as <code>true</code>, so that by default
   * the agent state is saved after a reaction.
   */
  private transient boolean updated = true;

  transient int reactNb = 0;
  
  /**
   * @return the reactNb
   */
  public int getReactNb() {
    return reactNb;
  }
  
  /**
   * Increments the tick counter that reflects activity in server.
   */
  public final void incWorkInProgress() {
    AgentServer.getEngine().incWorkInProgress();
  }

  /**
   * Boolean value indicating if the agent profiling is on.
   * If true, the cumulative time of reaction and commit is kept for this agent.
   */
  // TODO (AF): Should be transient.
  public boolean agentProfiling = false;
  
  /**
   * Returns true if the agent profiling is on.
   * 
   * @return true if the agent profiling is on.
   * @see fr.dyade.aaa.agent.EngineMBean#isAgentProfiling()
   */
  public boolean isAgentProfiling() {
    return this.agentProfiling;
  }
  
  /**
   * Sets the agent profiling.
   * 
   * @param agentProfiling if true sets the agent profiling.
   * @see fr.dyade.aaa.agent.EngineMBean#setAgentProfiling(boolean)
   */
  public void setAgentProfiling(boolean agentProfiling) {
    this.agentProfiling = agentProfiling;
  }

  transient long reactTime = 0L;

  /**
   * @return the reactTime
   */
  public long getReactTime() {
    return reactTime;
  }
  
  /**
   * reset the reactTime
   */
  public void resetReactTime(){
  	reactTime = 0;
  }

  transient long commitTime = 0L;

  /**
   * @return the commitTime
   */
  public long getCommitTime() {
    return commitTime;
  }  
  
  /**
   * reset the commitTime
   */
  public void resetCommitTime(){
  	commitTime = 0;
  }
  
  /**
   * Reset reactTime and commitTime
   */
  public void resetTimer(){
  	resetReactTime();
  	resetCommitTime();
  }
  
  /**
   * Sets the <code>updated</code> field to <code>false</code> so that the
   * agent state is not saved after the current reaction; the field is set
   * back to <code>true</code> for the next reaction.
   */
  protected void setNoSave() {
    updated = false;
  }

  /**
   * Sets the <code>updated</code> field to <code>true</code> so that the
   * agent state is saved after the current reaction.
   */
  protected void setSave() {
    updated = true;
  }
  
  /**
   * Indicates whether the agent has been updated or not.
   * @return true if the agent has been updated.
   */
  public boolean isUpdated() {
    return updated;
  }

  /**
   * Indicates to the Engine component that a commit is needed.
   * @return true if there is no error.
   */
  protected final boolean needToBeCommited() {
    try {
      ((EngineThread) Thread.currentThread()).engine.needToBeCommited = true;
      return true;
    } catch (ClassCastException exc) {
      return false;
    }
  }

  /**
   * Saves the agent state unless not requested.
   * @throws IOException if any error occurs.
   */
  protected final void save() throws IOException {
    agentSave();
    if (updated) {
      AgentServer.getTransaction().save(this, id.toString());
      if (DEBUG)
        logmon.log(BasicLevel.DEBUG,
                   "Agent" + id + " [" + name + "] saved");
    } else {
      updated = true;
      if (DEBUG)
        logmon.log(BasicLevel.DEBUG,
                   "Agent" + id + " [" + name + "] not saved");
    }
  }
  
  /**
   * Enables the sub-classes to save their state.
   * @throws IOException if any error occurs.
   */
  protected void agentSave() throws IOException {
    // Nothing to save
  }

  /**
   * Restores the object state from the persistent storage.
   *
   * @param id  The agent unique identifier.
   * @return    The agent object.
   * @throws IOException             when accessing the stored image.
   * @throws ClassNotFoundException  if the stored image class may not be found.
   */
  final static Agent load(AgentId id) throws IOException, ClassNotFoundException {
    return load(AgentServer.getTransaction(), id);
  }

  public final static Agent load(Transaction transaction, AgentId id) throws IOException, ClassNotFoundException {
     Agent ag = (Agent) transaction.load(id.toString());
     if (ag != null) {
       ag.id = id;
       ag.deployed = true;
     }
     return ag;
   }

  //  Declares all fields transient in order to avoid useless
  // description of each during serialization.

  /**
   * Global unique identifier of the agent. Each agent is identified by a
   * unique identifier allowing the agent to be found. The identifiers format
   * is detailed in <a href="AgentId.html">AgentId</a> class.
   * 
   * Be careful, id is not part of persistent data, it must be initialized after
   * transaction load.
   */
  transient AgentId id;
  
  /** Symbolic name of the agent */
  transient String name;

  public boolean hasName() {
    return (name != null);
  }
  
  /**
   * Returns this <code>Agent</code>'s name.
   * If the name is not set returns the string representation of its unique id.
   *
   * @return this <code>Agent</code>'s name.
   */
  public String getName() {
    if ((name == null) || (name.length() == 0))
      return id.toString();

    return name;
  }

  /**
   * Sets this <code>Agent</code>'s name.
   *
   * @param name	the <code>Agent</code>'s name.
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * Some agents must be loaded at any time, this can be enforced by this
   * member variable. If <code>true</code> agent is pinned in memory.
   */
  protected transient boolean fixed;

  /**
   * Returns default log topic for agents. Its method should be overridden
   * in subclass in order to permit fine configuration of logging system.
   * By default it returns <code>Debug.A3Agent</code>.
   * 
   * @return the default log topic for agents.
   */
  protected String getLogTopic() {
    return Agent.class.getName();
  }

  /**
   *  the <code>last</code> variable contains the virtual time of the
   * last access. It is used by swap-out policy.
   */
  transient long last;

  public static final String emptyString = "";

  private void writeObject(java.io.ObjectOutputStream out) throws IOException {
    if (name == null)
      out.writeUTF(emptyString);
    else
      out.writeUTF(name);
    out.writeBoolean(fixed);
  }

  private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
    name = in.readUTF();
    if (name.length() == 0)
      name = null;
    fixed = in.readBoolean();
    updated = true;
  }

  /**
   * Allocates a new Agent object. The resulting object <b>is not an agent</b>;
   * before it can react to a notification you must deploy it. This constructor
   * has the same effect as
   * <code>Agent(AgentServer.getServerId(), null, false)</code>.
   *
   * @see Agent#Agent(short, java.lang.String, boolean)
   * @see #deploy()
   */
  public Agent() {
    this(null, false);
  }

  /**
   * Allocates a new Agent object. This constructor has the same effect
   * as <code>Agent(AgentServer.getServerId(), null, fixed)</code>.
   * 
   * @param fixed if <code>true</code> agent is pinned in memory
   *
   * @see Agent#Agent(short, String, boolean)
   */
  public Agent(boolean fixed) {
    this(null, fixed);
  }

  /**
   * Allocates a new Agent object. This constructor has the same effect
   * as <code>Agent(AgentServer.getServerId(), name, false)</code>.
   * 
   * @param name  symbolic name
   *
   * @see Agent#Agent(short, java.lang.String, boolean)
   */
  public Agent(String name) {
    this(name, false);
  }

  /**
   * Allocates a new Agent object. This constructor has the same effect
   * as <code>Agent(AgentServer.getServerId(), name, fixed)</code>.
   * 
   * @param name  symbolic name
   * @param fixed if <code>true</code> agent is pinned in memory
   *
   * @see Agent#Agent(short, java.lang.String, boolean)
   */
  public Agent(String name, boolean fixed) {
    this(AgentServer.getServerId(), name, fixed);
  }

  /**
   * Allocates a new Agent object. This constructor has the same effect
   * as <code>Agent(to, null, false)</code>.
   *
   * @param to	  Identification of target agent server
   *
   * @see Agent#Agent(short, java.lang.String, boolean)
   */
  public Agent(short to) {
    this(to, null, false);
  }

  /**
   * Allocates a new Agent object. This constructor has the same effect
   * as <code>Agent(to, name, false)</code>.
   *
   * @param to	  Identification of target agent server
   * @param name  symbolic name
   *
   * @see Agent#Agent(short, java.lang.String, boolean)
   */
  public Agent(short to, String name) {
    this(to, name, false);
  }

  /**
   * Allocates a new Agent object. This constructor has the same effect
   * as <code>Agent(to, null, fixed)</code>.
   *
   * @param to	  Identification of target agent server
   * @param fixed if <code>true</code> agent is pinned in memory
   *
   * @see Agent#Agent(short, java.lang.String, boolean)
   */
  public Agent(short to, boolean fixed) {
    this(to, null, fixed);
  }

  /**
   * Allocates a new Agent object. The resulting object <b>is not an agent</b>;
   * before it can react to a notification you must deploy it.
   *
   * @param to	  Identification of target agent server
   * @param name  symbolic name
   * @param fixed if <code>true</code> agent is pinned in memory
   *
   * @see #deploy()
   */
  public Agent(short to, String name, boolean fixed) {
    AgentId id = null;

    try {
      id = new AgentId(to);
    } catch (IOException exc) {
      logmon = Debug.getLogger(Agent.class.getName() + ".#" + AgentServer.getServerId());
      logmon.log(BasicLevel.ERROR,
                 AgentServer.getName() + ", can't allocate new AgentId", exc);
      // TODO: throw an exception...
    }
    initState(name, fixed, id);
  }

  /**
   * Constructor used to build "system" agents like <code>AgentFactory</code>.
   * System agents are created from the <code>agent</code> package. This
   * constructor takes the agent id as a parameter instead of building it.
   *
   * @param name  symbolic name
   * @param fixed if <code>true</code> agent is pinned in memory
   * @param id    unique identifier
   */
  Agent(String name, boolean fixed, AgentId id) {
    initState(name, fixed, id);
  }

  private void initState(String name, boolean fixed, AgentId id) {
    this.name = name;
    this.fixed = fixed;
    this.id = id;
    // Get the logging monitor from current server MonologLoggerFactory
    this.logmon = Debug.getLogger(getLogTopic());
  }

  /**
   * Constructor used to build Well Known Services agents.
   * <p>
   * System agents are created from the <code>agent</code> package.
   * WKS agents are similar to system agents, except that they may be
   * defined in separate packages, and they do not necessarily exist on all
   * agent servers. Their creation is controlled from the configuration file
   * of the agent server.<p>
   * This constructor takes the agent id as a parameter instead of building it.
   * Since the constructor has been made public, the consistency of agent ids
   * allocation must be enforced. This is done by the constructor checking
   * that the id stamp is comprised in the <code>AgentId.MinWKSIdStamp</code>
   * - <code>AgentId.MaxWKSIdStamp</code> interval.
   *
   * @param name	symbolic name
   * @param fixed	if <code>true</code> agent is pinned in memory
   * @param stamp	well known stamp
   */
  public Agent(String name, boolean fixed, int stamp) {
    if (stamp < AgentId.MinWKSIdStamp || stamp > AgentId.MaxWKSIdStamp) {
      logmon = Debug.getLogger(Agent.class.getName() + ".#" + AgentServer.getServerId());
      logmon.log(BasicLevel.ERROR,
                 AgentServer.getName() + ", well known service stamp out of range: " + stamp);
      throw new IllegalArgumentException("Well known service stamp out of range: " + stamp);
    }
    AgentId id = new AgentId(AgentServer.getServerId(),
                             AgentServer.getServerId(),
                             stamp);
    initState(name, fixed, id);
  }

  /**
   * Determines if the current <code>Agent</code> has already been deployed.
   */
  transient boolean deployed = false;

  /**
   * Returns if the currently <code>Agent</code> has already been deployed.
   * 
   * @return true if the current agent has already been deployed.
   */
  public boolean isDeployed() {
    return deployed;
  }

  /**
   * Deploys a new <i>agent</i>.
   * It works by sending a notification to a special agent, of class Factory,
   * running on the target agent server. The notification asks for a remote
   * creation of the agent. This solution presents the advantage of reusing
   * the standard communication mechanisms of the agent machine.<p>
   * The whole process involves then the following steps:
   * <ul>
   * <li>serializing the object state,
   * <li>building an <code>AgentCreateRequest</code> notification with the
   *     resulting bytes stream,
   * <li>sending it to the target Factory agent.
   * </ul>
   * In reaction, the factory agent builds the agent in the target server
   * from the serialized image, and saves it into operational storage.
   *
   * @exception IOException
   *	unspecialized exception
   */
  public final void deploy() throws IOException {
    deploy(null);
  }

  /**
   * Deploys a new <i>agent</i>.
   * It works as <a href="#deploy()">deploy()</a> method above; after the
   * agent creation, the Factory agent sends an <code>AgentCreateReply</code>
   * notification.
   *
   * @param reply	agent to reply to
   * @exception IOException
   *	unspecialized exception
   */
  public final void deploy(AgentId reply) throws IOException {
    if ((id == null) || id.isNullId()) {
      logmon.log(BasicLevel.ERROR,
                 AgentServer.getName() + ", can't deploy " + this.toString() + ", id is null");
      throw new IOException("Can't deploy agent, id is null");
    }
    if (deployed) {
      logmon.log(BasicLevel.ERROR,
                 AgentServer.getName() + ", can't deploy " + this.toString() + ", already deployed");
      throw new IOException("Can't deploy agent, already deployed");
    }

    //  If we use sendTo agent's method the from field is the agent id, and
    // on reception the from node (from.to) can be false.
    Channel.sendTo(AgentId.factoryId(id.getTo()),
                   new AgentCreateRequest(this, reply));
    deployed = true;

    if (DEBUG)
      logmon.log(BasicLevel.DEBUG, this.toString() + " deployed");
  }

  /**
   * Returns a string representation of this agent, including the agent's
   * class, name, global identification, and fixed property.
   *
   * @return	A string representation of this agent. 
   */
  public String toString() {
    StringBuilder strbuf = new StringBuilder();

    strbuf.append('(').append(super.toString());
    strbuf.append(",name=").append(name);
    strbuf.append(",id=").append(id.toString());
    strbuf.append(",fixed=").append(fixed);
    strbuf.append(')');

    return strbuf.toString();
  }

  /**
   * Returns String format of the global unique identifier of the agent.
   *
   * @return the global unique identifier of the agent.
   */
  public final String getAgentId() {
    return id.toString();
  }

  /**
   * Returns the global unique identifier of the agent. Each agent is
   * identified by a unique identifier allowing the agent to be found.
   * The identifiers format is detailed in <a href="AgentId.html">AgentId</a>
   * class.
   *
   * @return the global unique identifier of the agent.
   */
  public final AgentId getId() {
    return id;
  }

  /** 
   * Tests if the agent is pinned in memory.
   *
   * @return true if this agent is a pinned in memory; false otherwise.
   */
  public final  boolean isFixed() {
    return fixed;
  }

  /**
   * Gives this agent an opportunity to initialize after having been deployed,
   * and each time it is loaded into memory.
   * <p>
   * This function is first called by the factory agent, just after it deploys
   * the agent.
   * <p>
   * This function is used by agents with a <code>fixed</code> field set to
   * <code>true</code> to initialize their transient variables, as it is called
   * each time the agent server is restarted.
   * <p>
   * This function is not declared <code>final</code> so that derived classes
   * may change their reload policy. The implementation of this method provided
   * by the <code>Agent</code> class just registers the JMS MBean.
   *
   * @param firstTime		true when first called by the factory
   *
   * @exception Exception
   *	unspecialized exception
   */
  protected void agentInitialize(boolean firstTime) throws Exception {
    // Get the logging monitor from current server MonologLoggerFactory
    this.logmon = Debug.getLogger(getLogTopic());
    // Initializes the updated field to true:
    this.updated = true;

    // TODO (AF): /!\ Registering all loaded agents in JMX may cause scalability issues!!
    // May be, we can do it only in a special debug mode.
    try {
      MXWrapper.registerMBean(this, "AgentServer", getA3MBeanName());
    } catch (Exception exc) {
      if (DEBUG) {
        logmon.log(BasicLevel.WARN,
                   "Agent" + id + " [" + name + "]: JMX registration failed", exc);
      } else {
        logmon.log(BasicLevel.WARN,
                   "Agent" + id + " [" + name + "]: JMX registration failed");
      }
    }

    if (DEBUG)
      logmon.log(BasicLevel.DEBUG,
                 "Agent" + id + " [" + name + "], initialized: " + firstTime);
  }

  private final String getA3MBeanName() {
    StringBuilder strbuf = new StringBuilder();
    strbuf.append("server=").append(AgentServer.getName());
    strbuf.append(",cons=Engine#").append(getId().getTo());
    if ((name == null) || (name.length() == 0))
      strbuf.append(",agent=").append(getAgentId());
    else
      strbuf.append(",agent=").append(name).append('[').append(getAgentId()).append(']');
    
    return strbuf.toString();
  }

  /**
   * This method sends a notification to the agent which id is given in
   * parameter. During an agent reaction alls notifications sent are buffered
   * until reaction commit.
   * <p>
   * Be careful if you use this method outside of an agent reaction,
   * its behavior is slightly different: each notification is immediately
   * sent using a local transaction.
   *
   * @see Channel#sendTo
   *
   * @param to   the unique id. of destination <code>Agent</code>.
   * @param not  the notification to send.
   */
  protected final void
  sendTo(AgentId to, Notification not) {
//     try {
//       EngineThread thread = (EngineThread) Thread.currentThread();
//       // Use the engine's sendTo method that push message in temporary queue
//       // until the end of current reaction.
//       thread.engine.push(getId(), to, not);
//     } catch (ClassCastException exc) {
//       //  Be careful, the destination node use the from.to field to
//       // get the from node id.
//       Channel.channel.directSendTo(getId(), to, not);
//     }

//  if (Class.EngineThread.isAssignable(Thread.currentThread())) {
    if (AgentServer.isEngineThread()) {
      AgentServer.engine.push(getId(), to, not);
    } else {
      Channel.channel.directSendTo(getId(), to, not);
    }
  }

  /**
   * This method sends a notification to the agent which id is wrapped
   * in the specified role.
   *
   * @param role  the destination <code>Role</code>.
   * @param not   the notification to send.
   */
  protected final void sendTo(Role role, Notification not) {
    if (role == null) return;
    sendTo(role.getListener(), not);
  }
 
  /**
   * Sends a notification to all the agents registered in a role.
   *
   * @param role  the destination <code>MultiplRole</code>.
   * @param not   the notification to send.
   */
  protected final void sendTo(RoleMultiple role, Notification not) {
    if (role == null) return;
    Enumeration<AgentId> to = role.getListeners();
    if (to == null) return;
    while (to.hasMoreElements())
      sendTo((AgentId) to.nextElement(), not);
  }

  /**
   * Permits this agent to destroy itself. If necessary, this method should be 
   * overloaded to work properly.
   */
  public void delete() {
    delete(null);
  }

  /**
   * Permits this agent to destroy itself. If necessary, this method should be
   * overloaded to work properly. 
   *
   * @param agent	Id of agent to notify.
   */
  public void delete(AgentId agent) {
    if (deployed)
      sendTo(AgentId.factoryId(id.getTo()),
	     new AgentDeleteRequest(agent));
  }

  /**
   * Permits this agent to destroy itself. If necessary, this method should be
   * overloaded to work properly.
   * 
   * @param agent
   *          Id of agent to notify.
   * @param extraInformation
   *          extra information added when notifying the agent.
   */
  public void delete(AgentId agent, Object extraInformation) {
    if (deployed)
      sendTo(AgentId.factoryId(id.getTo()), new AgentDeleteRequest(agent, extraInformation));
  }
 
  /**
   * Defines the reaction of the agent when receiving a notification. This
   * member function implements the common reactive behavior of an agent, it
   * is called by the execution engine (see <a href="Engine.html">Engine</a>
   * class).<p>
   * If there is no corresponding reaction, the agent send an
   * <code>UnknownNotification</code> notification to the sender.
   *
   * @param from	agent sending notification
   * @param not		notification to react to
   *
   * @exception Exception
   *	unspecialized exception
   */
  public void react(AgentId from, Notification not) throws Exception {
    if (not instanceof DeleteNot) {
      delete(((DeleteNot) not).reply);
    } else if (not instanceof AdminSyncNotification) {
      doAdminSyncNotification(from, (AdminSyncNotification) not);
    } else if ((not instanceof UnknownAgent) ||
               (not instanceof UnknownNotification) ||
               (not instanceof ExceptionNotification)) {
      logmon.log(BasicLevel.WARN,
                 this.toString() + ".react(" + from + ", " + not + ")");
     } else {
      logmon.log(BasicLevel.ERROR,
                 "Unknown notification, " + this.toString() + ".react(" + from + ", " + not + ")");
      sendTo(from, new UnknownNotification(id, not));
    }
  }

  /**
   * Default handling of AdminSyncNotification.
   * Currently there is no command defined at the Agent level, this method just logs a warning, and
   * unlocks the caller.
   * 
   * @param from
   * @param not
   */
  protected void doAdminSyncNotification(AgentId from, AdminSyncNotification not) {
    int command = not.getCommand();
    logmon.log(BasicLevel.WARN,
               this.toString() + ".doAdminSyncNotification, no handling for command #" + command);
    not.Return(new Object[] {"Unkonw command: " + command});
  }

  /**
   * Called to inform this agent that it is garbaged and that it should free
   * any active resources that it has allocated.
   * A subclass of <code>Agent</code> should override this method if it has
   * any operation that it wants to perform before it is garbaged. For example,
   * an agent with threads (a ProxyAgent for example) would use the initialize
   * method to create the threads and the <code>agentFinalize</code> method to
   * stop them.
   * <p>
   * Be careful, the notification sending is not allowed in this method.
   * <p>
   * The implementation of this method provided by the <code>Agent</code> class
   * just unregister the JMX MBean if needed.
   *
   * @param lastTime	true when last called by the factory on agent deletion.
   */
  public void agentFinalize(boolean lastTime) {
    try {
      MXWrapper.unregisterMBean("AgentServer", getA3MBeanName());
    } catch (Exception exc) {
      if (DEBUG)
        logmon.log(BasicLevel.WARN,
                   "Agent" + id + " [" + name + "] JMX unregistration failed", exc);
      else
        logmon.log(BasicLevel.WARN,
                   "Agent" + id + " [" + name + "] JMX unregistration failed");
    }

    if (DEBUG)
      logmon.log(BasicLevel.DEBUG,
                 "Agent" + id + " [" + name + "],  finalize: " + lastTime);
  }
  
  /* ************************************************************
   * Encodable interface.
   * ************************************************************/
  
  /**
   * Enables the sub classes not to implement this method.
   * @return -1
   */
  @Override
  public int getEncodableClassId() {
    return -1;
  }
  
  /**
   * Returns the size of the encoded object.
   * @return the size of the encoded object
   * @exception Exception if an error occurs
   */
  @Override
  public int getEncodedSize() throws Exception {
    int encodedSize = EncodableHelper.getNullableStringEncodedSize(name);
    encodedSize += BOOLEAN_ENCODED_SIZE;
    return encodedSize;
  }
  
  /**
   * Encodes the object.
   * @param encoder the encoder
   * @exception Exception if an error occurs
   */
  @Override
  public void encode(Encoder encoder) throws Exception {
    encoder.encodeNullableString(name);
    encoder.encodeBoolean(fixed);
  }

  /**
   * Decodes the object.
   * @param decoder the encoder
   * @exception Exception if an error occurs
   */
  @Override
  public void decode(Decoder decoder) throws Exception {
    name = decoder.decodeNullableString();
    fixed = decoder.decodeBoolean();
    updated = true;
  }
}
