/**
  * Exception processor for exceptions detected from the application.
  *
  * @param ex The exception that was generated.
  */
 public void handleException(SIPServerException ex) {
   // Return a parse error message to the client on the other end
   // if he is still alive.
   int rc = ex.getRC();
   String msgString = ex.getMessage();
   if (rc != 0) {
     // Do we have a valid Return code ? --
     // in this case format the message.
     Request request = (Request) ex.getSIPMessage();
     Response response = request.createResponse(rc);
     try {
       sendMessage(response);
     } catch (IOException ioex) {
       if (LogWriter.needsLogging) LogWriter.logException(ioex);
     }
   } else {
     // Otherwise, message is already formatted --
     // just return it
     try {
       sendMessage(msgString.getBytes(), false);
     } catch (IOException ioex) {
       if (LogWriter.needsLogging) LogWriter.logException(ioex);
     }
   }
 }
  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();
    }
  }
  /** Run method specified by runnable. */
  public void run() {

    while (true) {
      // Create a new string message parser to parse the list of messages.
      if (myParser == null) {
        myParser = new StringMsgParser();
        myParser.setParseExceptionListener(this);
      }

      // messages that we write out to the peer.
      Datagram packet;

      if (stack.threadPoolSize != -1) {
        synchronized (((UDPMessageProcessor) messageProcessor).messageQueue) {
          while (((UDPMessageProcessor) messageProcessor).messageQueue.isEmpty()) {

            // Check to see if we need to exit.
            if (!((UDPMessageProcessor) messageProcessor).running) return;

            try {
              // Wait for packets
              ((UDPMessageProcessor) messageProcessor).messageQueue.wait();
            } catch (InterruptedException ex) {
              if (!((UDPMessageProcessor) messageProcessor).running) return;
            }
          }
          packet = (Datagram) ((UDPMessageProcessor) messageProcessor).messageQueue.firstElement();
          ((UDPMessageProcessor) messageProcessor).messageQueue.removeElementAt(0);
        }
        incomingPacket = packet;
      } else {
        packet = incomingPacket;
      }

      if (LogWriter.needsLogging) {
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG, "Processing new incoming datagram " + packet.getLength());
      }
      // Process the packet. Catch and log any exception we may throw.
      try {
        processIncomingDataPacket(packet);
      } catch (Throwable e) {
        if (LogWriter.needsLogging) {
          LogWriter.logMessage(
              LogWriter.TRACE_EXCEPTION, "Exception processing incoming UDP packet");
          LogWriter.logException((Exception) e);
        }
      }

      if (stack.threadPoolSize == -1) {
        return;
      }
    }
  }
  /**
   * Exception processor for exceptions detected from the parser. (This is invoked by the parser
   * when an error is detected).
   *
   * @param sipMessage -- the message that incurred the error.
   * @param ex -- parse exception detected by the parser.
   * @param header -- header that caused the error.
   * @throws ParseException Thrown if we want to reject the message.
   */
  public void handleException(
      ParseException ex, Message sipMessage, Class hdrClass, String header, String message)
      throws ParseException {
    if (LogWriter.needsLogging) LogWriter.logException(ex);
    // Log the bad message for later reference.

    if (hdrClass.equals(FromHeader.clazz)
        || hdrClass.equals(ToHeader.clazz)
        || hdrClass.equals(CSeqHeader.clazz)
        || hdrClass.equals(ViaHeader.clazz)
        || hdrClass.equals(CallIdHeader.clazz)
        || hdrClass.equals(RequestLine.clazz)
        || hdrClass.equals(StatusLine.clazz)) {
      stack.logBadMessage(message);
      throw ex;
    } else {
      sipMessage.addUnparsed(header);
    }
  }
  /**
   * Process an incoming datagram
   *
   * @param packet is the incoming datagram packet.
   */
  private void processIncomingDataPacket(Datagram packet) throws Exception {

    // For a request first via header tells where the message
    // is coming from.
    // For response, just get the port from the packet.
    // format: address:port
    String address = packet.getAddress();
    try {
      int firstColon = address.indexOf("//");
      int secondColon = address.indexOf(":", firstColon + 1);
      this.peerAddress = address.substring(firstColon + 2, secondColon);
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG, "UDPMessageChannel, run(), sender address:" + peerAddress);
      String senderPortString = address.substring(address.indexOf(";") + 1, address.indexOf("|"));
      this.peerPacketSourcePort = Integer.parseInt(senderPortString);
      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_DEBUG, "UDPMessageChannel, run(), sender port:" + peerPacketSourcePort);
    } catch (NumberFormatException e) {

      if (LogWriter.needsLogging)
        LogWriter.logMessage(
            LogWriter.TRACE_EXCEPTION,
            "UDPMessageChannel, run(), exception raised: " + e.getMessage());
      e.printStackTrace();
      peerPacketSourcePort = -1;
    }

    int packetLength = packet.getLength();
    // Read bytes and put it in a queue.
    byte[] msgBytes = packet.getData();

    // Do debug logging.
    if (LogWriter.needsLogging) {
      LogWriter.logMessage(
          LogWriter.TRACE_DEBUG,
          "UDPMessageChannel: processIncomingDataPacket : peerAddress = "
              + peerAddress
              + "/"
              + peerPacketSourcePort
              + " Length = "
              + packetLength
              + " msgBytes "
              + msgBytes);
    }

    Message sipMessage = null;
    try {
      receptionTime = System.currentTimeMillis();
      sipMessage = myParser.parseSIPMessage(msgBytes);
      myParser = null;
    } catch (ParseException ex) {
      myParser = null; // let go of the parser reference.
      if (LogWriter.needsLogging) {
        LogWriter.logMessage(LogWriter.TRACE_DEBUG, "Rejecting message !  " + new String(msgBytes));
        LogWriter.logMessage(LogWriter.TRACE_DEBUG, "error message " + ex.getMessage());
        LogWriter.logException(ex);
      }

      // TODO: do this on TCP too
      // JvB: send a 400 response for requests (except ACK)
      String msgString = new String(msgBytes, 0, packetLength);
      if (!msgString.startsWith("SIP/") && !msgString.startsWith("ACK ")) {

        String badReqRes = create400Response(msgString, ex);
        if (badReqRes != null) {
          if (LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_DEBUG, "Sending automatic 400 Bad Request: " + badReqRes);
          try {
            this.sendMessage(badReqRes.getBytes(), peerAddress, peerPacketSourcePort, "UDP", false);
          } catch (IOException e) {
            LogWriter.logException(e);
          }
        } else {
          if (LogWriter.needsLogging)
            LogWriter.logMessage(
                LogWriter.TRACE_DEBUG, "Could not formulate automatic 400 Bad Request");
        }
      }

      return;
    }
    // No parse exception but null message - reject it and
    // march on (or return).
    // exit this message processor if the message did not parse.
    if (sipMessage == null) {
      if (LogWriter.needsLogging)
        LogWriter.logMessage(LogWriter.TRACE_DEBUG, "Rejecting message !  + Null message parsed.");
      return;
    }

    ViaList viaList = sipMessage.getViaHeaders();

    // Check for the required headers.
    if (sipMessage.getFromHeader() == null
        ||
        // sipMessage.getFromHeader().getTag() == null  ||
        sipMessage.getTo() == null
        || sipMessage.getCallId() == null
        || sipMessage.getCSeqHeader() == null
        || sipMessage.getViaHeaders() == null) {
      String badmsg = new String(msgBytes);
      if (LogWriter.needsLogging) {
        LogWriter.logMessage("bad message " + badmsg);
        LogWriter.logMessage(
            ">>> Dropped Bad Msg "
                + "FromHeader = "
                + sipMessage.getFromHeader()
                + "ToHeader = "
                + sipMessage.getTo()
                + "CallId = "
                + sipMessage.getCallId()
                + "CSeqHeader = "
                + sipMessage.getCSeqHeader()
                + "Via = "
                + sipMessage.getViaHeaders());
      }

      stack.logBadMessage(badmsg);
      return;
    }

    // For a request first via header tells where the message
    // is coming from.
    // For response, just get the port from the packet.
    if (sipMessage instanceof Request) {

      ViaHeader v = (ViaHeader) viaList.first();
      if (v.hasPort()) this.peerPort = v.getPort();
      else this.peerPort = SIPMessageStack.DEFAULT_PORT;

      this.peerProtocol = v.getTransport();

      boolean hasRPort = v.hasParameter(ViaHeader.RPORT);
      // 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 (hasRPort || !this.peerAddress.equals(v.getHost())) {
        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 (hasRPort) {
        v.setParameter(ViaHeader.RPORT, Integer.toString(peerPacketSourcePort));
        this.peerPort = peerPacketSourcePort;
      }

    } else {
      this.peerPort = this.peerPacketSourcePort;
      this.peerProtocol = ((ViaHeader) viaList.getFirst()).getTransport();
    }

    processMessage(sipMessage);
  }