/**
  * Create a client transaction from a raw channel.
  *
  * @param transaction is the transport channel to encapsulate.
  */
 public MessageChannel createMessageChannel(SIPTransaction transaction) {
   synchronized (clientTransactions) {
     // New client transaction to return
     SIPTransaction returnChannel = createClientTransaction(transaction.getMessageChannel());
     clientTransactions.add(0, (SIPClientTransaction) returnChannel);
     ((SIPClientTransaction) returnChannel).setViaPort(transaction.getViaPort());
     ((SIPClientTransaction) returnChannel).setViaHost(transaction.getViaHost());
     // Add the transaction timer for the state machine.
     returnChannel.startTransactionTimer();
     return returnChannel;
   }
 }
  /**
   * Handles a new SIP response. It finds a client transaction to handle this message. If none
   * exists, it sends the message directly to the superclass.
   *
   * @param responseReceived Response to handle.
   * @param responseMessageChannel Channel that received message.
   * @return A client transaction.
   */
  protected ServerResponseInterface newSIPServerResponse(
      SIPResponse responseReceived, MessageChannel responseMessageChannel) {
    //	System.out.println("response = " + responseReceived.encode());

    // Iterator through all client transactions
    Iterator<SIPClientTransaction> transactionIterator;
    // Next transaction in the set
    SIPClientTransaction nextTransaction;
    // Transaction to handle this request
    SIPClientTransaction currentTransaction;

    String key = responseReceived.getTransactionId();

    currentTransaction = (SIPClientTransaction) clientTransactionTable.get(key);

    if (currentTransaction == null
        || !currentTransaction.isMessagePartOfTransaction(responseReceived)) {
      // Loop through all server transactions
      synchronized (clientTransactions) {
        transactionIterator = clientTransactions.iterator();
        currentTransaction = null;
        while (transactionIterator.hasNext() && currentTransaction == null) {

          nextTransaction = (SIPClientTransaction) transactionIterator.next();

          // If this transaction should handle this request,
          if (nextTransaction.isMessagePartOfTransaction(responseReceived)) {

            // Mark this transaction as the one to
            // handle this message
            currentTransaction = nextTransaction;
          }
        }
      }

      // If no transaction exists to handle this message,
      if (currentTransaction == null) {

        // Pass the message directly to the TU
        return super.newSIPServerResponse(responseReceived, responseMessageChannel);
      }
    }

    // Set ths transaction's encapsulated response interface
    // from the superclass
    currentTransaction.setResponseInterface(
        super.newSIPServerResponse(responseReceived, currentTransaction));
    return currentTransaction;
  }
  /**
   * Find the transaction corresponding to a given request.
   *
   * @param sipMessage request for which to retrieve the transaction.
   * @param isServer search the server transaction table if true.
   * @return the transaction object corresponding to the request or null if no such mapping exists.
   */
  public SIPTransaction findTransaction(SIPMessage sipMessage, boolean isServer) {
    SIPTransaction retval = null;

    if (isServer) {
      Via via = sipMessage.getTopmostVia();
      if (via.getBranch() != null) {
        String key = sipMessage.getTransactionId();

        synchronized (this.serverTransactionTable) {
          retval = (SIPTransaction) serverTransactionTable.get(key);
          if (LogWriter.needsLogging) logMessage("looking for key " + key);
          if (retval != null && retval.isMessagePartOfTransaction(sipMessage)) return retval;
        }
      }
      // Need to scan the table for old style transactions (RFC 2543
      // style)
      synchronized (this.serverTransactions) {
        Iterator<SIPServerTransaction> it = serverTransactions.iterator();
        while (it.hasNext()) {
          SIPServerTransaction sipServerTransaction = (SIPServerTransaction) it.next();
          if (sipServerTransaction.isMessagePartOfTransaction(sipMessage))
            return sipServerTransaction;
        }
      }
    } else {
      Via via = sipMessage.getTopmostVia();
      if (via.getBranch() != null) {
        String key = sipMessage.getTransactionId();
        synchronized (this.clientTransactionTable) {
          retval = (SIPTransaction) clientTransactionTable.get(key);
          if (retval != null && retval.isMessagePartOfTransaction(sipMessage)) return retval;
        }
      }
      // Need to scan the table for old style transactions (RFC 2543
      // style)
      synchronized (this.clientTransactions) {
        Iterator<SIPClientTransaction> it = clientTransactions.iterator();
        while (it.hasNext()) {
          SIPClientTransaction clientTransaction = (SIPClientTransaction) it.next();
          if (clientTransaction.isMessagePartOfTransaction(sipMessage)) return clientTransaction;
        }
      }
    }
    return null;
  }
  /**
   * Add a new client transaction to the set of existing transactions. Add it to the top of the list
   * so an incoming response has less work to do in order to find the transaction.
   *
   * @param clientTransaction -- client transaction to add to the set.
   */
  public void addTransaction(SIPClientTransaction clientTransaction) {
    if (LogWriter.needsLogging) logWriter.logMessage("added transaction " + clientTransaction);
    synchronized (clientTransactions) {
      clientTransactions.add(0, clientTransaction);
    }

    addTransactionHash(clientTransaction);
    clientTransaction.startTransactionTimer();
  }
  /**
   * Get the transaction to cancel. Search the server transaction table for a transaction that
   * matches the given transaction.
   */
  public SIPTransaction findCancelTransaction(SIPRequest cancelRequest, boolean isServer) {

    if (LogWriter.needsLogging) {
      logWriter.logMessage(
          "findCancelTransaction request= \n"
              + cancelRequest
              + "\nfindCancelRequest isServer="
              + isServer);
    }

    if (isServer) {
      synchronized (this.serverTransactions) {
        Iterator<SIPServerTransaction> li = this.serverTransactions.iterator();
        while (li.hasNext()) {
          SIPTransaction transaction = (SIPTransaction) li.next();
          // SIPRequest sipRequest = (SIPRequest) (transaction
          //		.getRequest());

          SIPServerTransaction sipServerTransaction = (SIPServerTransaction) transaction;
          if (sipServerTransaction.doesCancelMatchTransaction(cancelRequest))
            return sipServerTransaction;
        }
      }
    } else {
      synchronized (this.clientTransactions) {
        Iterator<SIPClientTransaction> li = this.clientTransactions.iterator();
        while (li.hasNext()) {
          SIPTransaction transaction = (SIPTransaction) li.next();
          //					SIPRequest sipRequest = (SIPRequest) (transaction
          //							.getRequest());

          SIPClientTransaction sipClientTransaction = (SIPClientTransaction) transaction;
          if (sipClientTransaction.doesCancelMatchTransaction(cancelRequest))
            return sipClientTransaction;
        }
      }
    }
    if (LogWriter.needsLogging)
      logWriter.logMessage("Could not find transaction for cancel request");
    return null;
  }
  /**
   * Creates a client transaction to handle a new request. Gets the real message channel from the
   * superclass, and then creates a new client transaction wrapped around this channel.
   *
   * @param nextHop Hop to create a channel to contact.
   */
  public MessageChannel createMessageChannel(int sourcePort, Hop nextHop)
      throws UnknownHostException {
    synchronized (clientTransactions) {
      // New client transaction to return
      SIPTransaction returnChannel;

      // Create a new client transaction around the
      // superclass' message channel
      MessageChannel mc = super.createMessageChannel(sourcePort, nextHop);

      // Superclass will return null if no message processor
      // available for the transport.
      if (mc == null) return null;

      returnChannel = createClientTransaction(mc);
      clientTransactions.add(0, (SIPClientTransaction) returnChannel);
      ((SIPClientTransaction) returnChannel).setViaPort(nextHop.getPort());
      ((SIPClientTransaction) returnChannel).setViaHost(nextHop.getHost());
      // Add the transaction timer for the state machine.
      returnChannel.startTransactionTimer();
      return returnChannel;
    }
  }