/* (non-Javadoc)
   * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
   */
  public IMqttToken disconnect(
      long quiesceTimeout, Object userContext, IMqttActionListener callback) throws MqttException {
    final String methodName = "disconnect";
    // @TRACE 104=> quiesceTimeout={0} userContext={1} callback={2}
    log.fine(
        CLASS_NAME,
        methodName,
        "104",
        new Object[] {new Long(quiesceTimeout), userContext, callback});

    MqttToken token = new MqttToken(getClientId());
    token.setActionCallback(callback);
    token.setUserContext(userContext);

    MqttDisconnect disconnect = new MqttDisconnect();
    try {
      comms.disconnect(disconnect, quiesceTimeout, token);
    } catch (MqttException ex) {
      // @TRACE 105=< exception
      log.fine(CLASS_NAME, methodName, "105", null, ex);
      throw ex;
    }
    // @TRACE 108=<
    log.fine(CLASS_NAME, methodName, "108");

    return token;
  }
 /* (non-Javadoc)
  * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close()
  */
 public void close() throws MqttException {
   final String methodName = "close";
   // @TRACE 113=<
   log.fine(CLASS_NAME, methodName, "113");
   comms.close();
   // @TRACE 114=>
   log.fine(CLASS_NAME, methodName, "114");
 }
  /* (non-Javadoc)
   * Check and send a ping if needed.
   * <p>By default, client sends PingReq to server to keep the connection to
   * server. For some platforms which cannot use this mechanism, such as Android,
   * developer needs to handle the ping request manually with this method.
   * </p>
   *
   * @throws MqttException for other errors encountered while publishing the message.
   */
  public IMqttToken checkPing(Object userContext, IMqttActionListener callback)
      throws MqttException {
    final String methodName = "ping";
    MqttToken token;
    // @TRACE 117=>
    log.fine(CLASS_NAME, methodName, "117");

    token = comms.checkForActivity();
    // @TRACE 118=<
    log.fine(CLASS_NAME, methodName, "118");

    return token;
  }
  /**
   * User triggered attempt to reconnect
   *
   * @throws MqttException
   */
  public void reconnect() throws MqttException {
    final String methodName = "reconnect";
    // @Trace 500=Attempting to reconnect client: {0}
    log.fine(CLASS_NAME, methodName, "500", new Object[] {this.clientId});
    // Some checks to make sure that we're not attempting to reconnect an already connected client
    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);
    }
    // We don't want to spam the server
    stopReconnectCycle();

    attemptReconnect();
  }
  /* (non-Javadoc)
   * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
   */
  public IMqttToken subscribe(
      String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback)
      throws MqttException {
    final String methodName = "subscribe";

    if (topicFilters.length != qos.length) {
      throw new IllegalArgumentException();
    }

    // remove any message handlers for individual topics
    for (int i = 0; i < topicFilters.length; ++i) {
      this.comms.removeMessageListener(topicFilters[i]);
    }

    String subs = "";
    for (int i = 0; i < topicFilters.length; i++) {
      if (i > 0) {
        subs += ", ";
      }
      subs += "topic=" + topicFilters[i] + " qos=" + qos[i];

      // Check if the topic filter is valid before subscribing
      MqttTopic.validate(topicFilters[i], true /*allow wildcards*/);
    }
    // @TRACE 106=Subscribe topicFilter={0} userContext={1} callback={2}
    log.fine(CLASS_NAME, methodName, "106", new Object[] {subs, userContext, callback});

    MqttToken token = new MqttToken(getClientId());
    token.setActionCallback(callback);
    token.setUserContext(userContext);
    token.internalTok.setTopics(topicFilters);

    MqttSubscribe register = new MqttSubscribe(topicFilters, qos);

    comms.sendNoWait(register, token);
    // @TRACE 109=<
    log.fine(CLASS_NAME, methodName, "109");

    return token;
  }
  /* (non-Javadoc)
   * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
   */
  public IMqttToken unsubscribe(
      String[] topicFilters, Object userContext, IMqttActionListener callback)
      throws MqttException {
    final String methodName = "unsubscribe";
    String subs = "";
    for (int i = 0; i < topicFilters.length; i++) {
      if (i > 0) {
        subs += ", ";
      }
      subs += topicFilters[i];

      // Check if the topic filter is valid before unsubscribing
      // Although we already checked when subscribing, but invalid
      // topic filter is meanless for unsubscribing, just prohibit it
      // to reduce unnecessary control packet send to broker.
      MqttTopic.validate(topicFilters[i], true /*allow wildcards*/);
    }

    // @TRACE 107=Unsubscribe topic={0} userContext={1} callback={2}
    log.fine(CLASS_NAME, methodName, "107", new Object[] {subs, userContext, callback});

    // remove message handlers from the list for this client
    for (int i = 0; i < topicFilters.length; ++i) {
      this.comms.removeMessageListener(topicFilters[i]);
    }

    MqttToken token = new MqttToken(getClientId());
    token.setActionCallback(callback);
    token.setUserContext(userContext);
    token.internalTok.setTopics(topicFilters);

    MqttUnsubscribe unregister = new MqttUnsubscribe(topicFilters);

    comms.sendNoWait(unregister, token);
    // @TRACE 110=<
    log.fine(CLASS_NAME, methodName, "110");

    return token;
  }
  /* (non-Javadoc)
   * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener)
   */
  public IMqttDeliveryToken publish(
      String topic, MqttMessage message, Object userContext, IMqttActionListener callback)
      throws MqttException, MqttPersistenceException {
    final String methodName = "publish";
    // @TRACE 111=< topic={0} message={1}userContext={1} callback={2}
    log.fine(CLASS_NAME, methodName, "111", new Object[] {topic, userContext, callback});

    // Checks if a topic is valid when publishing a message.
    MqttTopic.validate(topic, false /*wildcards NOT allowed*/);

    MqttDeliveryToken token = new MqttDeliveryToken(getClientId());
    token.setActionCallback(callback);
    token.setUserContext(userContext);
    token.setMessage(message);
    token.internalTok.setTopics(new String[] {topic});

    MqttPublish pubMsg = new MqttPublish(topic, message);
    comms.sendNoWait(pubMsg, token);

    // @TRACE 112=<
    log.fine(CLASS_NAME, methodName, "112");

    return token;
  }
 public void messageArrivedComplete(int messageId, int qos) throws MqttException {
   comms.messageArrivedComplete(messageId, qos);
 }
 /* (non-Javadoc)
  * @see IMqttAsyncClient#setManualAcks(manualAcks)
  */
 public void setManualAcks(boolean manualAcks) {
   comms.setManualAcks(manualAcks);
 }
 /* (non-Javadoc)
  * @see IMqttAsyncClient#setCallback(MqttCallback)
  */
 public void setCallback(MqttCallback callback) {
   this.mqttCallback = callback;
   comms.setCallback(callback);
 }
 /**
  * Returns the currently connected Server URI Implemented due to:
  * https://bugs.eclipse.org/bugs/show_bug.cgi?id=481097
  *
  * <p>Where getServerURI only returns the URI that was provided in MqttAsyncClient's constructor,
  * getCurrentServerURI returns the URI of the Server that the client is currently connected to.
  * This would be different in scenarios where multiple server URIs have been provided to the
  * MqttConnectOptions.
  *
  * @return the currently connected server URI
  */
 public String getCurrentServerURI() {
   return comms.getNetworkModules()[comms.getNetworkModuleIndex()].getServerURI();
 }
 /* (non-Javadoc)
  * @see IMqttAsyncClient#isConnected()
  */
 public boolean isConnected() {
   return comms.isConnected();
 }
 /*
  * (non-Javadoc)
  * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long, long)
  */
 public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException {
   comms.disconnectForcibly(quiesceTimeout, disconnectTimeout);
 }
  /* (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;
  }
 /* (non-Javadoc)
  * @see IMqttAsyncClient#getPendingDeliveryTokens()
  */
 public IMqttDeliveryToken[] getPendingDeliveryTokens() {
   return comms.getPendingDeliveryTokens();
 }