public void closeFromParser() {
    try {
      if (LogWriter.needsLogging)
        LogWriter.logMessage("Closing message Channel " + this + " from Parser");

      if (mySock != null) {
        try {
          String addr = mySock.getAddress();
          int port = mySock.getPort();
          stack.ioHandler.removeAndCloseSocket(stack.ioHandler.makeKey(addr, port));
        } catch (IOException e) {
          if (LogWriter.needsLogging) LogWriter.logMessage("Socket was already closed for " + this);
        }
        mySock = null;
      } else {
        if (LogWriter.needsLogging) LogWriter.logMessage("Socket was already null for " + this);
      }

    } catch (Exception ex) {
      if (LogWriter.needsLogging && !stack.toExit) {
        LogWriter.logMessage(
            LogWriter.TRACE_EXCEPTION, "Exception closing message Channel " + this);
        LogWriter.logException(ex);
      }
    } finally {
      this.isRunning = false;
      uncache();
      this.tcpMessageProcessor.useCount--;
      if (LogWriter.needsLogging)
        LogWriter.logMessage("TCP Message Processor use count: " + tcpMessageProcessor.useCount);
    }
  }
  /** Close the message channel. */
  public void close() {
    try {
      if (LogWriter.needsLogging) LogWriter.logMessage("Closing message Channel " + this);

      if (mySock != null) {
        try {
          String addr = mySock.getAddress();
          int port = mySock.getPort();
          stack.ioHandler.removeAndCloseSocket(stack.ioHandler.makeKey(addr, port));
        } catch (IOException e) {
          if (LogWriter.needsLogging) LogWriter.logMessage("Socket was already closed for " + this);
        }
        mySock = null;
      } else {
        if (LogWriter.needsLogging) LogWriter.logMessage("Socket was already null for " + this);
      }

    } catch (Exception ex) {
      if (LogWriter.needsLogging) {
        LogWriter.logMessage("Exception closing message Channel " + this);
        LogWriter.logException(ex);
      }
    } finally {
      this.isRunning = false;
      uncache();
    }
  }
  /**
   * Constructor - gets called from the SIPMessageStack class with a socket on accepting a new
   * client. All the processing of the message is done here with the stack being freed up to handle
   * new connections. The sock input is the socket that is returned from the accept. Global data
   * that is shared by all threads is accessible in the Server structure.
   *
   * @param sock Socket from which to read and write messages. The socket is already connected (was
   *     created as a result of an accept).
   * @param sipStack the SIP Stack
   * @param channelNotifier Notifier (optional) that gets called when the channel is opened or
   *     closed.
   */
  protected TCPMessageChannel(
      SocketConnection sock, SIPMessageStack sipStack, TCPMessageProcessor msgProcessor)
      throws IOException {

    stack = sipStack;
    mySock = sock;
    myAddress = sipStack.getHostAddress();
    peerAddress = sock.getAddress();
    myClientInputStream = sock.openInputStream();
    myClientOutputStream = sock.openOutputStream();
    if (LogWriter.needsLogging) {
      LogWriter.logMessage("Creating new TCPMessageChannel " + this);
      LogWriter.logMessage(
          LogWriter.TRACE_DEBUG,
          "Channel parameters: "
              + "stack: "
              + stack
              + "\n"
              + "processor: "
              + msgProcessor
              + "\n"
              + "localAddress: "
              + myAddress
              + "\n"
              + "peerAddress: "
              + peerAddress
              + "\n"
              + "IS "
              + myClientInputStream
              + " Socket "
              + mySock);
    }
    stack.ioHandler.putSocket(
        stack.ioHandler.makeKey(mySock.getAddress(), mySock.getPort()),
        mySock,
        myClientOutputStream,
        myClientInputStream);

    mythread = new Thread(this, "TCPMessageChannel - incoming connection");

    this.tcpMessageProcessor = msgProcessor;
    this.myPort = this.tcpMessageProcessor.getPort();
    // Bug report by Vishwashanti Raj Kadiayl
    super.messageProcessor = msgProcessor;
    mythread.start();
  }
  /**
   * Gets invoked by the parser as a callback on successful message parsing (i.e. no parser errors).
   *
   * @param sipMessage Mesage to process (this calls the application for processing the message).
   */
  public void processMessage(Message sipMessage) {

    if (!stack.isAlive()) {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG,
            "MsgChannel " + this + " is dropping message as the stack is closing");
      return; // drop messages when closing, avoid Exceptions
    }

    try {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            "[TCPMessageChannel]-> Processing incoming message: " + sipMessage.getFirstLine());
      if (sipMessage.getFromHeader() == null
          || sipMessage.getTo() == null
          || sipMessage.getCallId() == null
          || sipMessage.getCSeqHeader() == null
          || sipMessage.getViaHeaders() == null) {
        String badmsg = sipMessage.encode();
        if (LogWriter.needsLogging) {
          ServerLog.logMessage("bad message " + badmsg);
          ServerLog.logMessage(">>> Dropped Bad Msg");
        }
        stack.logBadMessage(badmsg);
        return;
      }

      ViaList viaList = sipMessage.getViaHeaders();
      // For a request
      // first via header tells where the message is coming from.
      // For response, this has already been recorded in the outgoing
      // message.
      if (sipMessage instanceof Request) {
        ViaHeader v = (ViaHeader) viaList.first();
        if (v.hasPort()) {
          viaPort = v.getPort();
        } else {
          viaPort = SIPMessageStack.DEFAULT_PORT;
        }
        this.peerProtocol = v.getTransport();
        try {
          if (peerPort == -1) peerPort = mySock.getPort();
          this.peerAddress = mySock.getAddress();

          // Log this because it happens when the remote host identifies
          // as a FQDN but this is not resolvable to an IP by the OS.
          // S40 doesn't have DNS settings, for instance, so if the APN
          // is not able to resolve all the addresses of the SIP/IMS core,
          // this problem will appear.
          if (peerAddress == null && LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_EXCEPTION, "WARNING! Socket.getAddress() returned 'null'!!!");

          // Be warned, the host comparison may fail if socket.getAddress()
          // returns a domain name as the Via Host will be a numeric IP.
          // FIXME: No idea. Doing a DNS lookup or reverse DNS lookup
          // can be misleading because they can be non-matching, that is,
          // DNS(peerAddressName) != ReverseDNS(peerAddressIP)
          if (v.hasParameter(ViaHeader.RPORT) || !v.getHost().equals(this.peerAddress)) {
            if (LogWriter.needsLogging)
              LogWriter.logMessage(
                  LogWriter.TRACE_MESSAGES,
                  "WARNING! \"Received\" parameter "
                      + "has been temporarily disabled. Response will be sent to topmost Via Host: "
                      + v.getHost());
            this.peerAddress = v.getHost();
            //		                if (LogWriter.needsLogging)
            //			                   LogWriter.logMessage(LogWriter.TRACE_MESSAGES, "Adding
            // \"received\" parameter" +
            //			                   		" to incoming request with value: " + peerAddress +
            //			                   		" because it doesn't match the Via host " + v.getHost());
            //						v.setParameter(ViaHeader.RECEIVED, this.peerAddress);

          }

          if (v.hasParameter(ViaHeader.RPORT))
            v.setParameter(ViaHeader.RPORT, Integer.toString(this.peerPort));

          /*
           * If socket is invalid, close it because it is useless and dangerous.
           * Also if we ran out of slots for new sockets, as this could prevent
           * incoming connections from being accepted.
           */
          if (mySock.getAddress() == null
              || (stack.maxConnections != -1
                  && tcpMessageProcessor.getNumConnections() >= stack.maxConnections)) {
            stack.ioHandler.disposeSocket(mySock, myClientInputStream, myClientOutputStream);
            mySock = null;
            myClientInputStream = null;
            myClientOutputStream = null;
            if (stack.maxConnections != -1) {
              synchronized (tcpMessageProcessor) {
                tcpMessageProcessor.decreaseNumConnections();
                tcpMessageProcessor.notify();
              }
            }
          }
          // reuse socket even for outgoing requests
          else if (!this.isCached) {
            ((TCPMessageProcessor) this.messageProcessor).cacheMessageChannel(this);
            String key = "TCP" + ":" + stack.ioHandler.makeKey(peerAddress, peerPort);
            stack.ioHandler.putSocket(key, mySock, myClientOutputStream, myClientInputStream);
          }

        } catch (IOException e) {
          e.printStackTrace();
        }
      }

      // System.out.println("receiver address = " + receiverAddress);

      // For each part of the request header, fetch it and process it
      long receptionTime = System.currentTimeMillis();

      if (sipMessage instanceof Request) {
        // This is a request - process the request.
        Request sipRequest = (Request) sipMessage;
        // Create a new sever side request processor for this
        // message and let it handle the rest.

        if (LogWriter.needsLogging) {
          LogWriter.logMessage("----Processing Message---");
        }

        // TODO: check maximum size of request

        SIPServerRequestInterface sipServerRequest = stack.newSIPServerRequest(sipRequest, this);

        if (sipServerRequest != null) {
          try {
            sipServerRequest.processRequest(sipRequest, this);

            ServerLog.logMessage(
                sipMessage,
                sipRequest.getViaHost() + ":" + sipRequest.getViaPort(),
                stack.getHostAddress() + ":" + stack.getPort(this.getTransport()),
                false,
                receptionTime);
          } catch (SIPServerException ex) {
            ServerLog.logMessage(
                sipMessage,
                sipRequest.getViaHost() + ":" + sipRequest.getViaPort(),
                stack.getHostAddress() + ":" + stack.getPort(this.getTransport()),
                ex.getMessage(),
                false,
                receptionTime);
            handleException(ex);
          }

        } else {
          if (LogWriter.needsLogging)
            LogWriter.logMessage("Dropping request -- null sipServerRequest");
        }

      } else {
        // This is a response message - process it.
        Response sipResponse = (Response) sipMessage;

        // TODO: check maximum size of the response

        SIPServerResponseInterface sipServerResponse =
            stack.newSIPServerResponse(sipResponse, this);

        if (LogWriter.needsLogging)
          LogWriter.logMessage("got a response interface " + sipServerResponse);

        try {
          // Responses with no ClienTransaction associated will not be processed
          // as they may cause a NPE in the EventScanner thread.
          if (sipServerResponse != null) sipServerResponse.processResponse(sipResponse, this);
          else {
            if (LogWriter.needsLogging) {
              LogWriter.logMessage("null sipServerResponse!");
            }
          }
        } catch (SIPServerException ex) {
          // An error occured processing the message -- just log it.
          ServerLog.logMessage(
              sipMessage,
              getPeerAddress().toString() + ":" + getPeerPort(),
              stack.getHostAddress() + ":" + stack.getPort(this.getTransport()),
              ex.getMessage(),
              false,
              receptionTime);
          // Ignore errors while processing responses??
        }
      }
    } catch (Exception ee) {
      if (stack.isAlive()) {
        throw new RuntimeException(ee.getClass() + ":" + ee.getMessage());
      }
      // else ignore exceptions
    } finally {
      //            this.tcpMessageProcessor.useCount --;
    }
  }
  /**
   * Send message to whoever is connected to us. Uses the topmost via address to send to.
   *
   * @param message is the message to send.
   */
  private void sendMessage(byte[] msg, boolean retry) throws IOException {

    int portToUse;
    if (viaPort != -1) portToUse = viaPort;
    else portToUse = peerPort;

    if (!this.isRunning && !this.isCached) {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_MESSAGES,
            "Tried to send message through a Message Channel that is no longer running. Create a new one.");

      TCPMessageChannel newChannel =
          (TCPMessageChannel)
              this.tcpMessageProcessor.createMessageChannel(this.peerAddress, portToUse);
      newChannel.sendMessage(msg, retry);
      return;
    }

    SocketConnection sock =
        stack.ioHandler.sendBytes(this.peerAddress, portToUse, this.peerProtocol, msg, retry);

    /*Fix: don't replace the original socket (where the incoming
    request came from) with the one created to send the response
    to the port specified by the VIA header. This would cause a
    NPE later, as the 'via socket' will be closed shortly after
    sending the response. Thanks to Janos Vig (Genaker) for
    detecting this. */
    if (sock != null
        && (mySock == null || (sock != mySock && sock.getPort() == mySock.getPort()))) {
      try {
        if (mySock != null) {
          if (LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_MESSAGES,
                "Closing socket on TCPMessageChannel and replacing with new one");

          // Closing the IO streams will also stop the parser reading from them
          if (myClientOutputStream != null) myClientOutputStream.close();
          if (myClientInputStream != null) myClientInputStream.close();
          mySock.close();

        } else {
          if (LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_DEBUG, "TCP Msg channel " + this + " socket was null!");
        }
      } catch (IOException ex) {
        ex.printStackTrace();
      }

      // replace the old references of socket and IO streams
      // to the ones pointing to the newly created socket.
      mySock = sock;
      myClientOutputStream = stack.ioHandler.getSocketOutputStream(mySock);
      myClientInputStream = stack.ioHandler.getSocketInputStream(mySock);

      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG,
            "Creating new Thread from Message Channel "
                + this
                + " and new socket "
                + sock
                + " IS "
                + myClientInputStream);
      Thread thread = new Thread(this);
      thread.start();
    }
  }