Package com.ericsson.ssa.sip

Source Code of com.ericsson.ssa.sip.SipSessionImplBase$TmpCallData

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.sip;

import com.ericsson.ssa.config.LayerHandler;
import com.ericsson.ssa.config.ConvergedContext;
import com.ericsson.ssa.container.SipBindingResolver;
import com.ericsson.ssa.container.sim.ServletDispatcher;
import com.ericsson.ssa.sip.PathNode.Type;
import com.ericsson.ssa.sip.dialog.Cleanable;
import com.ericsson.ssa.sip.dialog.DialogCleaner;

import com.ericsson.ssa.utils.UUIDUtil;
import com.sun.enterprise.util.LocalStringManagerImpl;
import org.apache.catalina.session.SessionLock;

import org.jvnet.glassfish.comms.util.LogUtil;
import org.jvnet.glassfish.comms.deployment.backend.SipApplication;

import java.io.IOException;
import java.io.Externalizable;
import java.io.ObjectInput;
import java.io.ObjectOutput;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;

import java.net.InetAddress;
import java.net.InetSocketAddress;

import javax.servlet.ServletException;
import javax.servlet.ServletContext;
import javax.servlet.sip.Address;
import javax.servlet.sip.ProxyBranch;
import javax.servlet.sip.SipApplicationSession;
import javax.servlet.sip.SipServletMessage;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.SipSessionEvent;
import javax.servlet.sip.SipSessionListener;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.UAMode;
import javax.servlet.sip.URI;
import javax.servlet.sip.ar.SipApplicationRoutingRegion;


/**
* The SipSession implementation represents the SSA point-to-point SIP
* relationship which is almost equal to a SIP dialog (it extends the dialog
* definition, see specification). Important that this class will use getters
* and setters internally since it should be possible to serialize the content
* to external media.
*/
public abstract class SipSessionImplBase implements SipSessionBase, Externalizable, Cleanable {
    static SipBindingResolver sipBindingResolver = SipBindingResolver.instance();

    /** The serialized format versioning. 2 = second version. */
    private static final short serializedFormVersion  = 2;
   
    // --- Transient fields ---
    private static final long serialVersionUID = 3762535598732096310L;
    private static final Logger log = LogUtil.SIP_LOGGER.getLogger();
    public final static String SIP_SESSIONID_DELIMITER = "##";
   
    // this is an atomic boolean since we want to have visibility of this variable
    // in different threads and we want to avoid that multiple threads start invalidating
    // the SS without the need to obtain the sslock.
    private AtomicBoolean isValid = new AtomicBoolean(true);
    /**
     * Flag indicating whether the application will be notified
     * when this session transitions to ready-to-invalidate state or not.
     */
    private volatile boolean invalidateWhenReady = true;
    /**
     * Flag indicating whether the session is in ready-to-invalidate
     * state or not.
     */
    private volatile boolean readyToInvalidate = false;
    /**
     * Flag indicating whether the session was invalidated due ready-to-invalidate mechanism
     * (or explicitly invalidated by application or session-timeout)
     */
    protected transient volatile boolean invalidatedDueIWR = false;
    private transient SessionLock sessionLock = new SessionLock();
    private transient Object ssLock = new Object();
    private transient B2buaHelperPendingMessages pendingMessages = null;
    private transient OutboundInterface oi = null;
    private transient DialogSet dialogSet; // Final
    private transient DialogFragment dialogFragment = null;
    private transient TmpCallData tmpCallData = null;
    protected static final LocalStringManagerImpl localStrings =
            new LocalStringManagerImpl(SipSessionImplBase.class);

    protected SipSessionImplBase(DialogSet set) {
        dialogSet = set;
    }

    public boolean isValid() {
        return isValid.get();
    }

    /**
     * Creates a unique id for this session
     *
     * @return the unique id
     */
    protected StringBuilder createID() {
        StringBuilder id = new StringBuilder(UUIDUtil.randomUUID());
        id.append(SIP_SESSIONID_DELIMITER);
        id.append(sipBindingResolver.getCurrentPublicHost());

        return id;
    }

    public long getCreationTime() {
        return getPFieldCreationDate();
    }

    public String getId() {
        return getPFieldId();
    }

    public long getLastAccessedTime() {
        return getPFieldLastAccessedTime();
    }

    public long getExpirationTime() {
        return getPFieldExpirationTime();
    }

    public void access() {
        setPFieldLastAccessedTime(getPFieldCurrentAccessedTime());
        setPFieldCurrentAccessedTime(System.currentTimeMillis());
    }

    public void invalidate() {
        // if called from SipServlet code
        invalidate(false);
    }

    public void invalidate(boolean hasTimedOut) {
      if (isValid.getAndSet(false)) {
        if(log.isLoggable(Level.FINE)) {
          log.log(Level.FINE, "Invalidating " + this +
              ", readyToInvalidate = " + readyToInvalidate);
        }
            // fix for 1801 :: temporarily keep the data around, so that we can provide
            // access to all those getters which are documented (in jsr289) not
            // to throw IllegalStateException after the session is invalidated,  
            tmpCallData = new TmpCallData(getCallId(),
                    getFrom(), getTo(), getCSeq(), getPFieldDialogFragmentId());
            synchronized (ssLock) {
          DialogFragment df;

          try {
            df = getDF();
          } catch (RemoteLockRuntimeException e) {
            log.log(Level.SEVERE,
                "sip.stack.sipsession.remote_locked_df_in_invalidate",
                new Object[] { getId(), getPFieldDialogFragmentId() });
            DialogCleaner.getInstance().registerForSupervision(this);

            return;
          }

          if (df != null) {
            df.invalidate(hasTimedOut); // TODO SR-FIX
            setDF(null);
            setPFieldDialogFragmentId(null);
          } else {
            // Non-dialog creational
            DialogCleaner.getInstance()
            .registerForSupervision(this, 5000);
          }

          // If the call from the SipApplicationSession then m_IsValid
          // is
          // false,
          // But if the call made directly on the session then
          // m_IsValid is
          // true.
          SipApplicationSessionImpl sas;

          try {
            sas = getApplicationSessionImpl();
          } catch (RemoteLockRuntimeException e) {
            log.log(Level.SEVERE,
                "sip.stack.sipsession.remote_locked_sas_in_invalidate",
                new Object[] { getId() });
            DialogCleaner.getInstance().registerForSupervision(this);

            return;
          }

          notifyAttributesUnbound();
          notifySessionDestroyed();

          if (sas != null) {
            if (!sas.isValid()) {
              setPFieldSipApplicationSession(null);
            } else {
              sas.removeSession(this);
            }
          }
        }
      } else {
        throw new IllegalStateException(localStrings.getLocalString(
            "ss_already_invalid",
        "Should not call invalidate() when the sip session is not valid."));
      }
    }

    private void validateSessionState() {
        if(!isValid()) {
            throw new IllegalStateException(localStrings.getLocalString(
                    "ss_invalid", "SipSession is not valid."));
        }
    }

    private void validateSessionState(boolean checkReadyToInvalidate) {
        validateSessionState();
        if(checkReadyToInvalidate && readyToInvalidate) {
            throw new IllegalStateException(localStrings.getLocalString(
                    "ss_readytoinvalidate",
                    "SipSession is in ready-to-invalidate state."));
        }
    }

    protected void initInvalidateWhenReady() {
        SipSessionManager manager = getSipSessionManager();
        if (manager != null) {
            ConvergedContext ctx = manager.getContext();
            if (ctx != null) {
                SipApplication descriptor = ctx.getSipApplication();
                if (descriptor != null &&
                        SipApplication.ApplicationVersion.VERSION_1_0.equals(
                                descriptor.getApplicationVersion())) {
                    invalidateWhenReady = false;
                    return;
                }
            }
        }
        invalidateWhenReady = true;
    }
   
    public boolean isReadyToInvalidate() {
        validateSessionState();
        return readyToInvalidate;
    }

    public boolean invalidatedDueIWR() {
        return invalidatedDueIWR;
    }   
   
    public boolean getInvalidateWhenReady() {
        validateSessionState();
        return invalidateWhenReady;
    }

    public void setInvalidateWhenReady(boolean iwr) {

        validateSessionState();

        boolean wasInvalidateWhenReady;
        synchronized (ssLock) {
            if(iwr == invalidateWhenReady) {
                return;
            }
            wasInvalidateWhenReady = invalidateWhenReady;
            invalidateWhenReady = iwr;
        }
        /*
         * If IWR was already true, then we should do nothing.
         * Otherwise we will cause an infinite loop if setInvalidateWhenReady(true)
         * is called from listener's sessionReadyToInvalidate callback.
         */
        if (!wasInvalidateWhenReady) {
            if (checkSessionReadyToInvalidate()) { // we should notify the listeners in this case.
                sessionReadyToInvalidate(); // don't hold ssLock while notifying.
            }
        }

        if (log.isLoggable(Level.FINE)) {
            if (invalidateWhenReady) {
                log.log(Level.FINE, "Invalidate-When-Ready machanism is enabled"
                        + " for SipSession with ID " + getId() + ". wasEnabled = " + wasInvalidateWhenReady);
            } else {
                log.log(Level.FINE, "Invalidate-When-Ready machanism is disabled"
                        + " for SipSession with ID" + getId() + ". wasEnabled = " + wasInvalidateWhenReady);
            }
        }
    }

    /**
     * Transition this session to ready-to-invalidate state after the current
     * transaction completes. If all the sessions in the dialog are not ready,
     * then we can not transition this session to ready-to-invalidate.
     */
    private void markReadyToInvalidate() {
        // if the session was already marked while processing the request, then do nothing.
        if(readyToInvalidate) {
            return;
        }
        synchronized (ssLock) {
            readyToInvalidate = true;
        }
    }

    /**
     * This method does not check for the validity of the session.
     */
    boolean checkSessionReadyToInvalidate() {
        if(invalidateWhenReady && readyToInvalidate) {
            return true;
        }
        return false;
    }
   
    /**
    /**
     * This method is called under any of the following circumstances:
     *
     * (a) the session had transitioned to ready-to-invalidate state and
     *     servlet's service method has completed
     * (b) the servlet has invoked setReadyToInvalidate(true) when IWR was false
     *     earlier but the session had already transitioned to ready-to-invalidate
     *     state. (eg., from doBye() method of the servlet).
     *
     * @see com.ericsson.ssa.sip.IWRHandler
     */
    void sessionReadyToInvalidate() {
        if (isValid()) {
            notifySessionReadyToInvalidate(); // notify the application listeners.
            if (invalidateWhenReady && isValid()) {
                // application is OK to do invalidaion & has not invalidated explicitly.
                if(log.isLoggable(Level.FINE)) {
                    log.log(Level.FINE, "Invalidating " + this + " which is in" +
                            " ready-to-invalidate state");
                }
                // first set the InvalidateDueIWR, so that during invalidation
                // it is clear that this is an IWR-ed SS
                invalidatedDueIWR = true;
                try {
                  invalidate(false);
                } catch (IllegalStateException e) {
                    log.log(Level.FINE, "race condition when trying to IWR invalidate " + this);
                    // OK, since the session will be invalidated anyway
                }
            }
        }
    }

    private void notifySessionReadyToInvalidate() {
        List<SipSessionListener> listeners = getSipSessionListeners();
        if (listeners == null) {
            return;
        }
        Iterator<SipSessionListener> iter = listeners.iterator();
        SipSessionEvent sipSessionEvent = new SipSessionEvent(this);
        /**
         * Stop notifying the listeners if any of the following occur:
         *
         * (a) one of the listener invoked setInvalidateWhenReady(false)
         * (b) this session is no longer valid, may be because the listener
         *     has invoked invalidate() explicitly.
         */
        while (iter.hasNext() && invalidateWhenReady && isValid()) {
            SipSessionListener listener = iter.next();
            try {
               listener.sessionReadyToInvalidate(sipSessionEvent);
            } catch (Throwable t) {
               log.log(Level.WARNING, t.getMessage(), t)
            }
        }
    }

    protected abstract void notifyAttributesUnbound();
   
    private Address getFrom() {
     
        if (dialogSet != null) {         
            return dialogSet.getFrom();
        }

        if (tmpCallData != null) {         
            return tmpCallData.getFrom();
        }        
        return getDF().getFrom();
    }

    private void setDS(DialogSet ds) {
        dialogSet = ds;
    }

    DialogSet getDS() {
        if (dialogSet != null) {
            return dialogSet;
        }

        return getDF().getDialogSet();
    }

    protected synchronized DialogFragment getDF() {
        if (dialogFragment == null) {
            if ((getPFieldDialogFragmentId() != null)) {
                dialogFragment = getDF(getPFieldDialogFragmentId());
            }
        }

        return dialogFragment; // May be null
    }

    private DialogFragment getDF(String dfId) {
        try {
            return DialogFragmentManager.getInstance()
                                                  .findDialogFragment(dfId);
        } catch (RemoteLockException e) {
            throw new RemoteLockRuntimeException(e);
        }
    }

    protected void setDF(DialogFragment df) {
        dialogFragment = df;

        if (df != null) {         
            setPFieldDialogFragmentId(df.getDialogId());
            dialogSet = null;
        }
    }

    public SipApplicationSession getApplicationSession() {
        return getPFieldSipApplicationSession();
    }

    public SipApplicationSessionImpl getApplicationSessionImpl() {
        return getPFieldSipApplicationSession();
    }

    public String getCallId() {
        if (dialogSet != null) {
            return dialogSet.getCallId();
        }

        if (tmpCallData != null) {
            return tmpCallData.getCallId();
        }

        return getDF().getCallId();
    }

    public Address getLocalParty() {
        return getPFieldSwapLocalRemote() ? getTo() : getFrom();
    }

    public Address getRemoteParty() {
        return getPFieldSwapLocalRemote() ? getFrom() : getTo();
    }

    /**
     * Returns the from address for caller and to address for callee, otherwise
     * null
     *
     * @return the from address for caller and to address for callee, otherwise
     *         null
     */
    private Address getLocalSide() {
        Address add = null;

        if (getPFieldType().equals(Type.Caller)) {
            add = getFrom();
        } else if (getPFieldType().equals(Type.Callee)) {
            add = getTo();
        } else {
            if (log.isLoggable(Level.FINE)) {
                log.log(Level.FINE,
                    "Unable to decide if its caller or callee. Session id = " +
                    getId());
            }
        }

        return add;
    }

    /**
     * Returns the to address for caller and from address for callee, otherwise
     * null
     *
     * @return the to address for caller and from address for callee, otherwise
     *         null
     */
    private Address getOtherside() {
        Address add = null;

        if (getPFieldType().equals(Type.Caller)) {
            add = getTo();
        } else if (getPFieldType().equals(Type.Callee)) {
            add = getFrom();
        } else {
            if (log.isLoggable(Level.FINE)) {
                log.log(Level.FINE,
                    "Unable to decide if its caller or callee. Session id = " +
                    getId());
            }
        }

        return add;
    }

    /**
     * SSA 7.1.2 requestURI, Call-ID, From, To, CSeq and Route headers
     */
    public SipServletRequest createRequest(String method) {       
        checkCreateRequestMethodValidity(method);

        SipServletRequest result = (getState() == State.INITIAL) ? createRequestInitial(method)
                                                        : createRequestCommitted(method);
        if (getDF() != null)
            getDF().getDialogLifeCycle().initUnitOfWork();
        return result;
    }

    /**
    * Checks whether the proposed method name is allowed. To be allowed it must satisfy:
    * <br>
    * - being a proper rfc3261 "method" and it mus
    * <br>
    * - not being a CANCEL nor an ACK.
    * <br>
     *
     * @param method
     * @throws IllegalArgumentException if the method name is not valid
     */
    private void checkCreateRequestMethodValidity(String method)
        throws IllegalArgumentException {
        // Check that the name is a valid method name according to rfc3261 ...
        checkValidMethodName(method);

        // And that it is neither CANCEL or ACK
        if ("CANCEL".equals(method) || "ACK".equals(method)) {
            throw new IllegalArgumentException(method + " is not allowed");
        }
    }

    /**
     * Checks that the method name is a valid method name rfc 3261
     *
     * @param method
     * @throws IllegalArgumentException if the method name is not a rfc 3261 "Method"
     */
    private void checkValidMethodName(String method)
        throws IllegalArgumentException {
        //
        //       INVITEm           =  %x49.4E.56.49.54.45 ; INVITE in caps
        //       ACKm              =  %x41.43.4B ; ACK in caps
        //       OPTIONSm          =  %x4F.50.54.49.4F.4E.53 ; OPTIONS in caps
        //       BYEm              =  %x42.59.45 ; BYE in caps
        //       CANCELm           =  %x43.41.4E.43.45.4C ; CANCEL in caps
        //       REGISTERm         =  %x52.45.47.49.53.54.45.52 ; REGISTER in caps
        //       Method            =  INVITEm / ACKm / OPTIONSm / BYEm
        //                            / CANCELm / REGISTERm
        //                            / extension-method
        //       extension-method  =  token
        //       token       =  1*(alphanum / "-" / "." / "!" / "%" / "*"
        //             / "_" / "+" / "`" / "'" / "~" )
        //

        // We only need to check that method is a "token" according to RFC3261
        // since all INVITEm , ACKm , OPTIONSm , BYEm, CANCELm, REGISTERm are tokens as well
        int length = method.length();

        for (int i = 0; i < length; i++) {
            if (!isAllowedTokenCharacter(method.charAt(i))) {
                throw new IllegalArgumentException(method +
                    " is not a valid SIP method name. Contains illegal characters. The character \'" +
                    method.charAt(i) + "\' at index " + i + " is not allowed");
            }
        }
    }

    /**
     * Help method that checks that a character is allowed as part of a rfc 3261 "token".
     *
     * @param c
     * @return Whether it is an allowed character for a token.
     */
    private boolean isAllowedTokenCharacter(char c) {
        // A valid method name is a "token" according to RFC3261
        //
        //  token       =  1*(alphanum / "-" / "." / "!" / "%" / "*"
        //             / "_" / "+" / "`" / "'" / "~" )
        //
        //    alphanum  =  ALPHA / DIGIT
        //   
        //    ALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z
        //
        //    DIGIT          =  %x30-39  ; 0-9
        //    

        // Some pdf versions of rfc3261 allow back quote instead of single quote.
        // This is most probably a conversion issue but the specs are out there.
        // Although we formally should not allow backquote we better do that anyway.
        // We are using unicode literal \u00B4 for backquote (ACUTE ACCENT)
        // since editors might mess things up otherwise.
        return "ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvzyx0123456789-.!%*_+`'\u00B4~".indexOf(c) > -1;
    }

    /**
     * SSA 7.1.2 requestURI, Call-ID, From, To, CSeq and Route headers Only used
     * when
     */
    private SipServletRequest createRequestCommitted(String method) {
        validateSessionState(true);
        if ((getDF() != null) && (getFromTag() != null) &&
                (getToTag() != null) && hasPFieldCSeq()) {
            Address from = (Address) ((AddressImpl) getLocalSide()).clone(true,
                    true);
            Address to = (Address) ((AddressImpl) getOtherside()).clone(true,
                    true);

            if ((from == null) || (to == null)) {
                throw new IllegalStateException();
            }

            URI remoteTarget = getRemoteTarget();

            if (remoteTarget != null) {
                remoteTarget = (URI) remoteTarget.clone();
            }

            SipServletRequestImpl req = new SipServletRequestImpl(method,
                    remoteTarget, SipFactoryImpl.PROTOCOL_LINE);
            req.setDirection(getPFieldType());

            // set To
            Header toHeader = new SingleLineHeader(Header.TO, true);
            toHeader.setAddressValue(to, false);
            req.setHeader(toHeader);

            // set From
            Header fromHeader = new SingleLineHeader(Header.FROM, true);
            fromHeader.setAddressValue(from, false);
            req.setHeader(fromHeader);

            // set Max-Forwards
            Header maxForwardsHeader = new SingleLineHeader(Header.MAX_FORWARDS,
                    false);
            maxForwardsHeader.setValue("70", false);
            req.setHeader(maxForwardsHeader);

            // set CallID
            Header callIDHeader = new SingleLineHeader(Header.CALL_ID, true);
            callIDHeader.setValue(getCallId(), false);
            req.setHeader(callIDHeader);

            // set CSeq
            Header cSeqHeader = new SingleLineHeader(Header.CSEQ, true);
            cSeqHeader.setValue(Integer.toString(incrementAndGetPFieldCSeq()) +
                " " + req.getMethod(), false);
            req.setHeader(cSeqHeader);
            // update new request with session
            req.setSession(this);
            // update it with dialog
            req.setDialog(getDF());
            // subsequent request
            req.setInitial(false);

            List<Layer> layers = LayerHandler.getInstance().getLayers();
            req._applicationStack.addAll(layers);
            req.setRole(getPFieldSipApplicationSession().getName());

            DialogManager.getInstance().addContact(req);

            return req;
        } else {
            throw new IllegalStateException("Not allowed to create a request.");
        }
    }

    private SipServletRequest createRequestInitial(String method) {
        validateSessionState(false);
        if (tmpCallData != null) {
            // OK, so the session is reused.
            // the fact that we have tmpCallData is sufficient proof that this
            // is allowed.
            // To avoid the invalidation of the session by the removal of the
            // dialogs (invalidate earlier in the reset() ) we decouple the
            // caller pathnode from the path.
           
            readyToInvalidate = false;
            if (tmpCallData.getDialogFragmentId() != null) {
                DialogFragment oldDF = getDF(tmpCallData.getDialogFragmentId());
                if (oldDF != null) {
                    oldDF.removeCallerFromPath(getId());
                }
            }
           
            Address from = tmpCallData.getFrom();
            Address to = tmpCallData.getTo();
            int CSeq = tmpCallData.getCSeq()
            if (!hasPFieldCSeq()) {
                setPFieldCSeq(++CSeq);
            } else {
                int tmp = incrementAndGetPFieldCSeq();
                if (tmp <= CSeq) {
                    setPFieldCSeq(++CSeq);
                } else {
                    CSeq = tmp;
                }
            }

            String callId = tmpCallData.getCallId();
            tmpCallData = null;

            if ((from == null) || (to == null)) {
                throw new IllegalStateException();
            }

            URI requestURI = (URI) to.getURI().clone();

            if ("REGISTER".equals(method) && requestURI.isSipURI()) {
                ((SipURI) requestURI).setUser(null);
            }

            SipServletRequestImpl req = new SipServletRequestImpl(method,
                    requestURI, SipFactoryImpl.PROTOCOL_LINE);
            req.setDirection(getPFieldType());

            // set To
            Header toHeader = new SingleLineHeader(Header.TO, true);
            toHeader.setAddressValue(to, false);
            req.setHeader(toHeader);

            // set From
            Header fromHeader = new SingleLineHeader(Header.FROM, true);
            fromHeader.setAddressValue(from, false);
            req.setHeader(fromHeader);

            // set Max-Forwards
            Header maxForwardsHeader = new SingleLineHeader(Header.MAX_FORWARDS,
                    false);
            maxForwardsHeader.setValue("70", false);
            req.setHeader(maxForwardsHeader);

            // set CallID
            Header callIDHeader = new SingleLineHeader(Header.CALL_ID, true);
            callIDHeader.setValue(callId, false);
            req.setHeader(callIDHeader);

            // set CSeq
            Header cSeqHeader = new SingleLineHeader(Header.CSEQ, true);
            cSeqHeader.setValue(Integer.toString(CSeq) + " " + req.getMethod(),
                false);
            req.setHeader(cSeqHeader);

            // update new request with session
            req.setSession(this);

            // update it with a new dialog fragment and dialog set
            setDS(new DialogSet(req.getMethod(), req.getCallId(), req.getFromImpl(),
                    req.getBeKey(), // required just for ssr optimization to locate DF, refer issue 1586
                    SipFactoryImpl.isDialogCreational(req.getMethod())));
            req.setDialog(getDS().getInitialDialogFragment());

            // initial request
            req.setInitial(true);

            List<Layer> layers = LayerHandler.getInstance().getLayers();
            req._applicationStack.addAll(layers);
            req.setRole(getPFieldSipApplicationSession().getName());

            DialogManager.getInstance().addContact(req);

            return req;
        } else {
            throw new IllegalStateException("Not allowed to create a request.");
        }
    }

    public void setHandler(String name) throws ServletException {
        validateSessionState();
        ServletDispatcher servletDispatcher;
        servletDispatcher = getPFieldSipApplicationSession()
                                .getServletDispatcher();

        if (servletDispatcher != null) {
            if ((name == null) || !servletDispatcher.findServlet(name)) {
                throw new ServletException(
                    "Could not found the servlet to set it as a handler.");
            }
        } else {
            if (log.isLoggable(Level.FINE)) {
                log.log(Level.FINE,
                    "Unexpected: Called setHandler, but the servlet dispatcher is null.");
            }
        }

        setPFieldHandler(name);
    }

    public String getHandler() {
        return getPFieldHandler();
    }

    public Object getAttribute(String name) {
        if (name.equals(SipFactoryImpl.REMOTE_TARGET)) {
            return getRemoteTarget();
        }

        if (!isValid()) {
            throw new IllegalStateException("The session is not valid.");
        }

        return getFromPFieldSessionAttribute(name);
    }

    public Enumeration<String> getAttributeNames() {
        if (!isValid()) {
            throw new IllegalStateException("The session is not valid.");
        }

        // Since Map doesn't have Enumeration as interface need to convert via
        // private class EnumerationConverter.
        Collection<String> c = getFromPFieldSessionAttributeNames();

        if (c.equals(Collections.EMPTY_SET)) {
            c = new ArrayList<String>(1);
        } else {
            ArrayList<String> c1 = new ArrayList<String>(c.size() + 1);
            c1.addAll(c);
            c = c1;
        }

        c.add(SipFactoryImpl.REMOTE_TARGET);

        return new EnumerationConverter<String>(c);
    }

    public void setAttribute(String name, Object attribute) {
        if (name.equals(SipFactoryImpl.REMOTE_TARGET)) {
            throw new IllegalStateException("reserved key.");
        }

        if (!isValid()) {
            throw new IllegalStateException("The session is not valid.");
        }

        addToPFieldSessionAttribute(name, attribute);
    }

    public void removeAttribute(String name) {
        if (!isValid()) {
            throw new IllegalStateException("The session is not valid.");
        }

        removeFromPFieldSessionAttribute(name);
    }

    /**
     * @return the to tag for this session.
     */
    public String getToTag() {
        return getTo().getParameter(AddressImpl.TAG_PARAM);
    }

    /**
     * @return true if no to tag has been set in this dialog otherwise false.
     */
    public boolean hasNoToTag() {
        return getDF() == null;
    }

    /**
     * @return the from tag of this session.
     */
    public String getFromTag() {
        return getFrom().getParameter(AddressImpl.TAG_PARAM);
    }

    public synchronized void setType(Type type) {
        setPFieldType(type);

        // if user agent then a cseq counter is needed, otherwise save memory...
        if (!hasPFieldCSeq() &&
                (getPFieldType().equals(Type.Caller) ||
                getPFieldType().equals(Type.Callee))) {
            createPFieldCSeq();
        }
    }

    /**
     * The session is associated with a dialog that must already be established
     * with a proper toTag. If this is the first message retrieving the session
     * the original session is returned otherwise a derived session is created
     * and returned.
     *
     * @param d
     *            the established dialog to associate the session with
     * @return the original or a derived session
     */
    private SipSessionImplBase getOriginalOrDerivedSession(DialogFragment d,
        String toTag) {

        if ((d.getToTag() != null) && (toTag != null) &&
                toTag.equals(d.getToTag())) {
            SipSessionImplBase s = null;
            synchronized (ssLock) {
                if (deriveSessionNotApplicable(toTag, d)) {
                    // return original session
                    s = this;
                    registerSipSession(d, s, toTag);
                } else {
                    DialogSet ds = getDF().getDialogSet();
                    // return derived session
                    s = getSipSessionManager()
                            .createSipSession(ds,
                            getTo(), getApplicationSessionImpl(),
                            getHandler(), getPFieldType());
                    // ...and set toTag of session
                    s.setPFieldDerived(true);
                    registerSipSession(d, s, toTag);
                }
            }
            return s;
        } else {
            throw new IllegalStateException(
                "DialogFragment must have same to-tag as message.");
        }
    }

    /**
     * Fix for issue 935.
     *
     * As per section 6.2.3 of the Sip Servlet API, SIP Session gets created only
     * when a second 2xx respone or a second 1xx response (except 100) arrives.
     * This happens in case of a forking proxy and the responses will have a different
     * to tag than the one set in the original session.
     *
     * In case of spiralling, the fragment id will be different. So, in that case,
     * even if totags are same, there should be a different session.
     *
     * Basically, if totags are different or when the fragment-ids are different
     * create derived sipsessions. Otherwise use the orginal sipsession.
     */
    private boolean deriveSessionNotApplicable(String toTag, DialogFragment df) {
        return    getDF() == null
               || getToTag() == null
               ||  (getFragmentId().equals(df.getFragmentId())
                    && getToTag().equals(toTag));
    }

    private void registerSipSession(DialogFragment df, SipSessionImplBase s, String toTag) {
        // set the dialog fragment in the session
        s.setDF(df);
        // register the session in the dialog fragment
        s.getDF().registerSession(s);
   
        AddressImpl toAddress = (AddressImpl) s.getTo();
        toAddress.setReadOnly(false);
        toAddress.setParameter(AddressImpl.TAG_PARAM, toTag);
        toAddress.setReadOnly(true);
    }

    public SipSessionBase getOriginalOrDerivedSessionAndRegisterDialog(
        SipServletRequestImpl req, DialogFragment d) {
        // need to swap to and from for request
        String toTag = req.getFromImpl().getParameter(AddressImpl.TAG_PARAM);

        return getOriginalOrDerivedSessionAndRegisterDialog(req, d, toTag);
    }

    public SipSessionBase getOriginalOrDerivedSessionAndRegisterDialog(
        SipServletResponseImpl resp, DialogFragment d) {
        String toTag = resp.getToImpl().getParameter(AddressImpl.TAG_PARAM);

        return getOriginalOrDerivedSessionAndRegisterDialog(resp, d, toTag);
    }

    /**
     * Tries to register the DialogFragment with the toTag of the message. If
     * successful a original or derived session is returned. If unsuccessful a
     * confirmed DialogFragment with the toTag of the message is searched for.
     * If found the message is updated with this DialogFragment and a original
     * or derived session is returned. If no DialogFragment is found the
     * original DialogFragment is cloned and it is recursively sent to this
     * method.
     *
     * @param m
     *            the message with the toTag to be set in the DialogFragment
     * @param d
     *            the DialogFragment to register the session in.
     * @return an original or derived session
     */
    private SipSessionBase getOriginalOrDerivedSessionAndRegisterDialog(
        SipServletMessageImpl m, DialogFragment d, String toTag) {
        if (toTag != null) {
            SipSessionBase s = null;
            boolean isDialogCreational = SipFactoryImpl.isDialogCreational(m.getMethod());
            boolean success = d.tryToSetToTagAndRegisterDialog(toTag,
                    isDialogCreational);

            if (success) {
                s = getOriginalOrDerivedSession(d, toTag);
            } else {
                DialogFragment searchedDialog = getDS()
                                                    .searchForDialog(toTag,
                        m.getFragmentId());

                if (searchedDialog != null) {
                    m.setDialog(searchedDialog);
                    s = getOriginalOrDerivedSession(searchedDialog, toTag);
                } else {
                    // clone the dialog
                    DialogFragment clone = (DialogFragment) d.clone();
                    // FIXME its a recursive call and a break condition must be
                    // added
                    s = getOriginalOrDerivedSessionAndRegisterDialog(m, clone,
                            toTag);
                }
            }

            return s;
        } else {
            throw new IllegalStateException(
                "DialogFragment must have same to-tag as message.");
        }
    }

    public void swapLocalRemote() {
        setPFieldSwapLocalRemote(true);
    }

    public boolean setUpdateOngoing() {
        if (getPFieldUpdateOngoing()) {
            return false;
        } else {
            setPFieldUpdateOngoing(true);

            return true;
        }
    }

    public void resetUpdateOngoing() {
        setPFieldUpdateOngoing(false);
    }

    // set to true the flag which specify that there is an 1xx Reliable
    // response ongoing and if an sdp is included (CR38 PRACK)
    //
    // @param sdp=true if an sdp is included in the 1xx rel response
    // @return true if there was no pending 1xx reliable response
    // false if there was pending 1xx reliable response
    public synchronized boolean set1xxReliableOngoing(boolean sdp) {
        if (is1xxReliableOngoing()) {
            return false;
        }

        setPField1xxReliableSDP(sdp);
        setPField1xxReliableOngoing(true);

        return true;
    }

    public void reset1xxReliable() {
        setPField1xxReliableOngoing(false);
        setPField1xxReliableSDP(false);
    }

    public String getFragmentId() {
        return (getDF() != null) ? getDF().getFragmentId()
                                 : DialogFragment.BAD_FRAGMENT_ID;
    }

    public State getState() {
        return getState(true);
    }

    private State getState(boolean validateSession) {
        if(validateSession) {
            validateSessionState();
        }
        synchronized (ssLock) {
            return getPFieldSessionState();
        }
    }

    private void setState(State state) {
        synchronized (ssLock) {
            setPFieldSessionState(state);
        }
    }

    /**
     * JSR 289, 6.2.2.2 When in the INITIAL SipSession State.
     *
     * When a UAC transitions from the early state to the initial state, the
     * dialog state maintained in the SipSession is updated as follows (see RFC
     * 3261 for the definition of these dialog state components):
     *
     * the remote target is reset to the remote URI the remote tag component is
     * cleared the remote sequence number is cleared (e.g. set to -1) the route
     * set is cleared the �secure� flag is set to false
     *
     * As a consequence of these rules, requests generated in the same
     * SipSession have the following characteristics: they all have the same
     * Call-ID they all have the same From header field value including the same
     * non-empty tag the CSeq of requests generated in the same SipSession will
     * monotonically increase by one for each request created, regardless of the
     * state of the SipSession all requests generated in the same SipSession in
     * the initial state have the same To header field value which will not have
     * a tag parameter SipSession objects in the initial state have no route
     * set, the remote sequence number is undefined, the �secure� flag is false,
     * and the remote target URI is the URI of the To header field value.
     */
    private void reset(SipServletResponseImpl resp) {
        // Eventually move all the reset() to DS.
        // 1. update all FSMs to only add DF when CONFIRMED (not 3xx-6xx)
        // DF should only call register for 2xx (DFM.registerDialogFragment()).
        // 2. DS.invalidate() and DialogFragment.invalidate()
        // since it's call setup, DS.invalidate is OK.

        if (getOtherside() != null) {
           // remove to-tag
           Address to = (Address) ((AddressImpl) getOtherside()).clone(false, true);
           setPFieldTo(to);
        }
       
        int cseq = resp.getCSeqNumber();
       
        if (cseq < getCSeq()) {
            cseq = getCSeq();
        }

        tmpCallData = new TmpCallData(getCallId(), getFrom(), getTo(), cseq, getPFieldDialogFragmentId());
       
        for (Iterator<DialogFragment> it = getDS().getDialogs(); it.hasNext();) {
            it.next().invalidate(false);
        }

        // reset dialog set and dialog fragment
        setDF(null);
        setPFieldDialogFragmentId(null);
        // also clear the cached reference.
        // XXX better to do this as a direct result of setting the fid to null
        dialogFragment = null;

        // remove remote target
        setRemoteTarget(null);
    }

    /**
     * Implements the logic for JSR289 <code>SipSession.State</code> This
     * method is called on every response and may update the state of the
     * SipSession.<br>
     *
     * JSR 289 6.2.1 Figure 6. The SipSession state machine.
     *
     * <pre>
     * INITIAL ---&gt; 1xx EARLY
     * INITIAL ---&gt; 2xx CONFIRMED
     * EARLY ---&gt; 3xx-6xx INITIAL
     * EARLY ---&gt; 2xx CONFIRMED
     * </pre>
     *
     * Following section from RFC3265, 3.3.4. Dialog creation and termination is
     * not supported: a dialog created with an INVITE does not necessarily
     * terminate upon receipt of a BYE. Similarly, in the case that several
     * subscriptions are associated with a single dialog, the dialog does not
     * terminate until all the subscriptions in it are destroyed.
     *
     * @param resp
     *            Current response.
     * @param type
     *            type of PathNode
     *
     */
    public void updateSipSessionState(SipServletResponse resp,
        PathNode.Type type) {
        String method = resp.getRequest().getMethod();
        int status = resp.getStatus();
                      
        // From JSR 289 "6.2.1 Relationship to SIP Dialogs"
        // 2. The exception to the general rule is that it does not apply to
        // requests (e.g. BYE, CANCEL) that are dialog terminating
        // NOTE: Ignore CANCEL because if it is canceled a 487 will eventually
        // arrive and it will be handled correctly for UAC, UAS and Proxy.
        if (method.equals("BYE")) {
            if (status != 401 && status != 407) {
                setState(State.TERMINATED);
                markReadyToInvalidate();
                if (log.isLoggable(Level.FINER)) {
                    log.log(Level.FINER, this + " transitioned to TERMINATED " +
                        "state while processing " + status + " for " + method);
                }
            }
        } else if (method.equals("NOTIFY")) {
            // From RFC 3265:
            // A subscription is destroyed when a notifier sends a NOTIFY
            // request
            // with a "Subscription-State" of "terminated".
            String subState = resp.getRequest().getHeader("Subscription-State");

            if (subState != null) {
                if (subState.toLowerCase().startsWith("terminated")) {
                    DialogFragment df = getDF();
                    if (df == null || !df.isInviteDialog()) { // check if we are in SUBSCRIBE/REFER/NOTIFY dialog.
                        setState(State.TERMINATED);
                        markReadyToInvalidate();
                        if (log.isLoggable(Level.FINER)) {
                            log.log(Level.FINER, this + " transitioned to " +
                                    "TERMINATED state while processing " +
                                    status + " for " + method);
                        }
                    }
                }
            }
        } else {
            switch (getState(false)) {
            case INITIAL:

                // 3. If the servlet acts as a UAC and sends a dialog creating
                // request,
                // then the SipSession state tracks directly the SIP dialog
                // state.
                // 4. If the servlet acts as a UAS and receives a dialog
                // creating
                // request then the SipSession state directly tracks the SIP
                // dialog state.
                if (((status >= 100) && (status <= 199)) &&
                        SipFactoryImpl.isDialogCreational(method)) {
                    setState(State.EARLY);
                } else if (((status >= 200) && (status <= 299)) &&
                        SipFactoryImpl.isDialogCreational(method)) {
                    setState(State.CONFIRMED);
                    if (type == Type.Proxy) {
                        ProxyBranch pb = resp.getProxyBranch();
                        if (pb != null) {
                            if (!pb.getRecordRoute()) {
                                markReadyToInvalidate();
                            }
                        } else if (!resp.getProxy().getRecordRoute()) {
                            markReadyToInvalidate();
                        }
                        if (log.isLoggable(Level.FINER)) {
                            log.log(Level.FINER, this + " transitioned to " +
                                    "CONFIRMED state while processing " +
                                    status + " for " + method +
                                    " in case of non-record-routing proxy");
                        }
                    }
                // 4. Unlike a UAC, a non-2XX final response sent by the UAS in
                // the EARLY or INITIAL states causes the SipSession state to go
                // directly to the TERMINATED state.
                } else if ((type == Type.Callee) &&
                        ((status >= 300) && (status <= 699)) &&
                        SipFactoryImpl.isDialogCreational(method)) {
                    setState(State.TERMINATED);
                    markReadyToInvalidate();
                    if (log.isLoggable(Level.FINER)) {
                        log.log(Level.FINER, this + " transitioned to " +
                                "TERMINATED state while processing " +
                                status + " for " + method);
                    }
                } else if((type == Type.Caller) &&
                        ((status >= 300) && (status <= 699)) &&
                        SipFactoryImpl.isDialogCreational(method)) {
                    /**
                     * Need to mark for readyToInvalidate (refer issue 1814).
                     *
                     * Note that there is no state change, and this SipSession
                     * will be reset at the end of this method. If the application
                     * re-uses the reset SipSession to create new initial request, then
                     * this SipSession will regain life, so it will no longer be
                     * in readyToInvalidate state (see createRequest method).
                     */
                    markReadyToInvalidate();
                }

                break;

            case EARLY:

                // 3. If the servlet acts as a UAC and sends a dialog creating
                // request,
                // then the SipSession state tracks directly the SIP dialog
                // state
                // except that non-2XX final responses received in the EARLY or
                // INITIAL states cause the SipSession state to return to the
                // INITIAL state rather than going to TERMINATED.
                // 5. If the servlet acts as a proxy for a dialog creating
                // request
                // then the SipSession state tracks the SIP dialog state except
                // that
                // non-2XX final responses received from downstream in the EARLY
                // or
                // INITIAL states cause the SipSession state to return to
                // INITIAL
                // rather than going to TERMINATED.
                if (((type == Type.Caller) || (type == Type.Proxy)) &&
                        ((status >= 300) && (status <= 699)) &&
                        SipFactoryImpl.isDialogCreational(method)) {

                    setState(State.INITIAL);
                   
                    //TODO. In case of proxy, this invalidation should happen
                    //only after dialogfragments for the branches are fully
                    //copied for sequential branches.
                    if (type != Type.Proxy) {
                        markReadyToInvalidate();
                    }
                   
                    if (log.isLoggable(Level.FINER)) {
                        log.log(Level.FINER, this + " transitioned to INITIAL" +
                                " state while processing " + status +
                                " for " + method + " in case of UAC");
                    }
                }
                // 4. If the servlet acts as a UAS and receives a dialog
                // creating
                // request, then the SipSession state directly tracks the SIP
                // dialog
                // state. Unlike a UAC, a non-2XX final response sent by the UAS
                // in
                // the EARLY or INITIAL states causes the SipSession state to go
                // directly to the TERMINATED state.
                else if ((type == Type.Callee) &&
                        ((status >= 300) && (status <= 699)) &&
                        SipFactoryImpl.isDialogCreational(method)) {
                    setState(State.TERMINATED);
                    markReadyToInvalidate();
                    if (log.isLoggable(Level.FINER)) {
                        log.log(Level.FINER, this + " transitioned to " +
                                "TERMINATED state while processing " +
                                status + " for " + method);
                    }
                } else if (((status >= 200) && (status <= 299)) &&
                        SipFactoryImpl.isDialogCreational(method)) {
                    setState(State.CONFIRMED);
                    if ((type == Type.Proxy) && !resp.getProxy().getRecordRoute()) {
                        List<ProxyBranch> branches = resp.getProxy().getProxyBranches();
                        boolean isRecordRouted = false;
                        for (ProxyBranch branch : branches) {
                            if (branch.getRecordRoute()) {
                                isRecordRouted = true;                               
                                break;
                            }
                        }
                        if (!isRecordRouted) {
                            markReadyToInvalidate();
                        }

                        if (log.isLoggable(Level.FINER)) {
                            log.log(Level.FINER, this + " transitioned to " +
                                    "CONFIRMED state while processing " +
                                    status + " for " + method +
                                    " in case of non-record-routing proxy");
                        }
                    }
                }

                break;

            case CONFIRMED:
                break;

            case TERMINATED:
                break;
            }
            if (type == Type.Caller && (getState() == State.INITIAL)){               
                reset((SipServletResponseImpl) resp);
            }
        }     
       
        /*
         * Fix for sailfin issue 1615:
         *
         * The session associated with the non dialog creating && outside
         * the dialog requests should be marked for invalidation. For example:
         * MESSAGE, OPTIONS, PUBLISH, REGISTER requests fall into this catogory
         * if they are outside the dialog.
         *
         * The Invalidate When Ready machanism for this type of requests
         * is not defined in the JSR 289 specification. So, it is an
         * additional feature.
         *
         * Also, with this change the session which is reset also gets marked
         * for IWR. But if the application reuses the reset session to create
         * new requests then the IWR is unmarked in the createRequestInitial().
         */
        doNonDialogIWR(resp, type);

    }

    private void doNonDialogIWR(SipServletResponse resp,
                                PathNode.Type type) {
        String method = resp.getRequest().getMethod();
        if (!SipFactoryImpl.isDialogCreational(method) && getDF() == null) {
            if (type == Type.Proxy) {
                ProxyBranch pb = resp.getProxyBranch();
                if (pb != null) {
                    ProxyImpl.ProxyFacade p = ((ProxyBranchImpl) pb).getProxy();
                    if (p != null && p.hasBestRepsonse()) {
                        markReadyToInvalidate();
                    }
                } else {
                    markReadyToInvalidate();
                }
            } else {
                markReadyToInvalidate();
            }
        }
    }
   
    /**
     * Implements the logic for JSR289 <code>SipSession.State</code> This
     * method is called on every request and may update the state of the
     * SipSession.<br>
     *
     * @param req
     */
    public void updateSipSessionState(SipServletRequest req, PathNode.Type type) {
        String method = req.getMethod();

        // From JSR 289 "6.2.1 Relationship to SIP Dialogs"
        // 2. The exception to the general rule is that it does not apply to
        // requests (e.g. BYE, CANCEL) that are dialog terminating
        //  200 OK/BYE should transition the state to TERMINATED
        if (method.equals("NOTIFY")) {
            // From RFC 3265:
            // A subscription is destroyed when a notifier sends a NOTIFY
            // request
            // with a "Subscription-State" of "terminated".
            String subState = req.getHeader("Subscription-State");

            if (subState != null) {
                if (subState.toLowerCase().startsWith("terminated")) {
                    DialogFragment df = getDF();
                    if (df == null || !df.isInviteDialog()) { // check if we are in SUBSCRIBE/REFER/NOTIFY dialog.
                        setState(State.TERMINATED);
                        if (log.isLoggable(Level.FINER)) {
                            log.log(Level.FINER, this + " transitioned to " +
                                    "TERMINATED state while processing " +
                                    method + " request.");
                        }
                    }
                }
            }
        }
    }

    /**
     * The implementation is intentially made simple and does not correctly
     * track every single transaction. The purpose is to make it clear when to
     * allow invalidation of sessions.
     */
    public boolean isOngoingTransaction() {
        return ((getState() == State.EARLY) || (getState() == State.CONFIRMED))
        ? true : false;
    }

    /**
     * Not doing anything inside this method
     */
    public void setOutboundInterface(InetAddress address) {
        validateSessionState();
        if (address == null)
            throw new NullPointerException("Argument address is null");
        this.oi = new OutboundInterface(address);
    }

    /**
     * Not doing anything inside this method
     */
    public void setOutboundInterface(InetSocketAddress address) {
        validateSessionState();
        if (address == null)
            throw new NullPointerException("Argument address is null");
        this.oi = new OutboundInterface(address);
    }

    public OutboundInterface getOutboundInterface() {
        return this.oi;
    }

    /**
     * get this session locked for foreground if the session is found to be
     * presently background locked; retry logic in a time-decay polling loop
     * waits for background lock to clear after 6 attempts (12.6 seconds) it
     * unlocks the session and acquires the foreground lock
     */
    public boolean lockForegroundWithRetry() {
        boolean result = false;

        long pollTime = 200L;
        int tryNumber = 0;
        int maxNumberOfRetries = 7;
        boolean keepTrying = true;

        // try to lock up to numTries (i.e. 7) times
        // poll and wait starting with 200 ms
        while (keepTrying) {
            boolean lockResult = lockForeground();

            if (lockResult) {
                keepTrying = false;
                result = true;

                break;
            }

            tryNumber++;

            if (tryNumber < maxNumberOfRetries) {
                pollTime = pollTime * 2L;

                try {
                    Thread.sleep(pollTime);
                } catch (InterruptedException e) {
                    ;
                }
            } else {
                // unlock the background so we can take over
                // FIXME: need to log warning for this situation
                unlockBackground();
            }
        }

        return result;
    }

    /**
     * return whether this session is currently foreground locked
     */
    public synchronized boolean isForegroundLocked() {
        return sessionLock.isForegroundLocked();
    }

    /**
     * lock the session for foreground returns true if successful; false if
     * unsuccessful
     */
    public synchronized boolean lockBackground() {
        return sessionLock.lockBackground();
    }

    /**
     * lock the session for background returns true if successful; false if
     * unsuccessful
     */
    public synchronized boolean lockForeground() {
        return sessionLock.lockForeground();
    }

    /**
     * unlock the session completely irregardless of whether it was foreground
     * or background locked
     */
    public synchronized void unlockForegroundCompletely() {
        sessionLock.unlockForegroundCompletely();
    }

    /**
     * unlock the session from foreground
     */
    public synchronized void unlockForeground() {
        sessionLock.unlockForeground();
    }

    /**
     * unlock the session from background
     */
    public synchronized void unlockBackground() {
        sessionLock.unlockBackground();
    }

    /**
     * return the Session lock
     */
    public SessionLock getSessionLock() {
        return sessionLock;
    }

    /**
     * return the Session lock
     */
    public Object getSipSessionObjectLock() {
        return ssLock;
    }

    /**
     * Returns true if this SipSession is replicable, false otherwise.
     *
     * @return true if this SipSession is replicable, false otherwise
     */
    public boolean isReplicable() {
        return getPFieldSipApplicationSession().isReplicable();
    }

    public URI getRemoteTarget() {
        return getPFieldRemoteTarget();
    }

    public void setRemoteTarget(URI contact) {
        setPFieldRemoteTarget(contact);
    }

    // Used by ApplicationDispatcher to set this before invoking of servlets
    public void setRegion(SipApplicationRoutingRegion region) {
        setPFieldRoutingRegion(region);
    }

    public SipApplicationRoutingRegion getRegion() {
        return getPFieldRoutingRegion();
    }

    // Used by ApplicationDispatcher to set this before invoking of servlets
    public void setSubscriberURI(URI subscriberURI) {
        setPFieldSubscriberURI(subscriberURI);
    }

    public URI getSubscriberURI() {
        return getPFieldSubscriberURI();
    }

    public ServletContext getServletContext() {
        SipSessionManager sessMgr = 
            getSipSessionManager();
        ConvergedContext ctxt = getSipSessionManager().getContext();
        return ctxt.getServletContext();
    }

    // JSR289
    public String getLinkedSipSessionId() {
        return getPFieldLinkedSipSessionId();
    }

    public void setLinkedSipSessionId(String id) {
        setPFieldLinkedSipSessionId(id);
    }
   
    // JSR 289 15.11.4, point 5.
    public String getLinkedCorrespondingSipSessionId(String header) {
        return getPFieldCorrespondingSipSessionId(header);
    }

    public void setLinkedCorrespondingSipSessionId(String id, String header) {
        setPFieldCorrespondingSipSessionId(id, header);
    }

    public SipSessionManager getSipSessionManager() {
        return getPFieldSipSessionManagerField();
    }

    public Address getTo() {
        if (tmpCallData != null) {
            return tmpCallData.getTo();
        }

        return getPFieldTo();
    }

    public boolean isDerived() {
        return getPFieldDerived();
    }

    public boolean is1xxReliableOngoing() {
        return getPField1xxReliableOngoing();
    }

    public boolean is1xxReliableSDP() {
        return getPField1xxReliableSDP();
    }

    public synchronized List<SipServletMessage> getPendingMessages(UAMode mode) {
        return pendingMessages != null ? pendingMessages.get(mode) : new ArrayList<SipServletMessage>(2);
    }
   
    public synchronized void addPendingMessage(SipServletMessageImpl m, UAMode mode) {
      if (pendingMessages == null) {
        throw new IllegalStateException("B2buaHelper not initialized properly");
      }
     
      pendingMessages.addAndPurge(m, mode);
    }
   
    public synchronized boolean isB2buaHelper() {
      return pendingMessages != null;
    }
   
    public synchronized void createPendingMessageHelper() {
      if (pendingMessages == null) {
        pendingMessages = new B2buaHelperPendingMessages();
      }
    }
   
    protected abstract void notifySessionDestroyed();

    /**
     * @serialData first field is a short and represents the serializedFormVersion.<br><br>
     * <h3>Data layout for serializedFormVersion = 1 follows</h3>
     *
     * <li>field is a <b>Boolean</b> and represents isValid field</li>
     * <li>field is a <b>Boolean</b> and represents invalidateWhenReady field</li>
     * <li>field is a <b>Boolean</b> and represents readyToInvalidate field</li>
     *
     * <h3>Data layout for serializedFormVersion = 2 follows</h3>
     *
     * <li>field is a <b>Boolean</b> and represents isValid field</li>
     * <li>field is a <b>Boolean</b> and represents invalidateWhenReady field</li>
     * <li>field is a <b>Boolean</b> and represents readyToInvalidate field</li>
     * <li>field is a <b>Boolean</b> and represents whether the session is a b2buaHelper or not</li>
     *
     * @param in the stream to read the object members
     *
     * @throws IOException is thrown when unsupported version is detected
     * @throws ClassNotFoundException
     */
    public void readExternal(ObjectInput in)
            throws IOException, ClassNotFoundException {
        short readSerializedFormVersion = in.readShort();
        switch (readSerializedFormVersion) {
            case 1:
                readExternalFormVersion1(in);
                break;
            case 2:
                readExternalFormVersion2(in);
                break;
            default:
                throw new IOException("Unable to deserialize into "
                        + this.getClass().getName()
                        + " with serialVersionUID = " + serialVersionUID
                        + " due to unknown serializedFormVersion of "
                        + readSerializedFormVersion);

        }
    }

    private void readExternalFormVersion1(ObjectInput in)
            throws IOException, ClassNotFoundException {
        // NEVER change the body of this method. This method takes
        // care of reading the serialization stream of a released product.
        isValid = new AtomicBoolean(in.readBoolean());
        invalidateWhenReady = in.readBoolean();
        readyToInvalidate = in.readBoolean();
        ssLock = new Object();
        sessionLock = new SessionLock();
    }

    private void readExternalFormVersion2(ObjectInput in)
            throws IOException, ClassNotFoundException {
        // If serialization fields of serializedFormVersion1 are removed/shuffed
        // in version2 then deserialization should be done independently without
        // calling readExternalFormVersion1(in).
        readExternalFormVersion1(in);
        boolean isb2buaHelper = in.readBoolean();
        if(isb2buaHelper) {
            pendingMessages = new B2buaHelperPendingMessages();
        }
    }

    /**
     * @serialData See serialized form version
     * #serializedFormVersion in #readExternal(ObjectInput in)
     *
     * @param oos the stream to write the object members
     *
     * @throws IOException
     */
    public void writeExternal(ObjectOutput out)
            throws IOException {
        out.writeShort(serializedFormVersion);
        out.writeBoolean(isValid.get());
        out.writeBoolean(invalidateWhenReady);
        out.writeBoolean(readyToInvalidate);
        out.writeBoolean(isB2buaHelper());
    }
   
    // --- Persistable fields (PField) ---
    protected abstract String getPFieldHandler();

    protected abstract void setPFieldHandler(String name);

    protected abstract void setPFieldLinkedSipSessionId(String id);

    protected abstract String getPFieldLinkedSipSessionId();
   
   
    protected abstract void
    setPFieldCorrespondingSipSessionId(String id, String header);
   
    protected abstract String getPFieldCorrespondingSipSessionId(String header);   

    protected abstract void setPFieldSubscriberURI(URI subscriberURI);

    protected abstract URI getPFieldSubscriberURI();

    protected abstract URI getPFieldRemoteTarget();

    protected abstract void setPFieldRemoteTarget(URI contact);

    protected abstract Object getFromPFieldSessionAttribute(String name);

    protected abstract void addToPFieldSessionAttribute(String name,
        Object value);

    protected abstract Collection<String> getFromPFieldSessionAttributeNames();

    protected abstract void removeFromPFieldSessionAttribute(String name);

    protected abstract SipApplicationSessionImpl getPFieldSipApplicationSession();

    protected abstract void setPFieldSipApplicationSession(
        SipApplicationSessionImpl sipApplicationSession);

    protected abstract long getPFieldCreationDate();

    protected abstract long getPFieldLastAccessedTime();

    protected abstract void setPFieldLastAccessedTime(long lastAccessedTime);

    protected abstract long getPFieldCurrentAccessedTime();

    protected abstract void setPFieldCurrentAccessedTime(
        long currentAccessedTime);

    protected abstract int incrementAndGetPFieldCSeq();

    protected abstract boolean hasPFieldCSeq();

    protected abstract void createPFieldCSeq();
   
    protected abstract void setPFieldCSeq(int cseq);

    protected abstract void setPField1xxReliableSDP(boolean is1xxReliableSDP);

    protected abstract boolean getPField1xxReliableSDP();

    protected abstract boolean getPField1xxReliableOngoing();

    protected abstract void setPField1xxReliableOngoing(
        boolean is1xxReliableOngoing);

    protected abstract boolean getPFieldUpdateOngoing();

    protected abstract void setPFieldUpdateOngoing(boolean updateOngoing);

    protected abstract void setPFieldRoutingRegion(SipApplicationRoutingRegion routingRegion);

    protected abstract SipApplicationRoutingRegion getPFieldRoutingRegion();

    protected abstract boolean getPFieldSwapLocalRemote();

    protected abstract void setPFieldSwapLocalRemote(boolean swapLocalRemote);

    protected abstract Type getPFieldType();

    protected abstract void setPFieldType(Type type);

    protected abstract SipSessionManager getPFieldSipSessionManagerField();

    protected abstract void setPFieldTo(Address to);

    protected abstract Address getPFieldTo();

    protected abstract void setPFieldDerived(boolean isDerived);

    protected abstract boolean getPFieldDerived();

    protected abstract String getPFieldId();

    protected abstract String getPFieldDialogFragmentId();

    protected abstract void setPFieldDialogFragmentId(String dialogFragmentId);

    protected abstract State getPFieldSessionState();

    protected abstract void setPFieldSessionState(State state);

    protected abstract long getPFieldExpirationTime();

    protected abstract void setPFieldExpirationTime(long expirationTime);

    protected abstract List<SipSessionListener> getSipSessionListeners();

    /**
     * Temporary call data saved for SSA1.1, 6.2.1 Relationship to SIP Dialogs
     *
     * The INITIAL state is introduced to allow a UAC to generate multiple
     * requests to be generated with the same Call-ID, From (including tag),
     * and To (excluding tag), and within the same CSeq space.
     *
     * @author ehsroha
     */
    private class TmpCallData {
        private final String callID;
        private final Address from;
        private final Address to;
        private final int CSeq;
        private final String dfId;

        public TmpCallData(String callID, Address from, Address to, int seq, String dfId) {
            this.callID = callID;
            this.from = from;
            this.to = to;
            CSeq = seq;
            this.dfId = dfId;
           
        }

        public String getCallId() {
            return callID;
        }

        public Address getFrom() {
            return from;
        }

        public Address getTo() {
            return to;
        }

        public int getCSeq() {           
            return CSeq;
        }
       
        public String getDialogFragmentId() {
            return dfId;
        }
    }
}
TOP

Related Classes of com.ericsson.ssa.sip.SipSessionImplBase$TmpCallData

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.