Package com.ericsson.ssa.sip

Source Code of com.ericsson.ssa.sip.DialogManager

/*
* 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.annotations.Configuration;
import com.ericsson.ssa.config.annotations.UsagePolicy;
import com.ericsson.ssa.container.SipBindingCtx;
import com.ericsson.ssa.container.SipBindingListener;
import com.ericsson.ssa.container.SipBindingResolver;
import com.ericsson.ssa.container.reporter.ReporterResolver;
import com.ericsson.ssa.container.reporter.Reporter;
import com.ericsson.ssa.container.startup.SipMonitoring;
import com.ericsson.ssa.sip.PathNode.Type;
import com.ericsson.ssa.sip.dialog.DialogCleaner;
import com.ericsson.ssa.sip.dialog.DialogLifeCycle;
import com.ericsson.ssa.sip.dns.SipTransports;
import com.ericsson.ssa.sip.dns.TargetTuple;
import com.ericsson.ssa.sip.persistence.IncompleteDialogException;
import com.ericsson.ssa.sip.transaction.TransactionManager;

import org.jvnet.glassfish.comms.util.LogUtil;

import java.util.Iterator;
import java.util.ListIterator;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.sip.Address;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.URI;


/**
* A Singleton that stores SipSessions and dialogs and fetches them upon
* request.
*
* @author ehsroha
* @reviewed ejoelbi 2007-jan-17
*/
public class DialogManager implements Layer {
    private static final Logger m_Log = LogUtil.SIP_LOGGER.getLogger();
    private static DialogManager m_Instance = null;
    private static Class m_FactoryClass = null;

    // needed for dialog creational NOTIFY request
    private Layer m_NextLayer = null;
    private SipURIImpl m_URI = null;
    private SipURIImpl m_SURI = null;
    private boolean defaultTCPTransport = false;
    private AtomicLong m_EasFailedSipDialogs = new AtomicLong();
    private AtomicLong m_EasSuccessfulSipDialogs = new AtomicLong();
    private Reporter _reporter;
    private volatile boolean strictFid = false;

    /**
     * private since its is a Singleton
     */
    protected DialogManager() {
    }

    public void setReporters(String reporters) {
        _reporter = ReporterResolver.getInstance().getReporter(reporters);
    }

    public Reporter getReporter() {
        return _reporter;
    }

    /**
     * Initializes this Singleton via the double checked locking pattern to
     * minimize performance penelty since its accessed often.
     */
    public void start() {
        establishContactURIs();

        SipBindingResolver.instance().registerSipBindingListener(new SipBindingListener() {
                public void newSipBindingCtxAvaliable(String context) {
                    //ignore
                }

                public void sipBindingCtxUpdated(String context) {
                    //ignore
                }

                public void publicSipBindingCtxUpdated() {
                    establishContactURIs();
                }

                public void sipBindingCtxRemoved(String context) {
                    //ignore
                }
            });

        if (m_Log.isLoggable(Level.FINE)) {
            m_Log.log(Level.FINE, "Started DialogManager");
        }
    }

    public SipURIImpl getVipSipUri() {
        return (SipURIImpl) m_URI.clone();
    }

    public SipURIImpl getVipSipsUri() {
        return (SipURIImpl) m_SURI.clone();
    }

    private void establishContactURIs() {
        //TODO Deal with failures and lease expired.
        // Build a Contact URI and a header
        m_URI = null;
        m_SURI = null;

        SipURIImpl[] ifs = SipBindingResolver.getInterfaces();

        //Find first UDP || TCP ||TLS
        for (int i = 0; i < ifs.length; i++) {
            if ((m_SURI == null) && ifs[i].isSecure()) {
                m_SURI = ifs[i];
            } else if ((m_URI == null) && defaultTCPTransport &&
                    ifs[i].getTransportParam().equalsIgnoreCase("TCP")) {
                m_URI = ifs[i];
            } else if ((m_URI == null) && !defaultTCPTransport &&
                    ifs[i].getTransportParam().equalsIgnoreCase("UDP")) {
                m_URI = (SipURIImpl) ifs[i].clone();
                m_URI.removeParameter("transport");
            }
        }

        if (m_Log.isLoggable(Level.FINE)) {
            m_Log.log(Level.FINE,
                "DialogManager started or reinitialized: sip URI: " + m_URI +
                ", sips URI: " + m_SURI +
                (defaultTCPTransport ? ", using TCP as default transport." : "."));
        }
    }

    public void addContact(SipServletRequestImpl req) {
        //12.1.2 If Req URI contains sips or top route then contact must be sips
        //UAC behaviour
        SipURIImpl uri = getSipOrSipsURI(req);

        Header contactHeader = new MultiLineHeader(Header.CONTACT, true);
        contactHeader.setValue("<" + uri.toString() + ">", false);
        req.addHeader(contactHeader);
        req.indicateContact();
    }

    private boolean shouldShowFragmentId(SipServletMessageImpl msg) {
        return true;
    }

    public void addContact(SipServletResponseImpl resp) {
        //12.1.1 Contact should be sips if:
        //Req uri of the request was sips or
        //Top most RR was sips and if none present the contact was sips
        //UAS behaviour
        DialogFragment df = resp.getDialog();

        URI nextTarget = null;

        if ((resp.getRequestImpl() != null) &&
                (resp.getRequestImpl().getRequestURI() != null) &&
                resp.getRequestImpl().getRequestURI().isSipURI()) {
            nextTarget = resp.getRequestImpl().getRequestURI();
        }

        if ((nextTarget != null) && (!((SipURI) nextTarget).isSecure())) {
            Header rr = resp.getRawHeader(Header.RECORD_ROUTE);

            try {
                if ((rr != null) && rr.getAddressValue().getURI().isSipURI()) {
                    nextTarget = rr.getAddressValue().getURI();
                } else { //Get the other

                    Header c = resp.getRequestImpl().getRawHeader(Header.CONTACT);

                    if ((c != null) && (c.getAddressValue() != null)) {
                        nextTarget = c.getAddressValue().getURI();
                    }
                }
            } catch (ServletParseException spe) {
                if (m_Log.isLoggable(Level.FINE)) {
                    m_Log.log(Level.FINE, "Failed eval 12.1.1 rules", spe);
                }
            }
        }

        SipURIImpl uri = null;

        if (nextTarget != null && nextTarget.isSipURI())
        {
            SipURI targetUri = (SipURI) nextTarget;
            boolean isTransportParamTls =
                    targetUri.getTransportParam() != null &&
                            targetUri.getTransportParam().equalsIgnoreCase("tls");

            if (targetUri.isSecure() || isTransportParamTls)
            {
                uri = (SipURIImpl) m_SURI.clone();

                // Do not use SIPS URI scheme if transport=TLS
                if (isTransportParamTls)
                {
                    uri.setSecure(false);
                }
            }
            else
            {
                uri = (SipURIImpl) m_URI.clone();
            }
        } else {
            uri = (SipURIImpl) m_URI.clone();
        }

        if ((df != null) && shouldShowFragmentId(resp)) {
            uri.setParameter(SipURIImpl.FRAGID_PARAM, df.getFragmentId());
        }

        Header contactHeader = new MultiLineHeader(Header.CONTACT, true);
        contactHeader.setValue("<" + uri.toString() + ">", false);
        resp.addHeader(contactHeader);
        resp.indicateContact();
    }

    /**
     * Adds the default Path to the message
     *
     * @param req
     *           the message that will get the Path header set
     * @throws ServletParseException thrown in case the Path URI is not valid
     */
    public void addPath(SipServletRequestImpl req) {
        Header pathHeader;

        pathHeader = req.getRawHeader(Header.PATH);

        if (pathHeader == null) {
            pathHeader = new MultiLineHeader(Header.PATH, true);
            req.addHeader(pathHeader);
        }

        SipURIImpl uri = getSipOrSipsURI(req);
        uri.setLrParam(true);

        setPathParams(req, uri);
        pathHeader.setValue("<" + uri.toString() + ">", true);

        // Add Requires header if needed
        boolean pathRequired = false;
        ListIterator<String> supportedList = req.getHeaders(Header.REQUIRE);
        String supported = null;

        while (supportedList.hasNext()) {
            supported = (String) supportedList.next();

            if (supported.equals("path")) {
                pathRequired = true;
            }
        }

        if (!pathRequired) {
            req.addHeader(Header.REQUIRE, "path");
        }
    }

    private void setPathParams(SipServletRequestImpl req, SipURIImpl uri) {
        for (Iterator iter = req.getPathURIParamNames(); iter.hasNext();) {
            String name = (String) iter.next();
            uri.setParameter(name, req.getPathURIParam(name));
        }
    }

    /**
     * Adds the default record route to the message
     *
     * @param req
     *           the message that will get the record route header
     * @throws ServletParseException thrown in case thye Record Route URI is not valid
     */
    public void addRecordRoute(SipServletRequestImpl req) {
        Header rrHeader;
        rrHeader = req.getRawHeader(Header.RECORD_ROUTE);

        if (rrHeader == null) {
            rrHeader = new MultiLineHeader(Header.RECORD_ROUTE, true);
            req.addHeader(rrHeader);
        }

        SipURIImpl uri = getSipOrSipsURI(req);
        uri.setLrParam(true);

        setRecordRouteParams(req, uri);
        rrHeader.setValue("<" + uri.toString() + ">", true);
    }

    private void setRecordRouteParams(SipServletRequestImpl req, SipURIImpl uri) {
        for (Iterator iter = req.getRecordRouteURIParamNames(); iter.hasNext();) {
            String name = (String) iter.next();
            uri.setParameter(name, req.getRecordRouteURIParam(name));
        }
    }

    /**
     * Checks if a sip or sips URI should be used.
     * Returns a URI pointing to this container that can be used as Record-Route and Path header.
     * The returned URI will contain DialogFragment information if needed.
     * @param req the request to check
     * @return a URI pointing to this container that can be used as a valid header
     */
    private SipURIImpl getSipOrSipsURI(SipServletRequestImpl req) {
        DialogFragment df = req.getDialog();

        //Do the rfc 3261 sips check 16.6 (4,6,7)
        URI nextTarget = null;
        Header r = req.getRawHeader(Header.ROUTE);

        if (r == null) { //No route headers, next hop ReqURI
            nextTarget = req.getRequestURI();
        } else {
            //TODO maybe not a SipURI!
            try {
                if (r.getAddressValue() != null) {
                    nextTarget = r.getAddressValue().getURI();
                }
            } catch (ServletParseException ignore) {
            }
        }

        SipURIImpl uri = null;

        if (nextTarget != null && nextTarget.isSipURI())
        {
            SipURI targetUri = (SipURI) nextTarget;
            boolean isTransportParamTls =
                    targetUri.getTransportParam() != null &&
                            targetUri.getTransportParam().equalsIgnoreCase("tls");

            if (targetUri.isSecure() || isTransportParamTls)
            {
                uri = (SipURIImpl) m_SURI.clone();

                // Do not use SIPS URI scheme if transport=TLS
                if (isTransportParamTls)
                {
                    uri.setSecure(false);
                }
            }
            else
            {
                uri = (SipURIImpl) m_URI.clone();
            }
        } else {
            uri = (SipURIImpl) m_URI.clone();
        }
        if ((df != null) && shouldShowFragmentId(req)) {
            uri.setParameter(SipURIImpl.FRAGID_PARAM, df.getFragmentId());
        }
        return uri;
    }

    /**
     * Attaches the session and dialog to the incoming request if found otherwise
     * an error response is returned.
     *
     * @param req
     *           the incoming request
     * @return null if session is recovered otherwise the error response Call
     *         Leg/Transaction does not exist (481).
     */
    private SipServletResponseImpl setDialogContext(SipServletRequestImpl req)
        throws IllegalStateException {
        SipServletResponseImpl resp = null;
        SipSessionBase s = null;

        try {
            s = getSession(req);
        } catch (RemoteLockRuntimeException e) {
            if (!req.getMethod().equals("ACK")) {
                // Status code (481) indicating Call Leg/Transaction does not exist.
                resp = req.createTerminatingResponse(500);
                resp.setHeader(Header.RETRY_AFTER, "5");

                return resp;
            }
        } catch (RemoteLockException e) {
            if (!req.getMethod().equals("ACK")) {
                // Status code (481) indicating Call Leg/Transaction does not exist.
                resp = req.createTerminatingResponse(500);
                resp.setHeader(Header.RETRY_AFTER, "5");

                return resp;
            }
        } catch (IncompleteDialogException e) {
            // Status code (481) indicating Call Leg/Transaction does not exist.
            resp = req.createTerminatingResponse(481);

            return resp;
        }

        if ((s != null) && s.isValid()) {
            req.setSession(s);
        } else {
            if (!req.getMethod().equals("ACK")) {
                // Status code (481) indicating Call Leg/Transaction does not exist.
                resp = req.createTerminatingResponse(481);
            } else {
                throw new IllegalStateException(
                    "Call Leg/Transaction does not exist for ACK with callId = " +
                    req.getCallId() + ", from = " + req.getFromImpl() +
                    ", to = " + req.getToImpl());
            }
        }

        return resp;
    }

    /**
     * Sets the request to initial or subsequent
     *
     * @param req
     *           the request to set
     * @throws ServletParseException
     */
    public void next(SipServletRequestImpl req) {
        SipServletResponseImpl resp = null;
        req.pushTransactionDispatcher(this);
        req.pushApplicationDispatcher(this);

        // Look at targetting session with Join/Replace. Even if the
        // request is initial, we need to find the SAS.
        try {
            SessionTarget.setup(req);
        } catch (SessionTargetException set) {
            if (m_Log.isLoggable(Level.FINE)) {
                m_Log.log(Level.FINE, set.getLocalizedMessage());
            }

            resp = req.createTerminatingResponse(set.getErrorCode());

            if (resp != null) {
                resp.popDispatcher().dispatch(resp);

                return;
            }
        }

        if (!req.isInitial()) {
            try {
                resp = setDialogContext(req);
            } catch (IllegalStateException e) {
                // Call Leg/Transaction does not exist for request
                if (m_Log.isLoggable(Level.FINE)) {
                    m_Log.log(Level.FINE,
                        "Call Leg/Transaction does not exist for request " +
                        req.getMethod() + " with callId = " + req.getCallId() +
                        ", from = " + req.getFromImpl() + ", to = " +
                        req.getToImpl());
                }

                return;
            }

            if (resp != null) {
                resp.popDispatcher().dispatch(resp);

                return;
            }
        } else {
            if (SipFactoryImpl.isDialogCreational(req.getMethod())) {
                if (req.getHeader(Header.CONTACT) == null) {
                    // Respond with error code 400 because of the contact header
                    // is missing for a dialog creational request.
                    resp = req.createTerminatingResponse(400,
                            "Missing Contact header field");
                    resp.popDispatcher().dispatch(resp);

                    return;
                } else if (!isValidContact(req.getRawHeader(Header.CONTACT))) {
                    // Respond with error code 400 because of the contact header
                    // is invalid.

                    // TR HH52078
                    resp = req.createTerminatingResponse(400,
                            "Invalid Contact header field");

                    if (resp == null) {
                        return;
                    }

                    resp.popDispatcher().dispatch(resp);

                    return;
                }
            } else if (!SipFactoryImpl.initialRequestPossible(req.getMethod())) {
                if (m_Log.isLoggable(Level.FINE)) {
                    m_Log.log(Level.FINE,
                        "Received an Initial Request, method is" +
                        req.getMethod());
                }

                if (!"ACK".equals(req.getMethod())) {
                    resp = req.createTerminatingResponse(481);
                    resp.popDispatcher().dispatch(resp);
                }

                return;
            }
        }


        DialogFragment df = req.getDialog();
        boolean isLocked = false;
        try {
            if (!req.isInitial() && df != null) {
                df.obtainLockForIncomingMessage();
                isLocked = true;
                // Check if INVITE dialog is confirmed
                if ("ACK".equals(req.getMethod()) && (df != null)) {
                    try {
                        df.setConfirmed();
                    } catch (RemoteLockRuntimeException e) {
                        if (m_Log.isLoggable(Level.FINE)) {
                            m_Log.log(Level.FINE,
                                    "The dialog was remotely locked when the ACK arrived; the ACK is dropped.");
                        }

                        return;
                    }
                }
                pushApplicationDispatchers(req);
            }
            LayerHelper.next(req, this, m_NextLayer);
        } finally {
            if(isLocked) {
                df.releaseLockForIncomingMessage();
            }
        }
    }

    private boolean isValidContact(Header header) {
        // Verify the address spec is correct.
        try {
            Address adr = header.getAddressValue();
            adr.toString();
        } catch (ServletParseException e) {
            return false;
        }

        return true;
    }

    /**
     * Replaces PathNodes of the response transactionStack.
     *
     * @param resp
     * @param d
     */
    private void replaceTranscationPath(SipServletResponseImpl resp,
        DialogFragment d) {
        // need to update transaction path since there are new nodes.
        for (int i = 0; i < d.size(); i++) {
            // lets remove old nodes..
            resp.popDispatcher();
        }

        // need to take it in reverse order since its a stack.
        PathNode p = null;
        Iterator<PathNode> i = d.getCallee2CallerPath();

        while (i.hasNext()) {
            // ...and replace them with new nodes
            p = i.next();

            resp.pushTransactionDispatcher(p);
        }
    }

    public void next(SipServletResponseImpl resp) {
        // associate response with session and dialog, clone dialog if
        // necessary...
        SipServletRequestImpl req = resp.getRequestImpl();
        DialogFragment dialog = req.getDialog();

        if (dialog != null && dialog.isValid()) {
            String respToTag = resp.getToImpl()
                                   .getParameter(AddressImpl.TAG_PARAM);

            if ((respToTag == null) && !(resp.getStatus() == 100)) {
                // Except for 100 Trying, all responses MUST have a to-tag
                // to be complying to RFC3261 8.2.6.2
                // Drop the response.
                if (m_Log.isLoggable(Level.WARNING)) {
                    m_Log.log(Level.WARNING,
                        "sip.stack.dialog.response_without_totag");
                }

                return;
            }

            //
            // A dialog-establishing NOTIFY request might
            // already have created and stored a dialog
            //
            if (req.getMethod().equals("SUBSCRIBE") && req.isInitial()) {
                DialogSet ds = dialog.getDialogSet();
                DialogFragment clonedOrFetched = dialog;

                if (clonedOrFetched.getToTag() == null) {
                    // The dialog was not confirmed by a speedy NOTIFY
                    boolean success = false;
                    // fetch stored dialog in set,
                    // lets use fragmentId since it might be a spiral
                    clonedOrFetched = ds.getDialog(req.getFragmentId());

                    // is it already occupied?
                    if (clonedOrFetched != null) {
                        // TODO qbinjoe: Change so that DS registers and clones
                        success = clonedOrFetched.tryToSetToTagAndRegisterDialog(respToTag,
                                true);
                    }

                    if (!success) {
                        // need to clone dialog
                        // TODO qbinjoe Shouldn't we try to set to-tag and register dialog here?
                        clonedOrFetched = ds.cloneDialog(req.getFragmentId());

                        // clonedOrFetched.tryToSetToTagAndRegisterDialog(respToTag,
                        //        true);
                    }
                }

                resp.setDialog(clonedOrFetched);
                resp.copyTransactionStack();

                // it might be same dialog as from original request...
                if (clonedOrFetched != dialog) {
                    if (clonedOrFetched != null) {
                        replaceTranscationPath(resp, clonedOrFetched);
                    } else {
                        if (m_Log.isLoggable(Level.FINE)) {
                            Iterator<DialogFragment> allDialogFragments = ds.getDialogs();
                            int counter = 0;

                            while (allDialogFragments.hasNext()) {
                                counter++;
                            }

                            if (m_Log.isLoggable(Level.FINE)) {
                                m_Log.log(Level.FINE,
                                    "[clonedOrFetched=" + clonedOrFetched +
                                    ", FromTag=" + ds.getFromTag() +
                                    ", FragmentId=" + req.getFragmentId() +
                                    ", allDialogFragments size=" + counter +
                                    " FragmentId=" + dialog.getFragmentId());
                            }
                        }

                        // No dialogFragment exist.
                        return;
                    }
                }
            } else if (req.isInitial() && (dialog.getToTag() != null) &&
                    !dialog.getToTag().equals(respToTag)) {
                String id = DialogFragment.createKey(resp.getCallId(),
                        dialog.getDialogSet().getFromTag(), respToTag,
                        req.getFragmentId());

                try {
                    DialogFragment clonedOrFetched = DialogFragmentManager.getInstance()
                                                                          .findDialogFragment(id);

                    if (clonedOrFetched == null) {
                        // need to clone dialog
                        clonedOrFetched = (DialogFragment) dialog.clone();
                    }

                    resp.setDialog(clonedOrFetched);
                    resp.copyTransactionStack();
                    replaceTranscationPath(resp, clonedOrFetched);
                } catch (RemoteLockException ex) {
                    // The dialog was locked (could only happen if response was too late).
                    // Drop the response.
                    if (m_Log.isLoggable(Level.FINE)) {
                        m_Log.log(Level.FINE,
                            "sip.stack.dialog.remotely_locked_dialog_at_response");
                    }

                    return;
                }
            } else {
                resp.setDialog(dialog);
                resp.copyTransactionStack();
            }

            SipServletRequestImpl transReq = req.getTransactionRequest();

            if (transReq != null) {
                resp.setRequest(transReq);
                resp.setSession(transReq.getSessionImpl());
            } else {
                resp.setSession(req.getSessionImpl());
            }

            if (resp.getDialog() != null) {
                resp.getDialog().getDialogLifeCycle().setThreadLocalUnitOfWork();
            }

            DialogFragment df = resp.getDialog();
            try {
                if (df != null) {
                    df.obtainLockForIncomingMessage();
                }
                LayerHelper.next(resp, this, m_NextLayer);
            } finally {
                if (df != null) {
                    df.releaseLockForIncomingMessage();
                }
            }

            // Check if non-INVITE dialog is confirmed
            // NOTE, this is done after the application has been invoked.
            if (resp.getRequest().isInitial() &&
                    SipFactoryImpl.isDialogCreational(
                        resp.getRequest().getMethod()) &&
                    !"INVITE".equals(resp.getRequest().getMethod()) &&
                    (resp.getStatus() >= 200) && (resp.getStatus() < 300)) {
                if (resp.getDialog() != null) {
                    try {
                        resp.getDialog().setConfirmed();
                    } catch (RemoteLockRuntimeException e) {
                        if (m_Log.isLoggable(Level.FINE)) {
                            m_Log.log(Level.FINE,
                                "The dialog was remotely locked when the response arrived; probably the response was late; the response is dropped.");
                        }
                    }
                }
            }
        } else {
            // no valid dialog exist, lets return and go back...
            if (m_Log.isLoggable(Level.FINE) && dialog != null) {
                m_Log.log(Level.FINE,
                    "The dialog is invalid. Response is not routed to servlet: "+ resp.toDebugString());
            }

            resp.copyTransactionStack();
            dispatch(resp);
        }
    }

    public void registerNext(Layer layer) {
        m_NextLayer = layer;
    }

    public void dispatch(SipServletRequestImpl req) {
        if (m_Log.isLoggable(Level.FINEST)) {
            m_Log.log(Level.FINEST, req.toDebugString());
        }

        // add Record Route if its enabled and no contact exist
        if (!req.isContactIndicated() && req.isRecordRouteIndicated()) {
            addRecordRoute(req);
            OutboundInterface.modifyRecordRoute(req);
        }

        // Make sure that the protocol in Contact is correct
        try {
            OutboundInterface.modifyContact(req);
            req.validateContact();
        } catch (ServletParseException e) {
            if (m_Log.isLoggable(Level.WARNING)) {
                m_Log.log(Level.WARNING, "Failed to adjust Contact ", e);
            }
        }

        // add Path header if it is enabled
        if (req.isPathIndicated()) {
            addPath(req);
            OutboundInterface.modifyPath(req);
        }

        //req.handleAssertedIdentity();
        // lets register the early dialog...
        DialogSet.registerEarlyDialog(req);

        // Check if INVITE dialog has been confirmed
        if ("ACK".equals(req.getMethod()) && (req.getDialog() != null)) {
            try {
                req.getDialog().setConfirmed();
            } catch (RemoteLockRuntimeException e) {
                if (m_Log.isLoggable(Level.FINE)) {
                    m_Log.log(Level.FINE,
                        "The dialog was remotely locked when the ACK was sent; the ACK is dropped.");
                }

                return;
            }
        }

        // done...
        req.popDispatcher().dispatch(req);
    }

    public void dispatch(SipServletResponseImpl resp) {
        if (m_Log.isLoggable(Level.FINEST)) {
            m_Log.log(Level.FINEST, resp.toDebugString());
        }

        if (resp.getRequest().isInitial() &&
                SipFactoryImpl.isDialogCreational(resp.getRequest().getMethod())) {
            if ((resp.getStatus() >= 200) && (resp.getStatus() < 300)) {
                // OK
                if (SipMonitoring.isEnabled(SipMonitoring.SESSION_MANAGER)) {
                    m_EasSuccessfulSipDialogs.incrementAndGet();
                }

                // Check if non-INVITE dialog is confirmed
                if ((resp.getDialog() != null) &&
                        !"INVITE".equals(resp.getRequest().getMethod())) {
                    try {
                        resp.getDialog().setConfirmed();
                    } catch (RemoteLockRuntimeException e) {
                        if (m_Log.isLoggable(Level.FINE)) {
                            m_Log.log(Level.FINE,
                                "The dialog was remotely locked when the response was sent; the response is dropped.");
                        }

                        return;
                    }
                }
            } else if ((resp.getStatus() >= 400) && (resp.getStatus() < 700)) {
                if (SipMonitoring.isEnabled(SipMonitoring.SESSION_MANAGER)) {
                    // FAILED
                    m_EasFailedSipDialogs.incrementAndGet();
                }
            }
        }
        OutboundInterface.modifyContact(resp);
        resp.popDispatcher().dispatch(resp);
    }

    /**
     * Returns the instance of the SessionManager
     *
     * @return the instance of the SessionManager
     */
    public static DialogManager getInstance() {
        if (m_Instance == null) {
            synchronized (DialogManager.class) {
                if (m_Instance == null) {
                    if (m_FactoryClass == null) {
                        m_Instance = new DialogManager();
                    } else {
                        try {
                            m_Instance = (DialogManager) m_FactoryClass.newInstance();
                        } catch (Exception e) {
                            m_Log.log(Level.SEVERE,
                                "Failed to instantiate factory class : ", e);
                        }
                    }
                }
            }
        }

        return m_Instance;
    }

    /**
     * Returns the instance of the SessionManager
     *
     * @return the instance of the SessionManager
     */
    public static void initInstance(Class instance) {
        if (m_Log.isLoggable(Level.FINE)) {
            m_Log.log(Level.FINE,
                "instantiates this layer with class = " +
                instance.getCanonicalName());
        }

        if (m_Instance == null) {
            m_FactoryClass = instance;
        } else {
            throw new IllegalStateException(
                "Can not init class after the factory has given a reference away!");
        }
    }

    /**
     * Fetches the early dialog and clones it.
     *
     * @param m
     *           the NOTIFY request
     * @return the clone of the early dialog if found otherwise null
     */
    private DialogFragment createDialogByCloningEarlyDialog(
        SipServletMessageImpl m) {
        DialogFragment d = null;

        if (m.getMethod().equals("NOTIFY") && DialogSet.earlyDialogsExists()) {
            // According to RFC3265, 3.3.4 "to header of NOTIFY
            // should match from header of SUBSCRIBE"
            String toTag = m.getToImpl().getParameter(AddressImpl.TAG_PARAM);
            String id = DialogSet.createKey(m.getCallId(), toTag);
            DialogSet ds = DialogSet.getEarlyDialog(id);

            if (ds != null) {
                boolean success = false;
                // fetch stored dialog in set,
                // lets use fragmentId since it might be a spiral
                d = ds.getDialog(m.getFragmentId());

                // is it already occupied?
                if (d != null) {
                    // TODO qbinjoe: Change so that DS registers and clones
                    success = d.tryToSetToTagAndRegisterDialog(m.getFromImpl()
                                                                .getParameter(AddressImpl.TAG_PARAM),
                            true);
                }

                if (!success) {
                    d = ds.cloneDialog(m.getFragmentId());
                }

                m.setDialog(d);
            }
        }

        return d;
    }

    /**
     * According to RFC3265, 3.3.4 Dialog creation and termination: a matching
     * response or NOTIFY will establish a dialog. This method supports the
     * creation of dialog using a NOTIFY request and returns a established
     * session if found otherwise null.
     *
     * @param m
     *           the NOTIFY request
     * @return the session which now is established or null if not found
     */
    private SipSessionBase getEarlySession(SipServletRequestImpl req) {
        SipSessionBase s = null;
        DialogFragment d = createDialogByCloningEarlyDialog(req);

        if (d != null) {
            Iterator<PathNode> iter = null;
            PathNode p = null;
            req.setDirection(Type.Callee);
            req.setDialog(d);
            iter = d.getCaller2CalleePath();

            // add the dispatchers
            while (iter.hasNext()) {
                p = iter.next();
                req.pushApplicationDispatcher(p);
            }

            s = p.getSipSession();
        }

        return s;
    }

    private void pushApplicationDispatchers(SipServletRequestImpl req) {
        String fromTag = req.getFromImpl().getParameter(AddressImpl.TAG_PARAM);
        Iterator<PathNode> iter = null;
        DialogFragment d = req.getDialog();
        if (d != null) {
            if (fromTag.equals(d.getFromTag())) {
                iter = d.getCallee2CallerPath();
            } else {
                iter = d.getCaller2CalleePath();
            }
            // push the dispatchers
            while (iter.hasNext()) {
                PathNode p = iter.next();
                SipSessionBase sTmp = p.getSipSession();
                if (sTmp != null && sTmp.isValid()) {
                    req.pushApplicationDispatcher(p);
                }
            }
        }
    }
   
    /**
     * Fetches a established session for incoming subsequent requests.
     *
     * @param req
     *           the subsequent request
     * @return the established session or null if not found
     * @throws RemoteLockException, RemoteLockRuntimeException
     */
    public SipSessionBase getSession(SipServletRequestImpl req)
        throws RemoteLockException, RemoteLockRuntimeException,
            IncompleteDialogException {
        // 1. fetch matching dialog [callId, to-tag, from-tag]
        // 2. fetch the first path node from the application path of the dialog
        // depending on the direction (caller to callee or vice versa)
        // 3. Return the SipSession of the path node.
        SipSessionBase s = null;
        String toTag = null;
        String fromTag = null;
        DialogFragment d = null;
        Iterator<PathNode> iter = null;
        PathNode p = null;

        // we need the fragmentId here
        String fragmentId = req.getFragmentId();

        if (fragmentId.equals(DialogFragment.DEFAULT_FRAGMENT_ID) &&
                !getStrictFid()) {
            fragmentId = DialogSet.hardCodedDefaultFragmentId();

            if (m_Log.isLoggable(Level.FINE)) {
                m_Log.log(Level.FINE,
                    "fid undefined for message " + req.getMethod() +
                    ". Attempt using fid = " + fragmentId);
            }
        }

        // fetch to-tag & from-tag
        toTag = req.getToImpl().getParameter(AddressImpl.TAG_PARAM);
        fromTag = req.getFromImpl().getParameter(AddressImpl.TAG_PARAM);

        if ("NOTIFY".equals(req.getMethod())) {
            String id = DialogSet.createKey(req.getCallId(), toTag);
            DialogSet ds = DialogSet.getEarlyDialog(id);

            if (ds != null) {
                DialogFragment df = ds.getDialog(fragmentId);

                if (df.getToTag() == null) {
                    // this is a speedy NOTIFY
                    s = getEarlySession(req);
                }
            }
        }

        if (s == null) {
            if (!req.isInitial()) {
                String id = DialogFragment.createKey(req.getCallId(), toTag,
                        fromTag, fragmentId);

                d = DialogFragmentManager.getInstance().findDialogFragment(id);

                if ((d != null) &&
                        ((d.isValid() == false) ||
                        (d.isValidForIncomingTraffic() == false))) {
                    d = null;
                }
            }

            if (d != null) {
                if (fromTag.equals(d.getFromTag())) {
                    req.setDirection(Type.Caller);
                    iter = d.getCallee2CallerPath();
                } else {
                    req.setDirection(Type.Callee);
                    iter = d.getCaller2CalleePath();
                }

                req.setDialog(d);

                boolean foundValidSS = false;
               
                while (iter.hasNext()) {
                    p = iter.next();
                    SipSessionBase sTmp = p.getSipSession();
                    if (sTmp != null && sTmp.isValid()) {
                        // even if a valid session was found, iterate fully
                        // to see if the DS is remotely locked.
                        if (!foundValidSS) {
                            s = sTmp;
                            foundValidSS = true;
                        }
                    }
                }
            }
        }

        DialogFragment dialog = req.getDialog();

        if ((dialog != null) && dialog.isValid()) {
            DialogLifeCycle dialogLifeCycle = dialog.getDialogLifeCycle();
            dialogLifeCycle.associateTransaction(req.getTransactionId());
            dialogLifeCycle.initUnitOfWork();
            dialogLifeCycle.setThreadLocalUnitOfWork();
        }

        return s;
    }

    /**
     * Sets the value for this parameter, this is done via reflection when
     * starting Layer is started.
     *
     * @param defaultTCPTransport
     *           true if TCP should be used false if UDP should be used.
     */
    @Configuration(key = "DefaultTcpTransport", node = "/SipService/SipProtocol")
    public void setDefaultTCPTransport(Boolean aDefaultTCPTransport) {
        defaultTCPTransport = aDefaultTCPTransport;
    }

    @Configuration(key = "strictFid", node = "/SipContainer", usage = UsagePolicy.IGNORE)
    public void setStrictFid(boolean value) {
        strictFid = value;
    }

    private boolean getStrictFid() {
        return strictFid;
    }

    /*
    public Header getContactHeader()
    {
       return m_ContactHeader;
    }
     */
    public long getEasFailedSipDialogs() {
        return m_EasFailedSipDialogs.longValue();
    }

    public long getEasExpiredSipDialogs() {
        return DialogFragmentManager.getInstance().getEasExpiredSipDialogs();
    }

    public long getEasSuccessfulSipDialogs() {
        return m_EasSuccessfulSipDialogs.longValue();
    }

    public long getEasTotalSipDialogCount() {
        return DialogFragmentManager.getInstance().getEasTotalSipDialogCount();
    }

    public long getEasTotalSipDialogLifeTime() {
        return DialogFragmentManager.getInstance().getEasTotalSipDialogLifeTime();
    }

    public long getEasConcurrentSipDialogs() {
        return DialogFragmentManager.getInstance().getEasConcurrentSipDialogs();
    }

    public long getEasHeapMemoryUsage() {
        return Runtime.getRuntime().totalMemory() -
        Runtime.getRuntime().freeMemory();
    }
}
TOP

Related Classes of com.ericsson.ssa.sip.DialogManager

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.