Package jade.imtp.leap.JICP

Source Code of jade.imtp.leap.JICP.BIBEDispatcher$OutConnectionHolder

/*****************************************************************
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.imtp.leap.JICP;

//#J2ME_EXCLUDE_FILE

import jade.core.BackEndContainer;
import jade.core.BEConnectionManager;
import jade.core.BackEnd;
import jade.core.FrontEnd;
import jade.core.IMTPException;
import jade.core.Profile;
import jade.core.ProfileException;
import jade.imtp.leap.FrontEndStub;
import jade.imtp.leap.MicroSkeleton;
import jade.imtp.leap.BackEndSkel;
import jade.imtp.leap.Dispatcher;
import jade.imtp.leap.ICPException;
import jade.util.leap.Properties;
import jade.util.Logger;

import java.io.*;
import java.net.*;
//import java.util.*;

/**
* Class declaration
* @author Giovanni Caire - TILAB
*/
public class BIBEDispatcher extends Thread implements BEConnectionManager, Dispatcher, JICPMediator {
  private static final long RESPONSE_TIMEOUT = 60000;

  private static final int REACHABLE = 1;
  private static final int UNREACHABLE = 0;

  private int frontEndStatus = UNREACHABLE;
  private long              maxDisconnectionTime;
  private long              keepAliveTime;
  private long              lastReceivedTime;

  private JICPMediatorManager        myMediatorManager;
  private String            myID;

  private byte lastSid = 0x0f;
  private int inpCnt = 0;
  private boolean active = true;

  protected InpConnectionHolder  inpHolder = new InpConnectionHolder();
  protected OutConnectionHolder  outHolder = new OutConnectionHolder();

  private MicroSkeleton mySkel = null;
  private FrontEndStub myStub = null;
  private BackEndContainer myContainer = null;

  private Logger myLogger = Logger.getMyLogger(getClass().getName());

  /**
   * Constructor declaration
   */
  public BIBEDispatcher() {
  }

  /////////////////////////////////////
  // JICPMediator interface implementation
  /////////////////////////////////////
  public String getID() {
    return myID;
  }

  /**
     Initialize parameters and start the embedded thread
   */
  public void init(JICPMediatorManager mgr, String id, Properties props) throws ICPException {
    myMediatorManager = mgr;
    myID = id;

    // Verbosity
    int verbosity = 1;
    try {
      verbosity = Integer.parseInt(props.getProperty("verbosity"));
    }
    catch (NumberFormatException nfe) {
      // Use default (1)
    }

    // Max disconnection time
    maxDisconnectionTime = JICPProtocol.DEFAULT_MAX_DISCONNECTION_TIME;
    try {
      maxDisconnectionTime = Long.parseLong(props.getProperty(JICPProtocol.MAX_DISCONNECTION_TIME_KEY));
    }
    catch (Exception e) {
      // Keep default
    }

    // Keep-alive time
    keepAliveTime = JICPProtocol.DEFAULT_KEEP_ALIVE_TIME;
    try {
      keepAliveTime = Long.parseLong(props.getProperty(JICPProtocol.KEEP_ALIVE_TIME_KEY));
    }
    catch (Exception e) {
      // Keep default
    }

    // inpCnt
    try {
      inpCnt = (Integer.parseInt(props.getProperty("lastsid")) + 1) & 0x0f;
    }
    catch (Exception e) {
      // Keep default
    }

    // lastSid
    try {
      lastSid = (byte) (Integer.parseInt(props.getProperty("outcnt")) -1);
      if (lastSid < 0) {
        lastSid = 0x0f;
      }
    }
    catch (Exception e) {
      // Keep default
    }

    // Start the embedded thread dealing with outgoing commands
    start();

    myStub = new FrontEndStub(this);
    mySkel = startBackEndContainer(props);
  }

  protected final BackEndSkel startBackEndContainer(Properties props) throws ICPException {
    try {

      String nodeName = myID.replace(':', '_');
      props.setProperty(Profile.CONTAINER_NAME, nodeName);

      myContainer = new BackEndContainer(props, this);
      // BOOTSTRAP_AGENTS Gestire nuovo valore di ritorno
      if (!myContainer.connect()) {
        throw new ICPException("BackEnd container failed to join the platform");
      }
      // Possibly the node name was re-assigned by the main
      myID = myContainer.here().getName();
      if(myLogger.isLoggable(Logger.CONFIG)) {
        myLogger.log(Logger.CONFIG,"BackEndContainer "+myID+" successfully joined the platform.");
      }
      return new BackEndSkel(myContainer);
    }
    catch (ProfileException pe) {
      // should never happen
      pe.printStackTrace();
      throw new ICPException("Error creating profile");
    }
  }

  private Object shutdownLock = new Object();

  /**
     Shutdown self initiated or forced by the JICPServer this
     BackEndContainer is attached to.
   */
  public void kill() {
    // Avoid killing two times
    synchronized (shutdownLock) {
      if (active) {
        active = false;
        // Force the BackEndContainer to terminate. This will also
        // cause this BIBEDispatcher to terminate and deregister
        // from the JICPServer
        myContainer.shutDown();
      }
    }
  }

  /**
     This is called by the JICPServer when a JICP packet addressing this
     mediator as recipient-ID is received. In the case of the BIBEDispatcher
     this should never happen.
   */
  public JICPPacket handleJICPPacket(JICPPacket p, InetAddress addr, int port) throws ICPException {
    return null;
  }

  /**
     This is called by the JICPServer when a JICP CREATE_MEDIATOR or
     CONNECT_MEDIATOR is received.
   */
  public boolean handleIncomingConnection(Connection c, JICPPacket pkt, InetAddress addr, int port) {
    boolean inp = false;
    byte[] data = pkt.getData();
    if (data.length == 1) {
      inp = (data[0] == 1);
    }
    else {
      // Backward compatibility
      try {
        inp = (new String(data)).equals("inp");
      }
      catch (Exception e) {}
    }
    if (inp) {
      inpHolder.setConnection(c);
    }
    else {
      outHolder.setConnection(c);
    }

    // Update keep-alive info
    lastReceivedTime = System.currentTimeMillis();

    return true;
  }

  public void tick(long currentTime) {
    if (keepAliveTime > 0) {
      if ((currentTime - lastReceivedTime) > (keepAliveTime + 60000)) {
        // Missing keep-alive.
        // The OUT connection is no longer valid
        if (outHolder.isConnected()) {
          if(myLogger.isLoggable(Logger.WARNING))
            myLogger.log(Logger.WARNING,myID+" - Missing keep-alive");
          outHolder.resetConnection();
        }
        // Check the INP connection. Since this method must return
        // asap, does it in a separated Thread
        if (inpHolder.isConnected()) {
          Thread t = new Thread() {
            public void run() {
              try {
                JICPPacket pkt = new JICPPacket(JICPProtocol.KEEP_ALIVE_TYPE, JICPProtocol.DEFAULT_INFO, null);
                dispatchPacket(pkt, false);
                if(myLogger.isLoggable(Logger.FINE))
                  myLogger.log(Logger.FINE, myID+" - IC valid");
              }
              catch (Exception e) {
                // Just do nothing
              }
            }
          };
          t.start();
        }
      }
    }
  }

  ////////////////////////////////////////////////
  // BEConnectionManager interface implementation
  ////////////////////////////////////////////////
  /**
     Return a stub of the remote FrontEnd that is connected to the
     local BackEnd.
     @param be The local BackEnd
     @param props Additional (implementation dependent) connection
     configuration properties.
     @return A stub of the remote FrontEnd.
   */
  public FrontEnd getFrontEnd(BackEnd be, Properties props) throws IMTPException {
    return myStub;
  }


  /**
     Make this BackEndDispatcher terminate.
   */
  public void shutdown() {
    if(myLogger.isLoggable(Logger.FINE))
      myLogger.log(Logger.FINE,myID+" - Initiate BIBEDispatcher shutdown");


    // Deregister from the JICPServer
    if (myID != null) {
      myMediatorManager.deregisterMediator(myID);
      myID = null;
    }

    active = false;
    inpHolder.resetConnection(true);
    outHolder.resetConnection();
  }

  //////////////////////////////////////////
  // Dispatcher interface implementation
  //////////////////////////////////////////
  public byte[] dispatch(byte[] payload, boolean flush) throws ICPException {
    JICPPacket pkt = new JICPPacket(JICPProtocol.COMMAND_TYPE, JICPProtocol.DEFAULT_INFO, payload);
    pkt = dispatchPacket(pkt, flush);
    return pkt.getData();
  }

  private synchronized JICPPacket dispatchPacket(JICPPacket pkt, boolean flush) throws ICPException {
    Connection inpConnection = inpHolder.getConnection(flush);
    if (inpConnection != null && active) {
      int status = 0;
      if (pkt.getType() == JICPProtocol.KEEP_ALIVE_TYPE) {
        if(myLogger.isLoggable(Logger.FINER)) {
          myLogger.log(Logger.FINER,myID+" - Issuing Keep-alive to FE "+inpCnt);
        }
      }
      else {
        if(myLogger.isLoggable(Logger.FINER)) {
          myLogger.log(Logger.FINER,myID+" - Issuing command to FE "+inpCnt);
        }
      }
      pkt.setSessionID((byte) inpCnt);
      try {
        inpConnection.writePacket(pkt);
        status = 1;

        // Create a watch-dog to avoid waiting forever
        inpHolder.startWatchDog(RESPONSE_TIMEOUT);
        pkt = readPacket(inpConnection);
        // Reply received --> Remove the watch-dog
        inpHolder.stopWatchDog();
        status = 2;

        if(myLogger.isLoggable(Logger.FINER))
          myLogger.log(Logger.FINER,myID+" - Response received from FE "+pkt.getSessionID());

        if (pkt.getType() == JICPProtocol.ERROR_TYPE) {
          // Communication OK, but there was a JICP error on the peer
          throw new ICPException(new String(pkt.getData()));
        }
        if ((pkt.getInfo() & JICPProtocol.TERMINATED_INFO) != 0) {
          // This is the response to an exit command --> Suicide
          shutdown();
        }
        inpCnt = (inpCnt+1) & 0x0f;
        return pkt;
      }
      catch (IOException ioe) {
        // Can't reach the FrontEnd.
        if(myLogger.isLoggable(Logger.WARNING))
          myLogger.log(Logger.WARNING,myID+" - IOException IC["+status+"]"+ioe);

        inpHolder.resetConnection(false);
        throw new ICPException("Dispatching error.", ioe);
      }
    }
    else {
      throw new ICPException("Unreachable");
    }
  }

  //////////////////////////////////////////////////
  // The embedded Thread handling outgoing commands
  //////////////////////////////////////////////////
  private JICPPacket lastResponse;

  public void run() {
    lastResponse = null;
    int status = 0;

    if(myLogger.isLoggable(Logger.FINE))
      myLogger.log(Logger.FINE,myID+" - BIBEDispatcher thread started");

    while (active) {
      try {
        while (active) {
          status = 0;
          Connection outConnection = outHolder.getConnection();
          if (outConnection != null) {
            JICPPacket pkt = readPacket(outConnection);
            status = 1;
            pkt = handlePacket(pkt);
            if (pkt != null) {
              status = 2;

              outConnection.writePacket(pkt);
              status = 3;
            }
          }
          else {
            handlePeerExited("Max disconnection timeout expired");
          }
        }
      }
      catch (IOException ioe) {
        if (active) {
          if(myLogger.isLoggable(Logger.WARNING))
            myLogger.log(Logger.WARNING,myID+" - IOException OC["+status+"]"+ioe);

          outHolder.resetConnection();
        }
      }
    }
    if(myLogger.isLoggable(Logger.FINE))
      myLogger.log(Logger.FINE,myID+" - BIBEDispatcher Thread terminated");

  }

  protected JICPPacket handlePacket(JICPPacket pkt) {
    JICPPacket reply = null;
    if (pkt.getType() == JICPProtocol.KEEP_ALIVE_TYPE) {
      // Keep-alive packet
      if(myLogger.isLoggable(Logger.FINEST)) {
        myLogger.log(Logger.FINEST,myID+" - Keep-alive received");
      }

      reply = new JICPPacket(JICPProtocol.RESPONSE_TYPE, getReconnectInfo(), null);
    }
    else {
      // Outgoing command
      if ((pkt.getInfo() & JICPProtocol.TERMINATED_INFO) != 0) {
        // PEER TERMINATION NOTIFICATION
        // The remote FrontEnd has terminated spontaneously -->
        // Terminate and notify up.
        handlePeerExited("Peer termination notification received");
        return null;
      }
      byte sid = pkt.getSessionID();
      if (sid == lastSid) {
        if(myLogger.isLoggable(Logger.WARNING))
          myLogger.log(Logger.WARNING,myID+" - Duplicated command from FE "+sid);

        reply = lastResponse;
      }
      else {
        if(myLogger.isLoggable(Logger.FINER))
          myLogger.log(Logger.FINER,myID+" - Command from FE received "+sid);

        byte[] rspData = mySkel.handleCommand(pkt.getData());
        if(myLogger.isLoggable(Logger.FINER))
          myLogger.log(Logger.FINER,myID+" - Command from FE served "+ sid);

        reply = new JICPPacket(JICPProtocol.RESPONSE_TYPE, getReconnectInfo(), rspData);
        reply.setSessionID(sid);
        lastSid = sid;
        lastResponse = reply;
      }
    }
    return reply;
  }


  private byte getReconnectInfo() {
    byte info = JICPProtocol.DEFAULT_INFO;
    // If the inpConnection is null request the FrontEnd to reconnect
    if (!inpHolder.isConnected()) {
      info |= JICPProtocol.RECONNECT_INFO;
    }
    return info;
  }

  private void handlePeerExited(String msg) {
    if(myLogger.isLoggable(Logger.INFO))
      myLogger.log(Logger.INFO,myID+" - "+msg);

    kill();
  }

  private JICPPacket readPacket(Connection c) throws IOException {
    JICPPacket pkt = c.readPacket();
    // Update keep-alive info
    lastReceivedTime = System.currentTimeMillis();
    return pkt;
  }

  /**
     Inner class InpConnectionHolder.
     Wrapper for the connection used to deliver commands to the FrontEnd
   */
  protected class InpConnectionHolder {
    private Connection myConnection;
    private boolean connectionRefreshed;
    private boolean waitingForFlush = false;
    private Thread watchDog = null;

    private synchronized void setConnection(Connection c) {
      if(myLogger.isLoggable(Logger.FINE))
        myLogger.log(Logger.FINE,myID+" - New input connection.");

      // Close the old connection
      if (myConnection != null) {
        close(myConnection);
      }
      // Stop the WatchDog if any
      stopWatchDog();
      // Set the new connection
      myConnection = c;
      connectionRefreshed = true;
      waitingForFlush = myStub.flush();
      //myContainer.notifyInputConnectionReady();
    }

    private synchronized Connection getConnection(boolean flush) {
      if (waitingForFlush && (!flush)) {
        return null;
      }
      waitingForFlush = false;
      connectionRefreshed = false;
      return myConnection;
    }

    public synchronized void resetConnection(boolean force) {
      if (!connectionRefreshed || force) {
        if (myConnection != null) {
          close(myConnection);
        }
        myConnection = null;
      }
    }

    private synchronized boolean isConnected() {
      return myConnection != null;
    }

    private synchronized void startWatchDog(final long timeout) {
      watchDog = new Thread() {
        public void run() {
          try {
            Thread.sleep(timeout);
            // WatchDog expired --> close the connection
            if(myLogger.isLoggable(Logger.WARNING))
              myLogger.log(Logger.WARNING,myID+" - Response timeout expired");

            resetConnection(false);
          }
          catch (InterruptedException ie) {
            // Watch dog removed. Just do nothing
          }
        }
      };
      watchDog.start();
    }

    private synchronized void stopWatchDog() {
      if (watchDog != null) {
        watchDog.interrupt();
        watchDog = null;
      }
    }
  } // END of inner class InpConnectionHolder


  /**
     Inner class OutConnectionHolder
     Wrapper for the connection used to receive commands from the FrontEnd
   */
  protected class OutConnectionHolder {
    private Connection myConnection;
    private boolean connectionRefreshed;

    private synchronized void setConnection(Connection c) {
      if(myLogger.isLoggable(Logger.FINE))
        myLogger.log(Logger.FINE,myID+" - New output connection.");

      if (myConnection != null) {
        close(myConnection);
      }
      myConnection = c;
      connectionRefreshed = true;
      notifyAll();
    }

    private synchronized Connection getConnection() {
      while (myConnection == null) {
        try {
          wait(maxDisconnectionTime);
          if (myConnection == null) {
            return null;
          }
        }
        catch (Exception e) {
          if(myLogger.isLoggable(Logger.WARNING))
            myLogger.log(Logger.WARNING,myID+" - Spurious wake up");
        }
      }
      connectionRefreshed = false;
      return myConnection;
    }

    public synchronized void resetConnection() {
      if (!connectionRefreshed) {
        if (myConnection != null) {
          close(myConnection);
        }
        myConnection = null;
      }
    }

    private synchronized boolean isConnected() {
      return myConnection != null;
    }
  } // END of inner class OutConnectionHolder

  private void close(Connection c) {
    try {
      c.close();
    }
    catch (IOException ioe) {
    }
  }
}

TOP

Related Classes of jade.imtp.leap.JICP.BIBEDispatcher$OutConnectionHolder

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.