Package gov.nist.javax.sip.stack

Source Code of gov.nist.javax.sip.stack.SIPTransaction$LingerTimer

/*
* Conditions Of Use
*
* This software was developed by employees of the National Institute of
* Standards and Technology (NIST), an agency of the Federal Government.
* Pursuant to title 15 Untied States Code Section 105, works of NIST
* employees are not subject to copyright protection in the United States
* and are considered to be in the public domain.  As a result, a formal
* license is not needed to use the software.
*
* This software is provided by NIST as a service and is expressly
* provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
* OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY.  NIST does not warrant or make any representations
* regarding the use of the software or the results thereof, including but
* not limited to the correctness, accuracy, reliability or usefulness of
* the software.
*
* Permission to use this software is contingent upon your acceptance
* of the terms of this agreement
* .
*
*/
package gov.nist.javax.sip.stack;

import gov.nist.core.InternalErrorHandler;
import gov.nist.javax.sip.SIPConstants;
import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.header.CallID;
import gov.nist.javax.sip.header.Event;
import gov.nist.javax.sip.header.From;
import gov.nist.javax.sip.header.To;
import gov.nist.javax.sip.header.Via;
import gov.nist.javax.sip.header.ViaList;
import gov.nist.javax.sip.message.SIPMessage;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;

import java.io.IOException;
import java.net.InetAddress;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import javax.sip.Dialog;
import javax.sip.IOExceptionEvent;
import javax.sip.ServerTransaction;
import javax.sip.TransactionState;
import javax.sip.message.Request;
import javax.sip.message.Response;

/*
* Modifications for TLS Support added by Daniel J. Martinez Manzano
* <dani@dif.um.es> Bug fixes by Jeroen van Bemmel (JvB) and others.
*/

/**
* Abstract class to support both client and server transactions. Provides an
* encapsulation of a message channel, handles timer events, and creation of the
* Via header for a message.
*
* @author Jeff Keyser
* @author M. Ranganathan
*
*
* @version 1.2 $Revision: 1.60 $ $Date: 2009/05/11 18:52:39 $
*/
public abstract class SIPTransaction extends MessageChannel implements
    javax.sip.Transaction, gov.nist.javax.sip.TransactionExt {

  protected boolean toListener; // Flag to indicate that the listener gets

  // to see the event.

  protected int BASE_TIMER_INTERVAL = SIPTransactionStack.BASE_TIMER_INTERVAL;
  /**
   * 5 sec Maximum duration a message will remain in the network
   */
  protected int T4 = 5000 / BASE_TIMER_INTERVAL;

  /**
   * The maximum retransmit interval for non-INVITE requests and INVITE
   * responses
   */
  protected int T2 = 4000 / BASE_TIMER_INTERVAL;
  protected int TIMER_I = T4;

  protected int TIMER_K = T4;

  protected int TIMER_D = 32000 / BASE_TIMER_INTERVAL;

  // protected static final int TIMER_C = 3 * 60 * 1000 / BASE_TIMER_INTERVAL;

  /**
   * One timer tick.
   */
  protected static final int T1 = 1;

  /**
   * INVITE request retransmit interval, for UDP only
   */
  protected static final int TIMER_A = 1;

  /**
   * INVITE transaction timeout timer
   */
  protected static final int TIMER_B = 64;

  protected static final int TIMER_J = 64;

  protected static final int TIMER_F = 64;

  protected static final int TIMER_H = 64;

  // Proposed feature for next release.
  protected Object applicationData;

  protected SIPResponse lastResponse;

  // private SIPDialog dialog;

  protected boolean isMapped;

  private Semaphore semaphore;

  protected boolean isSemaphoreAquired;

  // protected boolean eventPending; // indicate that an event is pending
  // here.

  protected String transactionId; // Transaction Id.

  // Audit tag used by the SIP Stack audit
  public long auditTag = 0;

  /**
   * Initialized but no state assigned.
   */
  public static final TransactionState INITIAL_STATE = null;

  /**
   * Trying state.
   */
  public static final TransactionState TRYING_STATE = TransactionState.TRYING;

  /**
   * CALLING State.
   */
  public static final TransactionState CALLING_STATE = TransactionState.CALLING;

  /**
   * Proceeding state.
   */
  public static final TransactionState PROCEEDING_STATE = TransactionState.PROCEEDING;

  /**
   * Completed state.
   */
  public static final TransactionState COMPLETED_STATE = TransactionState.COMPLETED;

  /**
   * Confirmed state.
   */
  public static final TransactionState CONFIRMED_STATE = TransactionState.CONFIRMED;

  /**
   * Terminated state.
   */
  public static final TransactionState TERMINATED_STATE = TransactionState.TERMINATED;

  /**
   * Maximum number of ticks between retransmissions.
   */
  protected static final int MAXIMUM_RETRANSMISSION_TICK_COUNT = 8;

  // Parent stack for this transaction
  protected transient SIPTransactionStack sipStack;

  // Original request that is being handled by this transaction
  protected SIPRequest originalRequest;

  // Underlying channel being used to send messages for this transaction
  private transient MessageChannel encapsulatedChannel;

  // Port of peer
  protected int peerPort;

  // Address of peer
  protected InetAddress peerInetAddress;

  // Address of peer as a string
  protected String peerAddress;

  // Protocol of peer
  protected String peerProtocol;

  // @@@ hagai - NAT changes
  // Source port extracted from peer packet
  protected int peerPacketSourcePort;

  protected InetAddress peerPacketSourceAddress;

  protected boolean transactionTimerStarted = false;

  // Transaction branch ID
  private String branch;

  // Method of the Request used to create the transaction.
  private String method;

  // Sequence number of request used to create the transaction
  private long cSeq;

  // Current transaction state
  private TransactionState currentState;

  // Number of ticks the retransmission timer was set to last
  private transient int retransmissionTimerLastTickCount;

  // Number of ticks before the message is retransmitted
  private transient int retransmissionTimerTicksLeft;

  // Number of ticks before the transaction times out
  protected int timeoutTimerTicksLeft;

  // List of event listeners for this transaction
  private transient Set<SIPTransactionEventListener> eventListeners;

  // Hang on to these - we clear out the request URI after
  // transaction goes to final state. Pointers to these are kept around
  // for transaction matching as long as the transaction is in
  // the transaction table.
  protected From from;

  protected To to;

  protected Event event;

  protected CallID callId;

  // Back ptr to the JAIN layer.
  // private Object wrapper;

  // Counter for caching of connections.
  // Connection lingers for collectionTime
  // after the Transaction goes to terminated state.
  protected int collectionTime;

  protected String toTag;

  protected String fromTag;

  private boolean terminatedEventDelivered;

  public String getBranchId() {
    return this.branch;
  }

  /**
   * The linger timer is used to remove the transaction from the transaction
   * table after it goes into terminated state. This allows connection caching
   * and also takes care of race conditins.
   *
   *
   */
  class LingerTimer extends SIPStackTimerTask {

    public LingerTimer() {
      SIPTransaction sipTransaction = SIPTransaction.this;
      if (sipStack.logWriter.isLoggingEnabled()) {
        sipStack.logWriter.logDebug("LingerTimer : "
            + sipTransaction.getTransactionId());
      }

    }

    protected void runTask() {
      SIPTransaction transaction = SIPTransaction.this;
      // release the connection associated with this transaction.
      SIPTransactionStack sipStack = transaction.getSIPStack();

      if (sipStack.logWriter.isLoggingEnabled()) {
        sipStack.logWriter.logDebug("LingerTimer: run() : "
            + getTransactionId());
      }

      if (transaction instanceof SIPClientTransaction) {
        sipStack.removeTransaction(transaction);
        transaction.close();

      } else if (transaction instanceof ServerTransaction) {
        // Remove it from the set
        if (sipStack.isLoggingEnabled())
          sipStack.logWriter.logDebug("removing" + transaction);
        sipStack.removeTransaction(transaction);
        if ((!sipStack.cacheServerConnections)
            && transaction.encapsulatedChannel instanceof TCPMessageChannel
            && --((TCPMessageChannel) transaction.encapsulatedChannel).useCount <= 0) {
          // Close the encapsulated socket if stack is configured
          transaction.close();
        } else if ((!sipStack.cacheServerConnections)
            && transaction.encapsulatedChannel instanceof TLSMessageChannel
            && --((TLSMessageChannel) transaction.encapsulatedChannel).useCount <= 0) {
          // Close the encapsulated socket if stack is configured
          transaction.close();
        } else {
          if (sipStack.isLoggingEnabled()
              && (!sipStack.cacheServerConnections)
              && transaction.isReliable()) {
            int useCount;

            if (transaction.encapsulatedChannel instanceof TCPMessageChannel)
              useCount = ((TCPMessageChannel) transaction.encapsulatedChannel).useCount;
            else
              useCount = ((TLSMessageChannel) transaction.encapsulatedChannel).useCount;

            sipStack.logWriter.logDebug("Use Count = " + useCount);
          }
        }
      }

    }
  }

  /**
   * Transaction constructor.
   *
   * @param newParentStack
   *            Parent stack for this transaction.
   * @param newEncapsulatedChannel
   *            Underlying channel for this transaction.
   */
  protected SIPTransaction(SIPTransactionStack newParentStack,
      MessageChannel newEncapsulatedChannel) {

    sipStack = newParentStack;
    this.semaphore = new Semaphore(1,true);

    encapsulatedChannel = newEncapsulatedChannel;
    // Record this to check if the address has changed before sending
    // message to avoid possible race condition.
    this.peerPort = newEncapsulatedChannel.getPeerPort();
    this.peerAddress = newEncapsulatedChannel.getPeerAddress();
    this.peerInetAddress = newEncapsulatedChannel.getPeerInetAddress();
    // @@@ hagai
    this.peerPacketSourcePort = newEncapsulatedChannel
        .getPeerPacketSourcePort();
    this.peerPacketSourceAddress = newEncapsulatedChannel
        .getPeerPacketSourceAddress();
    this.peerProtocol = newEncapsulatedChannel.getPeerProtocol();
    if (this.isReliable()) {
      if (encapsulatedChannel instanceof TLSMessageChannel) {
        ((TLSMessageChannel) encapsulatedChannel).useCount++;
        if (sipStack.isLoggingEnabled())
          sipStack.logWriter
              .logDebug("use count for encapsulated channel"
                  + this
                  + " "
                  + ((TLSMessageChannel) encapsulatedChannel).useCount);
      } else {
        ((TCPMessageChannel) encapsulatedChannel).useCount++;
        if (sipStack.isLoggingEnabled())
          sipStack.logWriter
              .logDebug("use count for encapsulated channel"
                  + this
                  + " "
                  + ((TCPMessageChannel) encapsulatedChannel).useCount);
      }
    }

    this.currentState = null;

    disableRetransmissionTimer();
    disableTimeoutTimer();
    eventListeners = Collections.synchronizedSet(new HashSet<SIPTransactionEventListener>());

    // Always add the parent stack as a listener
    // of this transaction
    addEventListener(newParentStack);

  }

  /**
   * Sets the request message that this transaction handles.
   *
   * @param newOriginalRequest
   *            Request being handled.
   */
  public void setOriginalRequest(SIPRequest newOriginalRequest) {

    // Branch value of topmost Via header
    String newBranch;

    if (this.originalRequest != null
        && (!this.originalRequest.getTransactionId().equals(
            newOriginalRequest.getTransactionId()))) {
      sipStack.removeTransactionHash(this);
    }
    // This will be cleared later.

    this.originalRequest = newOriginalRequest;

    // just cache the control information so the
    // original request can be released later.
    this.method = newOriginalRequest.getMethod();
    this.from = (From) newOriginalRequest.getFrom();
    this.to = (To) newOriginalRequest.getTo();
    // Save these to avoid concurrent modification exceptions!
    this.toTag = this.to.getTag();
    this.fromTag = this.from.getTag();
    this.callId = (CallID) newOriginalRequest.getCallId();
    this.cSeq = newOriginalRequest.getCSeq().getSeqNumber();
    this.event = (Event) newOriginalRequest.getHeader("Event");
    this.transactionId = newOriginalRequest.getTransactionId();

    originalRequest.setTransaction(this);

    // If the message has an explicit branch value set,
    newBranch = ((Via) newOriginalRequest.getViaHeaders().getFirst())
        .getBranch();
    if (newBranch != null) {
      if (sipStack.isLoggingEnabled())
        sipStack.logWriter.logDebug("Setting Branch id : " + newBranch);

      // Override the default branch with the one
      // set by the message
      setBranch(newBranch);

    } else {
      if (sipStack.isLoggingEnabled())
        sipStack.logWriter.logDebug("Branch id is null - compute TID!"
            + newOriginalRequest.encode());
      setBranch(newOriginalRequest.getTransactionId());
    }
  }

  /**
   * Gets the request being handled by this transaction.
   *
   * @return -- the original Request associated with this transaction.
   */
  public SIPRequest getOriginalRequest() {
    return originalRequest;
  }

  /**
   * Get the original request but cast to a Request structure.
   *
   * @return the request that generated this transaction.
   */
  public Request getRequest() {
    return (Request) originalRequest;
  }

  /**
   * Returns a flag stating whether this transaction is for an INVITE request
   * or not.
   *
   * @return -- true if this is an INVITE request, false if not.
   */
  public final boolean isInviteTransaction() {
    return getMethod().equals(Request.INVITE);
  }

  /**
   * Return true if the transaction corresponds to a CANCEL message.
   *
   * @return -- true if the transaciton is a CANCEL transaction.
   */
  public final boolean isCancelTransaction() {
    return getMethod().equals(Request.CANCEL);
  }

  /**
   * Return a flag that states if this is a BYE transaction.
   *
   * @return true if the transaciton is a BYE transaction.
   */
  public final boolean isByeTransaction() {
    return getMethod().equals(Request.BYE);
  }

  /**
   * Returns the message channel used for transmitting/receiving messages for
   * this transaction. Made public in support of JAIN dual transaction model.
   *
   * @return Encapsulated MessageChannel.
   *
   */
  public MessageChannel getMessageChannel() {
    return encapsulatedChannel;
  }

  /**
   * Sets the Via header branch parameter used to identify this transaction.
   *
   * @param newBranch
   *            New string used as the branch for this transaction.
   */
  public final void setBranch(String newBranch) {
    branch = newBranch;
  }

  /**
   * Gets the current setting for the branch parameter of this transaction.
   *
   * @return Branch parameter for this transaction.
   */
  public final String getBranch() {
    if (this.branch == null) {
      this.branch = getOriginalRequest().getTopmostVia().getBranch();
    }
    return branch;
  }

  /**
   * Get the method of the request used to create this transaction.
   *
   * @return the method of the request for the transaction.
   */
  public final String getMethod() {
    return this.method;
  }

  /**
   * Get the Sequence number of the request used to create the transaction.
   *
   * @return the cseq of the request used to create the transaction.
   */
  public final long getCSeq() {
    return this.cSeq;
  }

  /**
   * Changes the state of this transaction.
   *
   * @param newState
   *            New state of this transaction.
   */
  public void setState(TransactionState newState) {
    // PATCH submitted by sribeyron
    if (currentState == TransactionState.COMPLETED) {
      if (newState != TransactionState.TERMINATED
          && newState != TransactionState.CONFIRMED)
        newState = TransactionState.COMPLETED;
    }
    if (currentState == TransactionState.CONFIRMED) {
      if (newState != TransactionState.TERMINATED)
        newState = TransactionState.CONFIRMED;
    }
    if (currentState != TransactionState.TERMINATED)
      currentState = newState;
    else
      newState = currentState;
    // END OF PATCH
    if (sipStack.isLoggingEnabled()) {
      sipStack.logWriter.logDebug("Transaction:setState " + newState
          + " " + this + " branchID = " + this.getBranch()
          + " isClient = " + (this instanceof SIPClientTransaction));
      sipStack.logWriter.logStackTrace();
    }
  }

  /**
   * Gets the current state of this transaction.
   *
   * @return Current state of this transaction.
   */
  public TransactionState getState() {
    return this.currentState;
  }

  /**
   * Enables retransmission timer events for this transaction to begin in one
   * tick.
   */
  protected final void enableRetransmissionTimer() {
    enableRetransmissionTimer(1);
  }

  /**
   * Enables retransmission timer events for this transaction to begin after
   * the number of ticks passed to this routine.
   *
   * @param tickCount
   *            Number of ticks before the next retransmission timer event
   *            occurs.
   */
  protected final void enableRetransmissionTimer(int tickCount) {
    // For INVITE Client transactions, double interval each time
    if (isInviteTransaction() && (this instanceof SIPClientTransaction)) {
      retransmissionTimerTicksLeft = tickCount;
    } else {
      // non-INVITE transactions and 3xx-6xx responses are capped at T2
      retransmissionTimerTicksLeft = Math.min(tickCount,
          MAXIMUM_RETRANSMISSION_TICK_COUNT);
    }
    retransmissionTimerLastTickCount = retransmissionTimerTicksLeft;
  }

  /**
   * Turns off retransmission events for this transaction.
   */
  protected final void disableRetransmissionTimer() {
    retransmissionTimerTicksLeft = -1;
  }

  /**
   * Enables a timeout event to occur for this transaction after the number of
   * ticks passed to this method.
   *
   * @param tickCount
   *            Number of ticks before this transaction times out.
   */
  protected final void enableTimeoutTimer(int tickCount) {
    if (sipStack.isLoggingEnabled())
      sipStack.logWriter.logDebug("enableTimeoutTimer " + this
          + " tickCount " + tickCount + " currentTickCount = "
          + timeoutTimerTicksLeft);

    timeoutTimerTicksLeft = tickCount;
  }

  /**
   * Disabled the timeout timer.
   */
  protected final void disableTimeoutTimer() {
    timeoutTimerTicksLeft = -1;
  }

  /**
   * Fired after each timer tick. Checks the retransmission and timeout timers
   * of this transaction, and fired these events if necessary.
   */
  final void fireTimer() {
    // If the timeout timer is enabled,

    if (timeoutTimerTicksLeft != -1) {
      // Count down the timer, and if it has run out,
      if (--timeoutTimerTicksLeft == 0) {
        // Fire the timeout timer
        fireTimeoutTimer();
      }
    }

    // If the retransmission timer is enabled,
    if (retransmissionTimerTicksLeft != -1) {
      // Count down the timer, and if it has run out,
      if (--retransmissionTimerTicksLeft == 0) {
        // Enable this timer to fire again after
        // twice the original time
        enableRetransmissionTimer(retransmissionTimerLastTickCount * 2);
        // Fire the timeout timer
        fireRetransmissionTimer();
      }
    }
  }

  /**
   * Tests if this transaction has terminated.
   *
   * @return Trus if this transaction is terminated, false if not.
   */
  public final boolean isTerminated() {
    return getState() == TERMINATED_STATE;
  }

  public String getHost() {
    return encapsulatedChannel.getHost();
  }

  public String getKey() {
    return encapsulatedChannel.getKey();
  }

  public int getPort() {
    return encapsulatedChannel.getPort();
  }

  public SIPTransactionStack getSIPStack() {
    return (SIPTransactionStack) sipStack;
  }

  public String getPeerAddress() {
    return this.peerAddress;
  }

  public int getPeerPort() {
    return this.peerPort;
  }

  // @@@ hagai
  public int getPeerPacketSourcePort() {
    return this.peerPacketSourcePort;
  }

  public InetAddress getPeerPacketSourceAddress() {
    return this.peerPacketSourceAddress;
  }

  protected InetAddress getPeerInetAddress() {
    return this.peerInetAddress;
  }

  protected String getPeerProtocol() {
    return this.peerProtocol;
  }

  public String getTransport() {
    return encapsulatedChannel.getTransport();
  }

  public boolean isReliable() {
    return encapsulatedChannel.isReliable();
  }

  /**
   * Returns the Via header for this channel. Gets the Via header of the
   * underlying message channel, and adds a branch parameter to it for this
   * transaction.
   */
  public Via getViaHeader() {
    // Via header of the encapulated channel
    Via channelViaHeader;

    // Add the branch parameter to the underlying
    // channel's Via header
    channelViaHeader = super.getViaHeader();
    try {
      channelViaHeader.setBranch(branch);
    } catch (java.text.ParseException ex) {
    }
    return channelViaHeader;

  }

  /**
   * Process the message through the transaction and sends it to the SIP peer.
   *
   * @param messageToSend
   *            Message to send to the SIP peer.
   */
  public void sendMessage(SIPMessage messageToSend) throws IOException {
    // Use the peer address, port and transport
    // that was specified when the transaction was
    // created. Bug was noted by Bruce Evangelder
    // soleo communications.
    try {
      encapsulatedChannel.sendMessage(messageToSend,
          this.peerInetAddress, this.peerPort);
    } finally {
      this.startTransactionTimer();
    }
  }

  /**
   * Parse the byte array as a message, process it through the transaction,
   * and send it to the SIP peer. This is just a placeholder method -- calling
   * it will result in an IO exception.
   *
   * @param messageBytes
   *            Bytes of the message to send.
   * @param receiverAddress
   *            Address of the target peer.
   * @param receiverPort
   *            Network port of the target peer.
   *
   * @throws IOException
   *             If called.
   */
  protected void sendMessage(byte[] messageBytes,
      InetAddress receiverAddress, int receiverPort, boolean retry)
      throws IOException {
    throw new IOException(
        "Cannot send unparsed message through Transaction Channel!");
  }

  /**
   * Adds a new event listener to this transaction.
   *
   * @param newListener
   *            Listener to add.
   */
  public void addEventListener(SIPTransactionEventListener newListener) {
    eventListeners.add(newListener);
  }

  /**
   * Removed an event listener from this transaction.
   *
   * @param oldListener
   *            Listener to remove.
   */
  public void removeEventListener(SIPTransactionEventListener oldListener) {
    eventListeners.remove(oldListener);
  }

  /**
   * Creates a SIPTransactionErrorEvent and sends it to all of the listeners
   * of this transaction. This method also flags the transaction as
   * terminated.
   *
   * @param errorEventID
   *            ID of the error to raise.
   */
  protected void raiseErrorEvent(int errorEventID) {

    // Error event to send to all listeners
    SIPTransactionErrorEvent newErrorEvent;
    // Iterator through the list of listeners
    Iterator<SIPTransactionEventListener> listenerIterator;
    // Next listener in the list
    SIPTransactionEventListener nextListener;

    // Create the error event
    newErrorEvent = new SIPTransactionErrorEvent(this, errorEventID);

    // Loop through all listeners of this transaction
    synchronized (eventListeners) {
      listenerIterator = eventListeners.iterator();
      while (listenerIterator.hasNext()) {
        // Send the event to the next listener
        nextListener = (SIPTransactionEventListener) listenerIterator
            .next();
        nextListener.transactionErrorEvent(newErrorEvent);
      }
    }
    // Clear the event listeners after propagating the error.
    // Retransmit notifications are just an alert to the
    // application (they are not an error).
    if (errorEventID != SIPTransactionErrorEvent.TIMEOUT_RETRANSMIT) {
      eventListeners.clear();

      // Errors always terminate a transaction
      this.setState(TransactionState.TERMINATED);

      if (this instanceof SIPServerTransaction && this.isByeTransaction()
          && this.getDialog() != null)
        ((SIPDialog) this.getDialog())
            .setState(SIPDialog.TERMINATED_STATE);
    }
  }

  /**
   * A shortcut way of telling if we are a server transaction.
   */
  protected boolean IsServerTransaction() {
    return this instanceof SIPServerTransaction;
  }

  /**
   * Gets the dialog object of this Transaction object. This object returns
   * null if no dialog exists. A dialog only exists for a transaction when a
   * session is setup between a User Agent Client and a User Agent Server,
   * either by a 1xx Provisional Response for an early dialog or a 200OK
   * Response for a committed dialog.
   *
   * @return the Dialog Object of this Transaction object.
   * @see Dialog
   */
  public abstract Dialog getDialog();

  /**
   * set the dialog object.
   *
   * @param sipDialog --
   *            the dialog to set.
   * @param dialogId --
   *            the dialog id ot associate with the dialog.s
   */
  public abstract void setDialog(SIPDialog sipDialog, String dialogId);

  /**
   * Returns the current value of the retransmit timer in milliseconds used to
   * retransmit messages over unreliable transports.
   *
   * @return the integer value of the retransmit timer in milliseconds.
   */
  public int getRetransmitTimer() {
    return SIPTransactionStack.BASE_TIMER_INTERVAL;
  }

  /**
   * Get the host to assign for an outgoing Request via header.
   */
  public String getViaHost() {
    return this.getViaHeader().getHost();

  }

  /**
   * Get the last response. This is used internally by the implementation.
   * Dont rely on it.
   *
   * @return the last response received (for client transactions) or sent (for
   *         server transactions).
   */
  public SIPResponse getLastResponse() {
    return this.lastResponse;
  }

  /**
   * Get the JAIN interface response
   */
  public Response getResponse() {
    return (Response) this.lastResponse;
  }

  /**
   * Get the transaction Id.
   */
  public String getTransactionId() {
    return this.transactionId;
  }

  /**
   * Hashcode method for fast hashtable lookup.
   */
  public int hashCode() {
    if (this.transactionId == null)
      return -1;
    else
      return this.transactionId.hashCode();
  }

  /**
   * Get the port to assign for the via header of an outgoing message.
   */
  public int getViaPort() {
    return this.getViaHeader().getPort();
  }

  /**
   * A method that can be used to test if an incoming request belongs to this
   * transction. This does not take the transaction state into account when
   * doing the check otherwise it is identical to isMessagePartOfTransaction.
   * This is useful for checking if a CANCEL belongs to this transaction.
   *
   * @param requestToTest
   *            is the request to test.
   * @return true if the the request belongs to the transaction.
   *
   */
  public boolean doesCancelMatchTransaction(SIPRequest requestToTest) {

    // List of Via headers in the message to test
    ViaList viaHeaders;
    // Topmost Via header in the list
    Via topViaHeader;
    // Branch code in the topmost Via header
    String messageBranch;
    // Flags whether the select message is part of this transaction
    boolean transactionMatches;

    transactionMatches = false;

    if (this.getOriginalRequest() == null
        || this.getOriginalRequest().getMethod().equals(Request.CANCEL))
      return false;
    // Get the topmost Via header and its branch parameter
    viaHeaders = requestToTest.getViaHeaders();
    if (viaHeaders != null) {

      topViaHeader = (Via) viaHeaders.getFirst();
      messageBranch = topViaHeader.getBranch();
      if (messageBranch != null) {

        // If the branch parameter exists but
        // does not start with the magic cookie,
        if (!messageBranch.startsWith(SIPConstants.BRANCH_MAGIC_COOKIE)) {

          // Flags this as old
          // (RFC2543-compatible) client
          // version
          messageBranch = null;

        }

      }

      // If a new branch parameter exists,
      if (messageBranch != null && this.getBranch() != null) {

        // If the branch equals the branch in
        // this message,
        if (getBranch().equalsIgnoreCase(messageBranch)
            && topViaHeader.getSentBy().equals(
                ((Via) getOriginalRequest().getViaHeaders()
                    .getFirst()).getSentBy())) {
          transactionMatches = true;
          if (sipStack.isLoggingEnabled())
            sipStack.logWriter.logDebug("returning  true");
        }

      } else {
        // If this is an RFC2543-compliant message,
        // If RequestURI, To tag, From tag,
        // CallID, CSeq number, and top Via
        // headers are the same,
        if (sipStack.isLoggingEnabled())
          sipStack.logWriter.logDebug("testing against "
              + getOriginalRequest());

        if (getOriginalRequest().getRequestURI().equals(
            requestToTest.getRequestURI())
            && getOriginalRequest().getTo().equals(
                requestToTest.getTo())
            && getOriginalRequest().getFrom().equals(
                requestToTest.getFrom())
            && getOriginalRequest().getCallId().getCallId().equals(
                requestToTest.getCallId().getCallId())
            && getOriginalRequest().getCSeq().getSeqNumber() == requestToTest
                .getCSeq().getSeqNumber()
            && topViaHeader.equals(getOriginalRequest()
                .getViaHeaders().getFirst())) {

          transactionMatches = true;
        }

      }

    }

    // JvB: Need to pass the CANCEL to the listener! Retransmitted INVITEs
    // set it to false
    if (transactionMatches) {
      this.setPassToListener();
    }
    return transactionMatches;
  }

  /**
   * Sets the value of the retransmit timer to the newly supplied timer value.
   * The retransmit timer is expressed in milliseconds and its default value
   * is 500ms. This method allows the application to change the transaction
   * retransmit behavior for different networks. Take the gateway proxy as an
   * example. The internal intranet is likely to be reatively uncongested and
   * the endpoints will be relatively close. The external network is the
   * general Internet. This functionality allows different retransmit times
   * for either side.
   *
   * @param retransmitTimer -
   *            the new integer value of the retransmit timer in milliseconds.
   */
  public void setRetransmitTimer(int retransmitTimer) {

    if (retransmitTimer <= 0)
      throw new IllegalArgumentException(
          "Retransmit timer must be positive!");
    if (this.transactionTimerStarted)
      throw new IllegalStateException(
          "Transaction timer is already started");
    BASE_TIMER_INTERVAL = retransmitTimer;
    T4 = 5000 / BASE_TIMER_INTERVAL;

    T2 = 4000 / BASE_TIMER_INTERVAL;
    TIMER_I = T4;

    TIMER_K = T4;

    TIMER_D = 32000 / BASE_TIMER_INTERVAL;

  }

  /**
   * Close the encapsulated channel.
   */
  public void close() {
    this.encapsulatedChannel.close();
    if (sipStack.isLoggingEnabled())
      sipStack.logWriter.logDebug("Closing " + this.encapsulatedChannel);

  }

  public boolean isSecure() {
    return encapsulatedChannel.isSecure();
  }

  public MessageProcessor getMessageProcessor() {
    return this.encapsulatedChannel.getMessageProcessor();
  }

  /**
   * Set the application data pointer. This is un-interpreted by the stack.
   * This is provided as a conveniant way of keeping book-keeping data for
   * applications. Note that null clears the application data pointer
   * (releases it).
   *
   * @param applicationData --
   *            application data pointer to set. null clears the applicationd
   *            data pointer.
   *
   */

  public void setApplicationData(Object applicationData) {
    this.applicationData = applicationData;
  }

  /**
   * Get the application data associated with this transaction.
   *
   * @return stored application data.
   */
  public Object getApplicationData() {
    return this.applicationData;
  }

  /**
   * Set the encapsuated channel. The peer inet address and port are set equal
   * to the message channel.
   */
  public void setEncapsulatedChannel(MessageChannel messageChannel) {
    this.encapsulatedChannel = messageChannel;
    this.peerInetAddress = messageChannel.getPeerInetAddress();
    this.peerPort = messageChannel.getPeerPort();
  }

  /**
   * Return the SipProvider for which the transaction is assigned.
   *
   * @return the SipProvider for the transaction.
   */
  public SipProviderImpl getSipProvider() {

    return this.getMessageProcessor().getListeningPoint().getProvider();
  }

  /**
   * Raise an IO Exception event - this is used for reporting asynchronous IO
   * Exceptions that are attributable to this transaction.
   *
   */
  public void raiseIOExceptionEvent() {
    setState(TransactionState.TERMINATED);
    String host = getPeerAddress();
    int port = getPeerPort();
    String transport = getTransport();
    IOExceptionEvent exceptionEvent = new IOExceptionEvent(this, host,
        port, transport);
    getSipProvider().handleEvent(exceptionEvent, this);
  }

  /**
   * A given tx can process only a single outstanding event at a time. This
   * semaphore gaurds re-entrancy to the transaction.
   *
   */
  public boolean acquireSem() {
    boolean retval = false;
    try {
      if (sipStack.getLogWriter().isLoggingEnabled()) {
        sipStack.getLogWriter().logDebug("acquireSem [[[[" + this);
        sipStack.getLogWriter().logStackTrace();
      }
      retval = this.semaphore.tryAcquire(1000, TimeUnit.MILLISECONDS);
      if ( sipStack.isLoggingEnabled())
        sipStack.getLogWriter().logDebug(
          "acquireSem() returning : " + retval);
      return retval;
    } catch (Exception ex) {
      sipStack.logWriter.logError("Unexpected exception acquiring sem",
          ex);
      InternalErrorHandler.handleException(ex);
      return false;
    } finally {
      this.isSemaphoreAquired = retval;
    }

  }

  /**
   * Release the transaction semaphore.
   *
   */
  public void releaseSem() {
    try {

      this.toListener = false;
      this.semRelease();

    } catch (Exception ex) {
      sipStack.logWriter.logError("Unexpected exception releasing sem",
          ex);

    }

  }

  protected void semRelease() {
    try {
      if (sipStack.getLogWriter().isLoggingEnabled()) {
        sipStack.getLogWriter().logDebug("semRelease ]]]]" + this);
        sipStack.getLogWriter().logStackTrace();
      }
      this.isSemaphoreAquired = false;
      this.semaphore.release();

    } catch (Exception ex) {
      sipStack.logWriter.logError("Unexpected exception releasing sem",
          ex);

    }
  }

  /**
   * Set true to pass the request up to the listener. False otherwise.
   *
   */

  public boolean passToListener() {
    return toListener;
  }

  /**
   * Set the passToListener flag to true.
   */
  public void setPassToListener() {
    if (sipStack.logWriter.isLoggingEnabled()) {
      sipStack.logWriter.logDebug("setPassToListener()");
    }
    this.toListener = true;

  }

  /**
   * Flag to test if the terminated event is delivered.
   *
   * @return
   */
  protected synchronized boolean testAndSetTransactionTerminatedEvent() {
    boolean retval = !this.terminatedEventDelivered;
    this.terminatedEventDelivered = true;
    return retval;
  }

  /**
   * Start the timer that runs the transaction state machine.
   *
   */

  protected abstract void startTransactionTimer();

  /**
   * Tests a message to see if it is part of this transaction.
   *
   * @return True if the message is part of this transaction, false if not.
   */
  public abstract boolean isMessagePartOfTransaction(SIPMessage messageToTest);

  /**
   * This method is called when this transaction's retransmission timer has
   * fired.
   */
  protected abstract void fireRetransmissionTimer();

  /**
   * This method is called when this transaction's timeout timer has fired.
   */
  protected abstract void fireTimeoutTimer();

}
TOP

Related Classes of gov.nist.javax.sip.stack.SIPTransaction$LingerTimer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.