/**
   * Return a formatted message to the client. We try to re-connect with the peer on the other end
   * if possible.
   *
   * @param sipMessage Message to send.
   * @throws IOException If there is an error sending the message
   */
  public void sendMessage(SIPMessage sipMessage) throws IOException {
    byte[] msg = sipMessage.encodeAsBytes();

    long time = System.currentTimeMillis();

    this.sendMessage(msg, sipMessage instanceof SIPRequest);

    if (this.stack.serverLog.needsLogging(ServerLog.TRACE_MESSAGES))
      logMessage(sipMessage, peerAddress, peerPort, time);
  }
  /**
   * Return a reply from a pre-constructed reply. This sends the message back to the entity who
   * caused us to create this channel in the first place.
   *
   * @param sipMessage Message string to send.
   * @throws IOException If there is a problem with sending the message.
   */
  public void sendMessage(SIPMessage sipMessage) throws IOException {
    if (sipStack.isLoggingEnabled() && this.sipStack.isLogStackTraceOnMessageSend()) {
      if (sipMessage instanceof SIPRequest && ((SIPRequest) sipMessage).getRequestLine() != null) {
        /*
         * We dont want to log empty trace messages.
         */
        this.sipStack.getStackLogger().logStackTrace(StackLogger.TRACE_INFO);
      } else {
        this.sipStack.getStackLogger().logStackTrace(StackLogger.TRACE_INFO);
      }
    }

    // Test and see where we are going to send the messsage. If the message
    // is sent back to oursleves, just
    // shortcircuit processing.
    long time = System.currentTimeMillis();
    try {
      for (MessageProcessor messageProcessor : sipStack.getMessageProcessors()) {
        if (messageProcessor.getIpAddress().equals(this.peerAddress)
            && messageProcessor.getPort() == this.peerPort
            && messageProcessor.getTransport().equals(this.peerProtocol)) {
          MessageChannel messageChannel =
              messageProcessor.createMessageChannel(this.peerAddress, this.peerPort);
          if (messageChannel instanceof RawMessageChannel) {
            ((RawMessageChannel) messageChannel).processMessage(sipMessage);
            if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG))
              sipStack.getStackLogger().logDebug("Self routing message");
            return;
          }
        }
      }

      byte[] msg = sipMessage.encodeAsBytes(this.getTransport());

      sendMessage(msg, peerAddress, peerPort, peerProtocol, sipMessage instanceof SIPRequest);

    } catch (IOException ex) {
      throw ex;
    } catch (Exception ex) {
      sipStack.getStackLogger().logError("An exception occured while sending message", ex);
      throw new IOException("An exception occured while sending message");
    } finally {
      if (sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_MESSAGES)
          && !sipMessage.isNullRequest()) logMessage(sipMessage, peerAddress, peerPort, time);
      else if (sipStack.getStackLogger().isLoggingEnabled(ServerLogger.TRACE_DEBUG))
        sipStack.getStackLogger().logDebug("Sent EMPTY Message");
    }
  }
  /**
   * Return a formatted message to the client. We try to re-connect with the peer on the other end
   * if possible.
   *
   * @param sipMessage Message to send.
   * @throws IOException If there is an error sending the message
   */
  public void sendMessage(final SIPMessage sipMessage) throws IOException {

    if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG) && !sipMessage.isNullRequest()) {
      logger.logDebug(
          "sendMessage:: "
              + sipMessage.getFirstLine()
              + " cseq method = "
              + sipMessage.getCSeq().getMethod());
    }

    for (MessageProcessor messageProcessor : getSIPStack().getMessageProcessors()) {
      if (messageProcessor.getIpAddress().getHostAddress().equals(this.getPeerAddress())
          && messageProcessor.getPort() == this.getPeerPort()
          && messageProcessor.getTransport().equalsIgnoreCase(this.getPeerProtocol())) {
        Runnable processMessageTask =
            new Runnable() {

              public void run() {
                try {
                  processMessage((SIPMessage) sipMessage.clone());
                } catch (Exception ex) {
                  if (logger.isLoggingEnabled(ServerLogger.TRACE_ERROR)) {
                    logger.logError("Error self routing message cause by: ", ex);
                  }
                }
              }
            };
        getSIPStack().getSelfRoutingThreadpoolExecutor().execute(processMessageTask);

        if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) logger.logDebug("Self routing message");
        return;
      }
    }

    byte[] msg = sipMessage.encodeAsBytes(this.getTransport());

    long time = System.currentTimeMillis();

    // need to store the peerPortAdvertisedInHeaders in case the response has an rport (ephemeral)
    // that failed to retry on the regular via port
    // for responses, no need to store anything for subsequent requests.
    if (peerPortAdvertisedInHeaders <= 0) {
      if (sipMessage instanceof SIPResponse) {
        SIPResponse sipResponse = (SIPResponse) sipMessage;
        Via via = sipResponse.getTopmostVia();
        if (via.getRPort() > 0) {
          if (via.getPort() <= 0) {
            // if port is 0 we assume the default port for TCP
            this.peerPortAdvertisedInHeaders = 5060;
          } else {
            this.peerPortAdvertisedInHeaders = via.getPort();
          }
          if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) {
            logger.logDebug(
                "1.Storing peerPortAdvertisedInHeaders = "
                    + peerPortAdvertisedInHeaders
                    + " for via port = "
                    + via.getPort()
                    + " via rport = "
                    + via.getRPort()
                    + " and peer port = "
                    + peerPort
                    + " for this channel "
                    + this
                    + " key "
                    + key);
          }
        }
      }
    }

    // JvB: also retry for responses, if the connection is gone we should
    // try to reconnect
    this.sendMessage(msg, sipMessage instanceof SIPRequest);

    // message was sent without any exception so let's set set port and
    // address before we feed it to the logger
    sipMessage.setRemoteAddress(this.peerAddress);
    sipMessage.setRemotePort(this.peerPort);
    sipMessage.setLocalAddress(this.getMessageProcessor().getIpAddress());
    sipMessage.setLocalPort(this.getPort());

    if (logger.isLoggingEnabled(ServerLogger.TRACE_MESSAGES))
      logMessage(sipMessage, peerAddress, peerPort, time);
  }