/**
   * 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();
  }
 /**
  * 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;
   }
 }
  /**
   * 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;
    }
  }
 public void putPending(PendingRecord pendingRecord) {
   synchronized (pendingRecords) {
     pendingRecords.add(pendingRecord);
   }
 }
  /**
   * Handles a new SIP request. It finds a server transaction to handle this message. If none
   * exists, it creates a new transaction.
   *
   * @param requestReceived Request to handle.
   * @param requestMessageChannel Channel that received message.
   * @return A server transaction.
   */
  protected ServerRequestInterface newSIPServerRequest(
      SIPRequest requestReceived, MessageChannel requestMessageChannel) {

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

    String key = requestReceived.getTransactionId();

    currentTransaction = (SIPServerTransaction) serverTransactionTable.get(key);
    if (currentTransaction == null
        || !currentTransaction.isMessagePartOfTransaction(requestReceived)) {

      // Loop through all server transactions
      synchronized (serverTransactions) {
        transactionIterator = serverTransactions.iterator();
        currentTransaction = null;
        while (transactionIterator.hasNext() && currentTransaction == null) {

          nextTransaction = (SIPServerTransaction) transactionIterator.next();

          // If this transaction should handle this request,
          if (nextTransaction.isMessagePartOfTransaction(requestReceived)) {
            // Mark this transaction as the one
            // to handle this message
            currentTransaction = nextTransaction;
          }
        }

        // If no transaction exists to handle this message
        if (currentTransaction == null) {
          currentTransaction = findPendingTransaction(requestReceived);
          if (currentTransaction != null) return currentTransaction;
          currentTransaction = createServerTransaction(requestMessageChannel);
          currentTransaction.setOriginalRequest(requestReceived);
          if (!isDialogCreated(requestReceived.getMethod())) {
            // Dialog is not created - can we find the state?
            // If so, then create a transaction and add it.
            String dialogId = requestReceived.getDialogId(true);
            SIPDialog dialog = getDialog(dialogId);
            // Sequence numbers are supposed to increment.
            // avoid processing old sequence numbers and
            // delivering the same request up to the
            // application if the request has already been seen.
            // Special handling applies to ACK processing.
            if (dialog != null
                && (requestReceived.getMethod().equals(Request.ACK)
                    || requestReceived.getCSeq().getSequenceNumber()
                        > dialog.getRemoteSequenceNumber())) {
              // Found a dialog.
              if (LogWriter.needsLogging)
                logWriter.logMessage("adding server transaction " + currentTransaction);
              serverTransactions.add(0, currentTransaction);
              addTransactionHash(currentTransaction);
              currentTransaction.startTransactionTimer();
              currentTransaction.isMapped = true;
            }
          } else {
            // Create the transaction but dont map it.
            String dialogId = requestReceived.getDialogId(true);
            SIPDialog dialog = getDialog(dialogId);
            // This is a dialog creating request that is part of an
            // existing dialog (eg. re-Invite). Re-invites get a non
            // null server transaction Id (unlike the original
            // invite).
            if (dialog != null
                && requestReceived.getCSeq().getSequenceNumber()
                    > dialog.getRemoteSequenceNumber()) {
              currentTransaction.map();
              if (LogWriter.needsLogging)
                logWriter.logMessage("adding server transaction " + currentTransaction);
              serverTransactions.add(0, currentTransaction);
              addTransactionHash(currentTransaction);
              currentTransaction.startTransactionTimer();
              currentTransaction.toListener = true;
            }
          }
        }
      }
    }

    // Set ths transaction's encapsulated request
    // interface from the superclass
    currentTransaction.setRequestInterface(
        super.newSIPServerRequest(requestReceived, currentTransaction));
    return currentTransaction;
  }