/**
   * Makes sure that the response type is correct. The response type for a NON can be NON or CON.
   * The response type for a CON should either be an ACK with a piggy-backed response or, if an
   * empty ACK has already be sent, a CON or NON with a separate response.
   */
  @Override
  public void sendResponse(final Exchange exchange, final Response response) {

    LOGGER.finer("Send response, failed transmissions: " + exchange.getFailedTransmissionCount());

    // If a response type is set, we do not mess around with it.
    // Only if none is set, we have to decide for one here.

    Type respType = response.getType();
    if (respType == null) {
      Type reqType = exchange.getCurrentRequest().getType();
      if (reqType == Type.CON) {
        if (exchange.getCurrentRequest().isAcknowledged()) {
          // send separate response
          response.setType(Type.CON);
        } else {
          exchange.getCurrentRequest().setAcknowledged(true);
          // send piggy-backed response
          response.setType(Type.ACK);
          response.setMID(exchange.getCurrentRequest().getMID());
        }
      } else {
        // send NON response
        response.setType(Type.NON);
      }

      LOGGER.finest(
          "Switched response message type from "
              + respType
              + " to "
              + response.getType()
              + " (request was "
              + reqType
              + ")");

    } else if (respType == Type.ACK || respType == Type.RST) {
      response.setMID(exchange.getCurrentRequest().getMID());
    }

    if (response.getType() == Type.CON) {
      LOGGER.finer("Scheduling retransmission for " + response);
      prepareRetransmission(
          exchange,
          new RetransmissionTask(exchange, response) {
            public void retransmit() {
              sendResponse(exchange, response);
            }
          });
    }
    super.sendResponse(exchange, response);
  }
  /**
   * Computes the back-off timer and schedules the specified retransmission task.
   *
   * @param exchange the exchange
   * @param task the retransmission task
   */
  protected void prepareRetransmission(Exchange exchange, RetransmissionTask task) {
    /*
     * For a new confirmable message, the initial timeout is set to a
     * random number between ACK_TIMEOUT and (ACK_TIMEOUT *
     * ACK_RANDOM_FACTOR)
     */
    int timeout;
    if (exchange.getFailedTransmissionCount() == 0) {
      timeout = getRandomTimeout(ack_timeout, (int) (ack_timeout * ack_random_factor));
    } else {
      timeout = (int) (ack_timeout_scale * exchange.getCurrentTimeout());
    }
    exchange.setCurrentTimeout(timeout);

    ScheduledFuture<?> f = executor.schedule(task, timeout, TimeUnit.MILLISECONDS);
    exchange.setRetransmissionHandle(f);
  }
  /** Schedules a retransmission for confirmable messages. */
  @Override
  public void sendRequest(final Exchange exchange, final Request request) {

    LOGGER.finer("Send request, failed transmissions: " + exchange.getFailedTransmissionCount());

    if (request.getType() == null) request.setType(Type.CON);

    if (request.getType() == Type.CON) {
      prepareRetransmission(
          exchange,
          new RetransmissionTask(exchange, request) {
            public void retransmit() {
              sendRequest(exchange, request);
            }
          });
    }
    super.sendRequest(exchange, request);
  }
    @Override
    public void run() {
      /*
       * Do not retransmit a message if it has been acknowledged,
       * rejected, canceled or already been retransmitted for the maximum
       * number of times.
       */
      try {
        int failedCount = exchange.getFailedTransmissionCount() + 1;
        exchange.setFailedTransmissionCount(failedCount);

        if (message.isAcknowledged()) {
          LOGGER.finest(
              "Timeout: message already acknowledged, cancel retransmission of " + message);
          return;

        } else if (message.isRejected()) {
          LOGGER.finest("Timeout: message already rejected, cancel retransmission of " + message);
          return;

        } else if (message.isCanceled()) {
          LOGGER.finest("Timeout: canceled (MID=" + message.getMID() + "), do not retransmit");
          return;

        } else if (failedCount <= max_retransmit) {
          LOGGER.finer(
              "Timeout: retransmit message, failed: " + failedCount + ", message: " + message);

          // Trigger MessageObservers
          message.retransmitting();

          // MessageObserver might have canceled
          if (!message.isCanceled()) retransmit();

        } else {
          LOGGER.info(
              "Timeout: retransmission limit reached, exchange failed, message: " + message);
          exchange.setTimedOut();
          message.setTimedOut(true);
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }