/**
   * Initializes this network address manager service implementation and starts all
   * processes/threads associated with this address manager, such as a stun firewall/nat detector,
   * keep alive threads, binding lifetime discovery threads and etc. The method may also be used
   * after a call to stop() as a reinitialization technique.
   */
  public void start() {
    // init stun
    String stunAddressStr = null;
    int port = -1;
    stunAddressStr = NetaddrActivator.getConfigurationService().getString(PROP_STUN_SERVER_ADDRESS);
    String portStr = NetaddrActivator.getConfigurationService().getString(PROP_STUN_SERVER_PORT);

    this.localHostFinderSocket = initRandomPortSocket();

    if (stunAddressStr == null || portStr == null) {
      useStun = false;
      // we use the default stun server address only for chosing a public
      // route and not for stun queries.
      stunServerAddress = new StunAddress(DEFAULT_STUN_SERVER_ADDRESS, DEFAULT_STUN_SERVER_PORT);
      logger.info(
          "Stun server address("
              + stunAddressStr
              + ")/port("
              + portStr
              + ") not set (or invalid). Disabling STUN.");

    } else {
      try {
        port = Integer.valueOf(portStr).intValue();
      } catch (NumberFormatException ex) {
        logger.error(portStr + " is not a valid port number. " + "Defaulting to 3478", ex);
        port = 3478;
      }

      stunServerAddress = new StunAddress(stunAddressStr, port);
      detector = new SimpleAddressDetector(stunServerAddress);

      if (logger.isDebugEnabled()) {
        logger.debug(
            "Created a STUN Address detector for the following "
                + "STUN server: "
                + stunAddressStr
                + ":"
                + port);
      }
      detector.start();
      logger.debug("STUN server detector started;");

      // make sure that someone doesn't set invalid stun address and port
      NetaddrActivator.getConfigurationService()
          .addVetoableChangeListener(PROP_STUN_SERVER_ADDRESS, this);
      NetaddrActivator.getConfigurationService()
          .addVetoableChangeListener(PROP_STUN_SERVER_PORT, this);

      // now start a thread query to the stun server and only set the
      // useStun flag to true if it succeeds.
      launchStunServerTest();
    }
  }
  /**
   * Kills all threads/processes lauched by this thread and prepares it for shutdown. You may use
   * this method as a reinitialization technique ( you'll have to call start afterwards)
   */
  public void stop() {
    try {
      try {
        detector.shutDown();
      } catch (Exception ex) {
        logger.debug("Failed to properly shutdown a stun detector: " + ex.getMessage());
      }
      detector = null;
      useStun = false;

      // remove the listeners
      NetaddrActivator.getConfigurationService()
          .removeVetoableChangeListener(PROP_STUN_SERVER_ADDRESS, this);

      NetaddrActivator.getConfigurationService()
          .removeVetoableChangeListener(PROP_STUN_SERVER_PORT, this);

    } finally {
      logger.logExit();
    }
  }
  /**
   * Initializes and binds a socket that on a random port number. The method would try to bind on a
   * random port and retry 5 times until a free port is found.
   *
   * @return the socket that we have initialized on a randomport number.
   */
  private DatagramSocket initRandomPortSocket() {
    DatagramSocket resultSocket = null;
    String bindRetriesStr =
        NetaddrActivator.getConfigurationService().getString(BIND_RETRIES_PROPERTY_NAME);

    int bindRetries = 5;

    if (bindRetriesStr != null) {
      try {
        bindRetries = Integer.parseInt(bindRetriesStr);
      } catch (NumberFormatException ex) {
        logger.error(
            bindRetriesStr
                + " does not appear to be an integer. "
                + "Defaulting port bind retries to "
                + bindRetries,
            ex);
      }
    }

    int currentlyTriedPort = NetworkUtils.getRandomPortNumber();

    // we'll first try to bind to a random port. if this fails we'll try
    // again (bindRetries times in all) until we find a free local port.
    for (int i = 0; i < bindRetries; i++) {
      try {
        resultSocket = new DatagramSocket(currentlyTriedPort);
        // we succeeded - break so that we don't try to bind again
        break;
      } catch (SocketException exc) {
        if (exc.getMessage().indexOf("Address already in use") == -1) {
          logger.fatal(
              "An exception occurred while trying to create" + "a local host discovery socket.",
              exc);
          resultSocket = null;
          return null;
        }
        // port seems to be taken. try another one.
        logger.debug("Port " + currentlyTriedPort + " seems in use.");
        currentlyTriedPort = NetworkUtils.getRandomPortNumber();
        logger.debug("Retrying bind on port " + currentlyTriedPort);
      }
    }

    return resultSocket;
  }