Package jade.imtp.leap

Source Code of jade.imtp.leap.CommandDispatcher

/*--- formatted by Jindent 2.1, (www.c-lab.de/~jindent) ---*/

/**
* ***************************************************************
* The LEAP libraries, when combined with certain JADE platform components,
* provide a run-time environment for enabling FIPA agents to execute on
* lightweight devices running Java. LEAP and JADE teams have jointly
* designed the API for ease of integration and hence to take advantage
* of these dual developments and extensions so that users only see
* one development platform and a
* single homogeneous set of APIs. Enabling deployment to a wide range of
* devices whilst still having access to the full development
* environment and functionalities that JADE provides.
* Copyright (C) 2001 Telecom Italia LAB S.p.A.
* Copyright (C) 2001 Siemens AG.
*
* 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.imtp.leap;


import jade.core.PlatformManager;
import jade.core.Node;
import jade.core.IMTPException;
import jade.core.Profile;
import jade.core.UnreachableException;
import jade.mtp.TransportAddress;
import jade.util.leap.Iterator;
import jade.util.leap.ArrayList;
import jade.util.leap.List;
import jade.util.leap.Map;
import jade.util.leap.HashMap;
import jade.util.Logger;

/**
* This class provides the implementation of a command
* dispatcher. The command dispatcher misses support for multiple remote
* objects, multiple ICPs and command routing.
*
* <p>The command dispatcher is based on an implementation written by
* Michael Watzke and Giovanni Caire (TILAB), 09/11/2000.</p>
*
* @author Tobias Schaefer
* @version 1.0
*/
class CommandDispatcher implements StubHelper, ICP.Listener {
  private static final String MAIN_PROTO_CLASS = "main-proto-class";
 
  /**
   * The default name for new instances of the class
   * <tt>CommandDispatcher</tt> that have not get an unique name by
   * their container yet.
   */
  protected static final String      DEFAULT_NAME = "Default";
 
  private static boolean enableMultiplePlatforms;
 
  /**
   * The default singleton instance of the command dispatcher.
   */
  protected static CommandDispatcher defaultCommandDispatcher;
 
  /**
   * The singleton map of command dispatchers in case more than one must be present in the same JVM.
   */
  protected static Map dispatchers = new HashMap();
 
  private String platformName;
 
  /**
   * The unique name of this command dispatcher used to avoid loops in
   * the forwarding mechanism.
   */
  protected String name;
 
  /**
   * The transport address of the default router. Commands that cannot
   * be dispatched directly will be sent to this address.
   */
  protected TransportAddress         routerTA = null;
 
 
  /**
   * This hashtable maps the IDs of the objects remotized by this
   * command dispatcher to the skeletons for these objects. It is used
   * when a command is received from a remote JVM.
   */
  protected Map skeletons = new HashMap();
 
  /**
   * This hashtable maps the objects remotized by this command
   * dispatcher to their IDs. It is used when a stub of a remotized
   * object must be built to be sent to a remote JVM.
   */
  protected Map ids = new HashMap();
 
  /**
   * A counter that is used for determining IDs for remotized objects.
   * Everytime a new object is registered by the command dispatcher it
   * gets the value of this field as ID and the field is increased.
   */
  protected int       nextID;
 
  /**
   * The pool of ICP objects used by this command dispatcher to
   * actually send/receive data over the network. It is a table that
   * associates a <tt>String</tt> representing a protocol (e.g. "http")
   * to a list of ICPs supporting that protocol.
   */
  protected Map icps = new HashMap();
 
  /**
   * The transport addresses the ICPs managed by this command
   * dispatcher are listening for commands on.
   */
  protected List      addresses = new ArrayList();
 
  /**
   * The URLs corresponding to the local transport addresses.
   */
  protected List      urls = new ArrayList();
 
  /**
   The stub for the platform service manager. This stub will be
   shared by all nodes within this Java virtual Machine.
   */
  private PlatformManager thePlatformManager = null;
 
 
  private Logger myLogger = Logger.getMyLogger(getClass().getName());;
 
  static {
    enableMultiplePlatforms = "true".equals(System.getProperty("jade.enable.multiple.platforms"));
  }
 
  /**
   * Returns a reference to the singleton instance of the CommandDispatcher for the indicated platform.
   * Such instance is created if necessary.
   * If no platform name is specified or the enableMultiplePlatforms flag is not set, then use the default
   * CommandDispatcher
   * @return the singleton instance of the CommandDispatcher for the indicated platform.
   */
  public synchronized static final CommandDispatcher getDispatcher(String name) throws IMTPException {
    System.out.println("Retrieving CommandDispatcher for platform "+name);
    if (enableMultiplePlatforms) {
      if (name != null) {
        CommandDispatcher cd = (CommandDispatcher) dispatchers.get(name);
        if (cd == null) {
          cd = new CommandDispatcher();
          cd.setPlatformName(name);
          dispatchers.put(name, cd);
        }
        return cd;
      }
      else {
        throw new IMTPException("No platform name specified and enable-multiple-platforms mode activated");
      }
    }
    else {
      // Use the default CommandDispatcher anyway
      if (defaultCommandDispatcher == null) {
        defaultCommandDispatcher = new CommandDispatcher();
        defaultCommandDispatcher.setPlatformName(name);
      }
      return defaultCommandDispatcher;
    }
  }
 
  private synchronized static void removeDispatcher(String name) {
    if (enableMultiplePlatforms) {
      dispatchers.remove(name);
    }
  }
 
  private void setPlatformName(String name) {
    platformName = name;
  }
 
  /**
   * A sole constructor. To get a command dispatcher the constructor
   * should not be called directly but the static <tt>create</tt> and
   * <tt>getDispatcher</tt> methods should be used. Thereby the
   * existence of a singleton instance of the command dispatcher will
   * be guaranteed.
   */
  private CommandDispatcher() {
    // Set a temporary name. Will be substituted as soon as the first
    // container attached to this CommandDispatcher will receive a
    // unique name from the main.
    name = DEFAULT_NAME;
    nextID = 1;
  }
 
  synchronized PlatformManager getPlatformManagerProxy(Profile p) throws IMTPException {
    if(thePlatformManager == null) {
     
      PlatformManagerStub stub = new PlatformManagerStub(platformName);
      TransportAddress mainTA = initMainTA(p);
      stub.bind(this);
      stub.addTA(mainTA);
      setPlatformManager(stub);
    }
   
    return thePlatformManager;
  }

  // No need to synchronize it as it is always called within synchronized blocks
  private void setPlatformManager(PlatformManager pm) throws IMTPException {
    thePlatformManager = pm;
    String actualPlatformName  = thePlatformManager.getPlatformName();
    if (platformName != null) {
      // PlatformName already set --> Check that it is consistent with the actual name of the platform
      if (!platformName.equals(actualPlatformName)) {
        throw new IMTPException("Wrong platform name "+platformName+". It should be "+actualPlatformName);
      }
    }
    else {
      platformName = actualPlatformName;
    }
  }
 
  // This is only called when reconnecting to a new Master Main Container --> No need to check again the platformName
  synchronized void setPlatformManagerProxy(PlatformManager pm) {
    thePlatformManager = pm;
  }
 
  public PlatformManager getPlatformManagerStub(String addr) throws IMTPException {
   
    // Try to translate the address into a TransportAddress
    // using a protocol supported by this CommandDispatcher
    try {
      PlatformManagerStub stub = new PlatformManagerStub(platformName);
      TransportAddress ta = stringToAddr(addr);
      stub.bind(this);
      stub.addTA(ta);
      return stub;
    }
    catch (DispatcherException de) {
      throw new IMTPException("Invalid address for a Platform Manager", de);
    }
   
  }
 
  public void addAddressToStub(Stub target, String toAdd) {
    try {
      TransportAddress ta = stringToAddr(toAdd);
      target.addTA(ta);
    }
    catch(DispatcherException de) {
      de.printStackTrace();
    }
  }
 
  public void removeAddressFromStub(Stub target, String toRemove) {
    try {
      TransportAddress ta = stringToAddr(toRemove);
      target.removeTA(ta);
    }
    catch(DispatcherException de) {
      de.printStackTrace();
    }
  }
 
  public void clearStubAddresses(Stub target) {
    target.clearTAs();
  }
 
  /**
   * Sets the transport address of the default router used for the
   * forwarding mechanism.
   *
   * @param url the URL of the default router.
   */
  void setRouterAddress(String url) {
    if (url != null) {
      // The default router must be directly reachable -->
      // Its URL can be converted into a TransportAddress by
      // the ICP registered to this CommandDispatcher
      try {
        TransportAddress ta = stringToAddr(url);
        if (routerTA != null && !routerTA.equals(ta)) {
          if(myLogger.isLoggable(Logger.WARNING))
            myLogger.log(Logger.WARNING,"Transport address of current router has been changed");
        }
        routerTA = ta;
      }
      catch (Exception e) {
        // Just print a warning: default (i.e. main TA) will be used
        if(myLogger.isLoggable(Logger.WARNING))
          myLogger.log(Logger.WARNING,"Can't initialize router address");
      }
    }       
  }
 
  /**
   * This method dispatches the specified command to the first address
   * (among those specified) to which dispatching succeeds.
   *
   * @param destTAs a list of transport addresses where the command
   * dispatcher should try to dispatch the command.
   * @param command the command that is to be dispatched.
   * @return a response command from the receiving container.
   * @throws DispatcherException if an error occurs during dispatching.
   * @throws UnreachableException if none of the destination addresses
   * is reachable.
   */
  public Command dispatchCommand(List destTAs,
      Command command) throws DispatcherException, UnreachableException {
   
    // DEBUG
    //TransportAddress ta = (TransportAddress) destTAs.get(0);
    //System.out.println("Dispatching command of type " + command.getCode() + " to "+ta.getHost()+":"+ta.getPort());
    Command response = null;
    //#J2ME_EXCLUDE_BEGIN
    if (isLocal(destTAs)) {
      Integer id = new Integer(command.getObjectID());
      Skeleton skel = (Skeleton) skeletons.get(id);
      if (skel != null) {
        response = skel.processCommand(command);
      }
    }
    //#J2ME_EXCLUDE_END
    if (response == null) {
      try {
        response = dispatchSerializedCommand(destTAs, serializeCommand(command), command.getRequireFreshConnection(), name);
      }
      catch (LEAPSerializationException lse) {
        throw new DispatcherException("Error serializing command "+command+" ["+lse.getMessage()+"]");
      }
    }
   
    // If the dispatched command was an ADD_NODE --> get the
    // name from the response and use it as the name of the CommandDispatcher
    if (command.getCode() == Command.ADD_NODE && name.equals(DEFAULT_NAME)) {
      name = (String) response.getParamAt(0);
    }
    return response;
  }
 
  private boolean isLocal(List destTAs) {
    try {
      TransportAddress ta1 = (TransportAddress) addresses.get(0);
      TransportAddress ta2 = (TransportAddress) destTAs.get(0);
      return (ta1.getHost().equals(ta2.getHost()) && ta1.getPort().equals(ta2.getPort()) && ta2.getFile() == null);
    }
    catch (Exception e) {
      return false;
    }
  }
 
  /**
   * Dispatches the specified serialized command to one of the
   * specified transport addresses (the first where dispatching
   * succeeds) directly or through the router.
   *
   * @param destTAs a list of transport addresses where the command
   * dispatcher should try to dispatch the command.
   * @param commandPayload the serialized command that is to be
   * dispatched.
   * @param origin a <tt>String</tt> object describing the origin of
   * the command to be dispatched.
   * @return a response command from the receiving container.
   * @throws DispatcherException if an error occurs during dispatching.
   * @throws UnreachableException if none of the destination addresses
   * is reachable.
   */
  private Command dispatchSerializedCommand(List destTAs, byte[] commandPayload, boolean requireFreshConnection, String origin)
  throws DispatcherException, UnreachableException {
   
    // Be sure that the destination addresses are correctly specified
    if (destTAs == null || destTAs.size() == 0) {
      throw new DispatcherException("no destination address specified.");   
    }
   
    byte[] responsePayload = null;
    try {   
      // Try to dispatch the command directly
      responsePayload = dispatchDirectly(destTAs, commandPayload, requireFreshConnection);
    }
    catch (UnreachableException ue) {
      // Direct dispatching failed --> Try through the router
      if (myLogger.isLoggable(Logger.FINE)) {
        myLogger.log(Logger.FINE, "Destination unreachable. Dispatch command through router.", ue);
      }
     
      if (routerTA != null) {
        responsePayload = dispatchThroughRouter(destTAs, commandPayload, requireFreshConnection, origin);
      }
      else {
        throw ue;
      }
    }
   
    // Deserialize the response
    try {
      Command response = deserializeCommand(responsePayload);
     
      // Check whether some exceptions to be handled by the
      // CommandDispatcher occurred on the remote site
      checkRemoteExceptions(response);
     
      return response;
    }
    catch (LEAPSerializationException lse) {
      throw new DispatcherException("error deserializing response ["+lse.getMessage()+"].");
    }
  }
 
  /**
   * Dispatches the specified serialized command to one of the
   * specified transport addresses (the first where dispatching
   * succeeds) directly.
   *
   * @param destTAs a list of transport addresses where the command
   * dispatcher should try to dispatch the command.
   * @param commandPayload the serialized command that is to be
   * dispatched.
   * @return a serialized response command from the receiving
   * container.
   * @throws UnreachableException if none of the destination addresses
   * is reachable.
   */
  private byte[] dispatchDirectly(List destTAs, byte[] commandPayload, boolean requireFreshConnection) throws UnreachableException {
   
    // Loop on destinaltion addresses (No need to check again
    // that the list of addresses is not-null and not-empty)
    UnreachableException lastException = null;
    for (int i = 0; i < destTAs.size(); i++) {
      try {
        return send((TransportAddress) destTAs.get(i), commandPayload, requireFreshConnection);
      }
      catch (UnreachableException ue) {
        lastException = ue;
        // Can't send command to this address --> try the next one
        // DEBUG
        //TransportAddress ta = (TransportAddress)destTAs.get(i);
        //System.out.println("Sending command to " + ta.getProto() + "://" + ta.getHost() + ":" + ta.getPort() + " failed [" + ue.getMessage() + "]");
        //if (i < destTAs.size() - 1)
        //  System.out.println("Try next address");
      }
    }
   
    // Sending failed to all addresses --> Throw the last exception
    throw lastException;
  }
 
  /**
   * Dispatches the specified serialized command to one of the
   * specified transport addresses (the first where dispatching
   * succeeds) through the router.
   *
   * @param destTAs a list of transport addresses where the command
   * dispatcher should try to dispatch the command.
   * @param commandPayload the serialized command that is to be
   * dispatched.
   * @param origin a <tt>String</tt> object describing the origin of
   * the command to be dispatched.
   * @return a serialized response command from the receiving
   * container.
   * @throws DispatcherException if an error occurs during dispatching.
   * @throws UnreachableException if none of the destination addresses
   * is reachable.
   */
  private byte[] dispatchThroughRouter(List destTAs, byte[] commandPayload, boolean requireFreshConnection, String origin) throws DispatcherException, UnreachableException {
    // Build a FORWARD command
    Command forward = new Command(Command.FORWARD);
    forward.addParam(commandPayload);
    forward.addParam(destTAs);
    forward.addParam(origin);
   
    try {
      return send(routerTA, serializeCommand(forward), requireFreshConnection);
    }
    catch (LEAPSerializationException lse) {
      throw new DispatcherException("error serializing FORWARD command ["+lse.getMessage()+"].");
    }
  }
 
  /**
   * Checks whether some exceptions to be handled by the command
   * dispatcher occurred on the remote site. If this is the case the
   * command dispatcher throws the corresponding exception locally.
   *
   * @param response the resonse comman from the receiving container.
   * @throws DispatcherException if an error occurs on the remote site
   * during dispatching.
   * @throws UnreachableException if the destination address is not
   * reachable.
   */
  protected void checkRemoteExceptions(Command response)
  throws DispatcherException, UnreachableException {
    if (response.getCode() == Command.ERROR) {
      String exception = (String) response.getParamAt(0);
     
      // DispatcherException (some error occurred in the remote
      // CommandDispatcher) --> throw a DispatcherException.
      if (exception.equals("jade.imtp.leap.DispatcherException")) {
        throw new DispatcherException("DispatcherException in remote site. "+response.getParamAt(1));
      }
      else    // UnreachableException (the Command was sent to the router,
        // but the final destination was unreachable from there)
        // --> throw an UnreachableException
        if (exception.equals("jade.core.UnreachableException")) {
          throw new UnreachableException((String) response.getParamAt(1));
        }
    }
  }
 
  /**
   * Serializes a <tt>Command</tt> object into a <tt>byte</tt> array.
   *
   * @param command the command to be serialized.
   * @return the serialized command.
   * @throws LEAPSerializationException if the command cannot be
   * serialized.
   */
  protected byte[] serializeCommand(Command command) throws LEAPSerializationException {
    DeliverableDataOutputStream ddout = new DeliverableDataOutputStream(this);
    ddout.serializeCommand(command);
   
    return ddout.getSerializedByteArray();
  }
 
  /**
   * Deserializes a <tt>Command</tt> object from a <tt>byte</tt> array.
   *
   * @param data the <tt>byte</tt> array containing serialized command.
   * @return the deserialized command.
   * @throws LEAPSerializationException if the command cannot be
   * deserialized.
   */
  protected Command deserializeCommand(byte[] data) throws LEAPSerializationException {
    return new DeliverableDataInputStream(data, this).deserializeCommand();
  }
 
  /**
   * Builds a command that carries an exception.
   *
   * @param exception the exception to be carried.
   * @return the command carrying the exception.
   */
  protected Command buildExceptionResponse(Exception exception) {
    Command response = new Command(Command.ERROR);
    response.addParam(exception.getClass().getName());
    response.addParam(exception.getMessage());
   
    return response;
  }
 
  private TransportAddress initMainTA(Profile p) throws IMTPException {
   
    TransportAddress mainTA = null;
   
    try {
      String mainURL = p.getParameter(LEAPIMTPManager.MAIN_URL, null);
      // DEBUG
      //System.out.println("Main URL is "+mainURL);
     
      // Try to translate the mainURL into a TransportAddress
      // using a protocol supported by this CommandDispatcher
      try {
        mainTA = stringToAddr(mainURL);
      }
      catch (DispatcherException de) {
        // Failure --> A suitable protocol class may be explicitly
        // indicated in the profile (otherwise rethrow the exception)
        String mainTPClass = p.getParameter(MAIN_PROTO_CLASS, null);
        if (mainTPClass != null) {
          TransportProtocol tp = (TransportProtocol) Class.forName(mainTPClass).newInstance();
          mainTA = tp.stringToAddr(mainURL);
        }
        else {
          throw de;
        }
      }
     
      // If the router TA was not set --> use the mainTA as default
      //if (routerTA == null) {
      //  routerTA = mainTA;
      //}
     
      return mainTA;
     
    }
    catch (Exception e) {
      throw new IMTPException("Error getting Main Container address", e);
    }
   
  }
 
  /**
   * Adds (and activates) an ICP to this command dispatcher.
   *
   * @param peer the ICP to add.
   * @param args the arguments required by the ICP for the activation.
   * These arguments are ICP specific.
   */
  public void addICP(ICP peer, String peerID, Profile p) {
    try {
     
      // Activate the peer.
      TransportAddress  ta = peer.activate(this, peerID, p);
     
      // Add the listening address to the list of local addresses.
      TransportProtocol tp = peer.getProtocol();
      String            url = tp.addrToString(ta);
      if (myLogger.isLoggable(Logger.FINE)) {
        myLogger.log(Logger.FINE, "ICP "+peerID+" of class "+peer.getClass().getName()+" activated. Address is: "+url);
      }
      addresses.add(ta);
      urls.add(url);
     
      // Put the peer in the table of local ICPs.
      String proto = tp.getName().toLowerCase();
      List                  list = (List) icps.get(proto);
      if (list == null) {
        icps.put(proto, (list = new ArrayList()));
      }
     
      list.add(peer);
    }
    catch (ICPException icpe) {
      // Print a warning.
      myLogger.log(Logger.WARNING, "Error adding ICP "+peer+"["+icpe.getMessage()+"].");
    }
  }
 
  TransportProtocol getProtocol(String protoName) {
    List list = (List) icps.get(protoName.toLowerCase());
    if (list != null && list.size() > 0) {
      ICP icp = (ICP) list.get(0);
      return icp.getProtocol();
    }
    return null;
  }
 
  /**
   * Returns the ID of the specified remotized object.
   *
   * @param remoteObject the object whose ID should be returned.
   * @return the ID of the reomte object.
   * @throws RuntimeException if the specified object is not
   * remotized by this command dispatcher.
   */
  public int getID(Object remoteObject) throws IMTPException {
    Integer id = (Integer) ids.get(remoteObject);
    if (id != null) {
      return id.intValue();
    }
   
    throw new IMTPException("specified object is not remotized by this command dispatcher.");
  }
 
  /**
   * Returns the list of local addresses.
   *
   * @return the list of local addresses.
   */
  public List getLocalTAs() {
    return addresses;
  }
 
  /**
   * Returns the list of URLs corresponding to the local addresses.
   *
   * @return the list of URLs corresponding to the local addresses.
   */
  public List getLocalURLs() {
    return urls;
  }
 
  /**
   * Converts an URL into a transport address using the transport
   * protocol supported by the ICPs currently installed in the command
   * dispatcher. If there is no ICP installed to the command dispatcher
   * or their transport protocols are not able to convert the specified
   * URL a <tt>DispatcherException</tt> is thrown.
   *
   * @param url a <tt>String</tt> object specifying the URL to convert.
   * @return the converted URL.
   * @throws DispatcherException if there is no ICP installed to the
   * command dispatcher or the transport protocols of the ICPs
   * are not able to convert the specified URL.
   */
  protected TransportAddress stringToAddr(String url) throws DispatcherException {
    Iterator peers = icps.values().iterator();
   
    while (peers.hasNext()) {
     
      // Try to convert the url using the TransportProtocol
      // supported by this ICP.
      try {
        // There can be more than one peer supporting the same
        // protocol. Use the first one.
        return ((ICP) ((List) peers.next()).get(0)).getProtocol().stringToAddr(url);
      }
      catch (Throwable t) {
        // Do nothing and try the next one.
      }
    }
   
    // If we reach this point the url can't be converted.
    throw new DispatcherException("can't convert URL "+url+".");
  }
 
  /**
   * Registers the specified skeleton to the command dispatcher.
   *
   * @param skeleton a skeleton to be managed by the command
   * dispatcher.
   * @param remoteObject the remote object related to the specified
   * skeleton.
   */
  public synchronized void registerSkeleton(Skeleton skeleton, Object remotizedObject) throws IMTPException {
    Integer id = null;
    if(remotizedObject instanceof PlatformManager) {
      id = new Integer(0);
      name = "Service-Manager";
      setPlatformManager((PlatformManager) remotizedObject);
    }
    else {
      id = new Integer((int) (System.currentTimeMillis() & 0xffffff));
    }
    if (myLogger.isLoggable(Logger.FINE)) {
      myLogger.log(Logger.FINE, "Registering skeleton "+skeleton+" for remotized object "+remotizedObject+". ID is "+id);
    }
    skeletons.put(id, skeleton);
    ids.put(remotizedObject, id);
  }
 
  /**
   * Deregisters the specified remote object from the command dispatcher.
   *
   * @param remoteObject the remote object related to the specified
   * skeleton.
   */
  public synchronized void deregisterSkeleton(final Object remoteObject) {
    if (myLogger.isLoggable(Logger.FINE)) {
      myLogger.log(Logger.FINE, "Deregistering skeleton for remotized object "+remoteObject);
    }
    if (skeletons.size() == 1) {
      // This is the only skeleton --> The problem described below (in the "else" clause)
      // can't happen. Moreover the JVM is likely going to exit --> We can't delay the skeleton deregistration
      removeRemoteObject(remoteObject);
    }
    else {       
      // Hack: If the PlatformManager monitoring this node is in the same
      // JVM it needs some time to broadcast the termination of this node
      // to its replicas (if any) --> asynchronously deregister the skeleton after
      // a while
      Thread t = new Thread() {
        public void run() {
          try {
            Thread.sleep(1000);
          }
          catch (InterruptedException ie) {}
          removeRemoteObject(remoteObject);
        }
      };
      t.start();
    }
  }
 
  private synchronized void removeRemoteObject(Object remoteObject) {
    Object id = ids.remove(remoteObject);
    if (id != null) {
      if (myLogger.isLoggable(Logger.FINE)) {
        myLogger.log(Logger.FINE, "Asynchronous deregisteration of skeleton for remotized object "+remoteObject+". ID is "+id);
      }
       skeletons.remove(id);
    }
   
    // When there are no more skeletons, shutdown the CommandDispatcher (this closes all ICPs)
    if (ids.isEmpty()) {
      if (myLogger.isLoggable(Logger.FINE)) {
        myLogger.log(Logger.FINE, "All skeletons deregistered. Shutting down.");
      }
      shutDown();
    }
  }
 
  public Stub buildLocalStub(Object remotizedObject) throws IMTPException {
    Stub stub = null;
   
    if (remotizedObject instanceof Node) {
      stub = new NodeStub(getID(remotizedObject), platformName);
    }
    else if (remotizedObject instanceof PlatformManager) {
      stub = new PlatformManagerStub(platformName);
    }
    else {
      throw new IMTPException("can't create a stub for object "+remotizedObject+".");
    }
   
    stub.bind(this);
   
    // Add the local addresses.
    Iterator it = addresses.iterator();
    while (it.hasNext()) {
      stub.addTA((TransportAddress) it.next());
    }
   
    return stub;
  }
 
 
  /**
   * Selects a suitable peer and sends the specified serialized command
   * to the specified transport address.
   *
   * @param ta the transport addresses where the command should be
   * sent.
   * @param commandPayload the serialized command that is to be
   * sent.
   * @return a serialized response command from the receiving
   * container.
   * @throws UnreachableException if the destination address is not
   * reachable.
   */
  private byte[] send(TransportAddress ta, byte[] commandPayload, boolean requireFreshConnection) throws UnreachableException {
   
    // Get the ICPs suitable for the given TransportAddress.
    List list = (List) icps.get(ta.getProto().toLowerCase());
   
    if (list == null) {
      throw new UnreachableException("no ICP suitable for protocol "+ta.getProto()+".");
     
    }
   
    ICPException lastException = null;
    for (int i = 0; i < list.size(); i++) {
      try {
        return ((ICP) list.get(i)).deliverCommand(ta, commandPayload, requireFreshConnection);
      }
      catch (ICPException icpe) {
        lastException = icpe;
        // DEBUG
        // Print a warning and try next address
        //System.out.println("Warning: can't deliver command to "+ta+". "+icpe.getMessage());
      }
    }
   
    throw new UnreachableException("ICPException delivering command to address "+ta+".", lastException);
  }
 
  /**
   * Shuts the command dispatcher down and deactivates the local ICPs.
   */
  private void shutDown() {
    Iterator peersKeys = icps.keySet().iterator();
   
    while (peersKeys.hasNext()) {
      List list = (List) icps.get(peersKeys.next());
     
      for (int i = 0; i < list.size(); i++) {
        try {
          // This call interrupts the listening thread of this peer
          // and waits for its completion.
          ((ICP) list.get(i)).deactivate();
        }
        catch (ICPException icpe) {
          // Do nothing as this means that this peer had never been activated.
        }
      }
      list.clear();
    }
    icps.clear();
    urls.clear();
    addresses.clear();
    thePlatformManager = null;
    name = DEFAULT_NAME;
    nextID = 1;
    removeDispatcher(platformName);
    platformName = null;
  }
 
  // /////////////////////////////////////////
  // ICP.Listener INTERFACE
  // /////////////////////////////////////////
 
  /**
   * Handles a received (still serialized) command object, i.e.
   * deserialize it and launch processing of the command.
   *
   * @param commandPayload the command to be deserialized and
   * processed.
   * @return a <tt>byte</tt> array containing the serialized response
   * command.
   * @throws LEAPSerializationException if the command cannot be
   * (de-)serialized.
   */
  public byte[] handleCommand(byte[] commandPayload) throws LEAPSerializationException {
    try {
     
      // Deserialize the incoming command.
      Command command = deserializeCommand(commandPayload);
      Command response = null;
     
      // DEBUG
      //System.out.println("Received command of type " + command.getCode());
      if (command.getCode() == Command.FORWARD) {
       
        // DEBUG
        // System.out.println("Routing command");
       
        // If this is a FORWARD command then handle it directly.
        byte[] originalPayload = (byte[]) command.getParamAt(0);
        List   destTAs = (List) command.getParamAt(1);
        String origin = (String) command.getParamAt(2);
       
        if (origin.equals(name)) {
          // The forwarding mechanism is looping.
          response = buildExceptionResponse(new UnreachableException("destination unreachable (and forward loop)."));
        }
        else {
          try {
            response = dispatchSerializedCommand(destTAs, originalPayload, false, origin);
          }
          catch (UnreachableException ue) {
            response = buildExceptionResponse(ue);
          }
        }
      }
      else {
       
        // If this is a normal Command, let the proper Skeleton
        // process it.
        Integer id = new Integer(command.getObjectID());
        Skeleton s = (Skeleton) skeletons.get(id);
        if (s != null) {
          response = s.processCommand(command);
        }
        else {
          response = buildExceptionResponse(new DispatcherException("No skeleton for object-id "+id));
        }
      }
     
      return serializeCommand(response);
    }
    catch (LEAPSerializationException lse) {
      lse.printStackTrace();
      // Note that if the call below throws an exception this is not handled by
      // the CommandDispatcher. However this should never happen as an exception
      // response should always be serializable.
      return serializeCommand(buildExceptionResponse(new DispatcherException(lse.toString())));
    }
    catch (Exception e) {
      // Note that if the call below throws an exception this is not handled by
      // the CommandDispatcher. However this should never happen as an exception
      // response should always be serializable.
      return serializeCommand(buildExceptionResponse(new DispatcherException(e.toString())));
    }
  }
 
 
 
}

TOP

Related Classes of jade.imtp.leap.CommandDispatcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.