/* (non-Javadoc)
   * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.client.mqttv3.MqttConnectOptions, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
   */
  public IMqttToken connect(
      MqttConnectOptions options, Object userContext, IMqttActionListener callback)
      throws MqttException, MqttSecurityException {
    final String methodName = "connect";
    if (comms.isConnected()) {
      throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_CLIENT_CONNECTED);
    }
    if (comms.isConnecting()) {
      throw new MqttException(MqttException.REASON_CODE_CONNECT_IN_PROGRESS);
    }
    if (comms.isDisconnecting()) {
      throw new MqttException(MqttException.REASON_CODE_CLIENT_DISCONNECTING);
    }
    if (comms.isClosed()) {
      throw new MqttException(MqttException.REASON_CODE_CLIENT_CLOSED);
    }

    this.connOpts = options;
    this.userContext = userContext;
    final boolean automaticReconnect = options.isAutomaticReconnect();

    // @TRACE 103=cleanSession={0} connectionTimeout={1} TimekeepAlive={2} userName={3} password={4}
    // will={5} userContext={6} callback={7}
    log.fine(
        CLASS_NAME,
        methodName,
        "103",
        new Object[] {
          Boolean.valueOf(options.isCleanSession()),
          new Integer(options.getConnectionTimeout()),
          new Integer(options.getKeepAliveInterval()),
          options.getUserName(),
          ((null == options.getPassword()) ? "[null]" : "[notnull]"),
          ((null == options.getWillMessage()) ? "[null]" : "[notnull]"),
          userContext,
          callback
        });
    comms.setNetworkModules(createNetworkModules(serverURI, options));
    comms.setReconnectCallback(
        new MqttCallbackExtended() {

          public void messageArrived(String topic, MqttMessage message) throws Exception {}

          public void deliveryComplete(IMqttDeliveryToken token) {}

          public void connectComplete(boolean reconnect, String serverURI) {}

          public void connectionLost(Throwable cause) {
            if (automaticReconnect) {
              // Automatic reconnect is set so make sure comms is in resting state
              comms.setRestingState(true);
              reconnecting = true;
              startReconnectCycle();
            }
          }
        });

    // Insert our own callback to iterate through the URIs till the connect succeeds
    MqttToken userToken = new MqttToken(getClientId());
    ConnectActionListener connectActionListener =
        new ConnectActionListener(
            this, persistence, comms, options, userToken, userContext, callback, reconnecting);
    userToken.setActionCallback(connectActionListener);
    userToken.setUserContext(this);

    // If we are using the MqttCallbackExtended, set it on the connectActionListener
    if (this.mqttCallback instanceof MqttCallbackExtended) {
      connectActionListener.setMqttCallbackExtended((MqttCallbackExtended) this.mqttCallback);
    }

    comms.setNetworkModuleIndex(0);
    connectActionListener.connect();

    return userToken;
  }