Package jade.core

Source Code of jade.core.MainDetectionManager$MainAddr

package jade.core;

import jade.util.Logger;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

//#J2ME_EXCLUDE_FILE

class MainDetectionManager {

  private final static Logger logger = Logger.getMyLogger(MainDetectionManager.class.getName());

  /*
   *  configuration options with defaults
   */
  private final static String OPT_MCAST_ADDR = "jade_core_MainDetectionManager_mcastaddr";
  private final static String OPT_MCAST_ADDR_DEFAULT = "239.255.10.99";

  private final static String OPT_MCAST_PORT = "jade_core_MainDetectionManager_mcastport";
  private final static String OPT_MCAST_PORT_DEFAULT = "1199";

  private final static String OPT_MCAST_TTL = "jade_core_MainDetectionManager_mcastttl";
  private final static String OPT_MCAST_TTL_DEFAULT = "4";

  private final static String OPT_MCAST_FIRST_TIMEOUT = "jade_core_MainDetectionManager_mcastfirsttimeout";
  private final static String OPT_MCAST_FIRST_TIMEOUT_DEFAULT = "500";

  private final static String OPT_MCAST_TIMEOUT = "jade_core_MainDetectionManager_mcasttimeout";
  private final static String OPT_MCAST_TIMEOUT_DEFAULT = "2500";

  private final static String OPT_MCAST_RETRIES = "jade_core_MainDetectionManager_mcastretries";
  private final static String OPT_MCAST_RETRIES_DEFAULT = "3";

  /*
   *  protocol data
   */
  // version string
  public final static String PROTO_VERSION = " MJADE/1.0";

  // charset used to encode/decode packets
  public final static String PROTO_ENCODING = "ISO-8859-1";

  // protocol commands
  public final static String PROTO_CMD_GETMAIN = "get-main";
  public final static String PROTO_CMD_PING = "ping";

  // constants for get-main command
  public final static String PROTO_ADDRESSES_SEPARATOR = ";";
  public final static String PROTO_ADDR_SEPARATOR = ":";

  // protocol responses
  public final static String PROTO_RESP_OK = "200 OK ";
  public final static String PROTO_RESP_ERR = "500 Internal Server Error";
  public final static String PROTO_RESP_NOTFOUND = "404 Not Found";

  // buffer size
  private final static int DGRAM_BUF_LEN = 1024;

  // source port for datagram packets
  private final static int SRC_PORT = 1198;

  private static final String MATCH_ALL_PLATFORMS = "*";

  /*
   * inner class used to parse and keep addresses from main
   */
  private static class MainAddr {
    public String protocol;
    public String hostname;
    public int port;

    public MainAddr(String address) {
      StringTokenizer ast;
      /*
       * Address must be in form "proto:host:port"
       * If address has a bad format, the following code
       * will throw an exception. The behaviour is by design.
       */
      ast = new StringTokenizer(address, PROTO_ADDR_SEPARATOR);
      protocol = ast.nextToken();
      hostname = ast.nextToken();
      port = Integer.parseInt(ast.nextToken());
    }
  }

  /*
   * inner class used to get multicast parameters from Profile
   */
  static class MulticastParams {
    String address;
    int port;
    int firstTimeout;
    int timeout;
    int retries;
    int ttl;

    private void checkTrue(boolean condition, String paramName, String paramValue) throws ProfileException {
      if (!condition) {
        throw new ProfileException("Bad value \""+paramValue+"\" for option "+paramName);
      }
    }

    private int parseInt(String paramName, String paramValue) throws ProfileException {
      try {
        return Integer.parseInt(paramValue);
      } catch (NumberFormatException nfe) {
        throw new ProfileException("Bad value \""+paramValue+"\" for option "+paramName+": integer value required", nfe);
      }
    }

    public MulticastParams(Profile p) throws ProfileException {
      String s;

      address = p.getParameter(OPT_MCAST_ADDR, OPT_MCAST_ADDR_DEFAULT);
      checkTrue(address != null && address.length() > 0, OPT_MCAST_ADDR, address);

      s = p.getParameter(OPT_MCAST_PORT, OPT_MCAST_PORT_DEFAULT);
      port = parseInt(OPT_MCAST_PORT, s);
      checkTrue(port > 0, OPT_MCAST_PORT, s);

      s = p.getParameter(OPT_MCAST_FIRST_TIMEOUT, OPT_MCAST_FIRST_TIMEOUT_DEFAULT);
      firstTimeout = parseInt(OPT_MCAST_FIRST_TIMEOUT, s);
      checkTrue(firstTimeout > 0, OPT_MCAST_FIRST_TIMEOUT, s);

      s = p.getParameter(OPT_MCAST_TIMEOUT, OPT_MCAST_TIMEOUT_DEFAULT);
      timeout = parseInt(OPT_MCAST_TIMEOUT, s);
      checkTrue(timeout >= 0, OPT_MCAST_TIMEOUT, s);

      s = p.getParameter(OPT_MCAST_RETRIES, OPT_MCAST_RETRIES_DEFAULT);
      retries = parseInt(OPT_MCAST_RETRIES, s);
      checkTrue(retries >= 0, OPT_MCAST_RETRIES, s);

      s = p.getParameter(OPT_MCAST_TTL, OPT_MCAST_TTL_DEFAULT);
      ttl = parseInt(OPT_MCAST_TTL, s);
      checkTrue(ttl > 0, OPT_MCAST_TTL, s);
    }
  }

  /*
   * decode data using the right protocol charset and throw away trailing '\0's
   */
  public static String decodeData(byte[] data) {
    String result = null;

    try {
      // discard all null bytes at the end of the buffer
      int i;
      for (i = data.length-1; i >= 0; i--) {
        if (data[i] != 0) {
          break;
        }
      }
      if (i > 0) {
        result = new String(data, 0, i+1, PROTO_ENCODING);
      }
    } catch (UnsupportedEncodingException uee) {
      logger.log(Logger.SEVERE, "Cannot decode data with charset "+PROTO_ENCODING, uee);
    }
    return result;
  }

  /*
   *  check protocol version at the end of request
   */
  public static int checkProtocolVersion(String request) throws Exception {
    int i = request.lastIndexOf(PROTO_VERSION);
    if (i < 0) {
      throw new Exception("Bad message");
    }
    if (i+PROTO_VERSION.length() != request.length()) {
      throw new Exception("Wrong protocol version");
    }
    return i;
  }

  /*
   * extract Main address from the list got in response
   */
  private static MainAddr extractAddress(String response, String proto) {
    logger.log(Logger.FINER, "MainDetectionManager::extractAddress(response=\""+response+"\", proto=\""+proto+"\")");
    MainAddr result = null;

    // parse a list of addresses in form proto_1:hostname_1:port_1;...;proto_n:hostname_n:port_n
    StringTokenizer st = new StringTokenizer(response, PROTO_ADDRESSES_SEPARATOR);
    String address;
    MainAddr ma;
    while (st.hasMoreTokens()) {
      address = st.nextToken();
      try {
        ma = new MainAddr(address);
        result = ma;
        /*
         * If either no protocol specified or main has an address with
         * desired protocol, then we are done, otherwise, go on searching
         * for the right protocol.
         */
        if (proto == null || proto.equals(ma.protocol)) {
          break;
        }
      } catch (Exception e) {
        // skip malformed address...
        logger.log(Logger.WARNING, "Skipping malformed address", e);
      }
    }
    return result;
  }

  /*
   * manage responses for get-main query
   */
  private static MainAddr manageGetMainResponses(List responses, String proto) {
    logger.log(Logger.FINER, "MainDetectionManager::manageGetMainResponses(responses.size()="+responses.size()+", proto=\""+proto+"\")");
    MainAddr mainAddress = null;

    String response;
    String s;
    Iterator iter = responses.iterator();
    byte[] data;
    InetAddress senderHost;
    int senderPort;
    while (iter.hasNext()) {
      DatagramPacket p = (DatagramPacket)iter.next();
      data = p.getData();
      senderHost = p.getAddress();
      senderPort = p.getPort();
      response = decodeData(data);

      try {
        if (response == null) {
          throw new Exception("Response cannot be decoded");
        }
        s = response;

        // first of all, check protocol version in response
        int i = checkProtocolVersion(s);
        s = s.substring(0, i);

        // then, check for errors returned by Main
        i = s.indexOf(PROTO_RESP_OK);
        if (i != 0) {
          throw new Exception("Main container returned Error in response");
        }
        s = s.substring(PROTO_RESP_OK.length());
        mainAddress = extractAddress(s, proto);
        // bail out at first good answer
        break;
      } catch (Exception e) {
        logger.log(Logger.WARNING, "Error managing response \""+response+"\" from "+senderHost+":"+senderPort+"; response discarded", e);
      }
    }
    // at last, extract Main address
    return mainAddress;
  }

  /*
   * send multicast request, managing timeout and retries, and return decoded response
   */
  private static List multicAsk(String request, Profile p) throws ProfileException {
    logger.log(Logger.FINER, "MainDetectionManager::multicAsk(...)");
    List result = null;
    /*
     * usually we expect only one Main, but when more are active,
     * we collect all responses and choose the one we like more
     */
    List responses = new ArrayList(3);

    // parse parameters from profile
    MulticastParams mcast = new MulticastParams(p);

    logger.log(Logger.FINER, "MainDetectionManager::multicAsk(): prepared msg=\""+request+"\"");

    InetAddress mcastGroupAddress;
    try {
      mcastGroupAddress = InetAddress.getByName(mcast.address);
    } catch (UnknownHostException e) {
      throw new ProfileException("Cannot resolve address "+mcast.address, e);
    }
    if (!mcastGroupAddress.isMulticastAddress()) {
      throw new ProfileException("Address "+mcast.address+" is not a multicast address");
    }

    MulticastSocket socket = null;
    try {
      try {
        socket = new MulticastSocket(SRC_PORT);
        socket.setTimeToLive(mcast.ttl);
        socket.setSoTimeout(mcast.firstTimeout);
      } catch (IOException ioe) {
        throw new ProfileException("Error setting up multicast socket", ioe);
      }
      DatagramPacket packet;
      int retries = mcast.retries+1;
      do {
        try {
          logger.log(Logger.FINER, "MainDetectionManager::multicAsk(): sending msg =\""+request+"\" to "+mcast.address+":"+mcast.port);
          packet = new DatagramPacket(request.getBytes(PROTO_ENCODING), request.length(), mcastGroupAddress, mcast.port);
          socket.send(packet);

          do {
            try {
              // get responses
              byte[] buf = new byte[DGRAM_BUF_LEN];
              DatagramPacket recv = new DatagramPacket(buf, buf.length);
              socket.receive(recv);
              logger.log(Logger.FINER, "MainDetectionManager::multicAsk(): received "+recv.getLength()+" bytes");
              responses.add(recv);
            } catch (SocketTimeoutException ste) {
              socket.setSoTimeout(mcast.timeout);
              if (responses.size() > 0) {
                // we received at least one answer, it's enough
                break;
              }
              throw ste;
            }
          } while(true);
          if (responses.size() > 0) {
            // bail out
            break;
          }
        } catch (SocketTimeoutException ste) {
          // timeout, go for retry
          --retries;
          logger.log(Logger.FINER, "MainDetectionManager::multicAsk(): timeout, "+retries+" retries left");
        }
      } while (retries > 0);
      if (responses.size() > 0) {
        // we got at least a response!
        result = responses;
      }
    } catch (Exception e) {
      throw new ProfileException("Error during multicast querying", e);
    } finally {
      if (socket != null) {
        socket.close();
      }
    }
    return result;
  }

  /*
   * build request for get-main command
   */
  private static String buildGetMainRequest(String platformName, String proto) {
    // build request
    StringBuffer msg = new StringBuffer(PROTO_CMD_GETMAIN);
    if (platformName != null) {
      msg.append('@');
      msg.append(platformName);
    }
    if (proto != null) {
      msg.append(':');
      msg.append(proto);
    }
    msg.append(PROTO_VERSION);
    return msg.toString();
  }

  /*
   * search for a main container
   */
  private static MainAddr getMainAddress(Profile profile) throws ProfileException {
    logger.log(Logger.FINER, "MainDetectionManager::getMainAddress(...)");
    MainAddr result = null;
    // get platform name and protocol
    String platformName = profile.getParameter(Profile.PLATFORM_ID, null);
    if (platformName == null) {
      throw new ProfileException("platform id is mandatory when using automatic main detection; use \""+MATCH_ALL_PLATFORMS+"\" to match all");
    }
    if (MATCH_ALL_PLATFORMS.equals(platformName)) {
      platformName = null;
    }
    String proto = profile.getParameter(Profile.MAIN_PROTO, null);

    // build request
    String msg = buildGetMainRequest(platformName, proto);

    // send multicast query to search for a main
    List responses = multicAsk(msg, profile);
    if (responses != null) {
      // parse the response
      result = manageGetMainResponses(responses, proto);
    }
    return result;
  }

  public static void detect(ProfileImpl profile) throws ProfileException {
    logger.log(Logger.FINER, "MainDetectionManager::detect(...)");
    if (!profile.isMasterMain()) {
      MainAddr mainAddress = getMainAddress(profile);
      if (mainAddress == null) {
        // FIXME is the right exception to throw?
        throw new ProfileException("Cannot detect Main Container");
      }
      logger.log(Logger.CONFIG, "setting "+Profile.MAIN_PROTO+"="+mainAddress.protocol);
      profile.setParameter(Profile.MAIN_PROTO, mainAddress.protocol);
      logger.log(Logger.CONFIG, "setting "+Profile.MAIN_HOST+"="+mainAddress.hostname);
      profile.setParameter(Profile.MAIN_HOST, mainAddress.hostname);
      logger.log(Logger.CONFIG, "setting "+Profile.MAIN_PORT+"="+mainAddress.port);
      profile.setParameter(Profile.MAIN_PORT, Integer.toString(mainAddress.port));
    }
  }

  public static MulticastMainDetectionListener createListener(ProfileImpl profile, IMTPManager m) throws ProfileException {
    logger.log(Logger.FINER, "MainDetectionManager::export(...)");
    MulticastMainDetectionListener listener = new MulticastMainDetectionListener(profile, m);
    Thread listenerThread = new Thread(listener);
    listenerThread.start();
    return listener;
  }
}
TOP

Related Classes of jade.core.MainDetectionManager$MainAddr

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.