Package com.ericsson.ssa.sip

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

/*
* 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 java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.logging.Level;

// inserted by hockey (automatic)
import java.util.logging.Logger;
import org.jvnet.glassfish.comms.util.LogUtil;

import javax.servlet.Servlet;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipServletMessage;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.UAMode;


/**
* This is a Singleton handling all SUBSCRIBE, REFER and NOTIFY messages and the
* associated responses. Each unique event header with the id parameter and
* value will be stored in this dialog. When a NOTIFY terminating the
* SUBSCRIBE/REFER session arrives the session will be removed from this dialog.
*
* @author ehsroha
*/
public class SUBSCRIBE_REFERSession extends FSM {
    private static final Logger m_Log = LogUtil.SIP_LOGGER.getLogger();
    private static SUBSCRIBE_REFERSession m_Instance = new SUBSCRIBE_REFERSession();

    private SUBSCRIBE_REFERSession() {
    }

    /**
     * Will return a SUBSCRIBE_REFERSession if the message matches this FSM.
     *
     * @param req
     *        the incoming message is the key to decide which FSM to return
     * @return a GeneralSession or null if no match occured
     */
    public static FSM createFSM(SipServletMessage m) {
        if (isStaticResponsible(m)) {
            return m_Instance;
        }

        return null;
    }

    public Object clone() {
        return m_Instance;
    }

    /**
     * Returns whether the message contains a Subscription-State header with
     * condition terminated or not.
     *
     * @param m
     *        the messge holding the Subscription-State header
     * @return whether a termination condition was found or not
     */
    private static boolean isNotifyTerminatedState(SipServletMessage m) {
        // Example "Subscription-State: terminated"
        String subState = m.getHeader("Subscription-State");

        if (subState == null) {
            return false;
        }

        return subState.matches("terminated");
    }

    protected static boolean hasEventHeader(SipServletMessage m) {
        return m.getHeader(Header.EVENT) != null;
    }

    protected static boolean checkReferToHeader(SipServletMessage m) {
        ListIterator referTo = m.getHeaders(Header.REFER_TO);
        ListIterator referToShort = m.getHeaders(Header.REFER_TO_SHORT);
        boolean referToCriterion = false;

        try {
            if ((referTo.next() != null) && !referTo.hasNext()) {
                referToCriterion = true;
            }
        } catch (NoSuchElementException e) {
        }

        try {
            if ((referToShort.next() != null) && !referToShort.hasNext()) {
                referToCriterion = true;
            }
        } catch (NoSuchElementException e) {
        }

        return referToCriterion;
    }

    /**
     * Extracts the Event header and returns the event and the id number as a
     * combined key. Example 1. Event: foo; param = yuy; id=25; expires=3600 will
     * return the combined key: foo25, Example 2. Event: refer;id=256565858 will
     * return the combined key: refer256565858
     *
     * @param m
     *        the messge holding the Event header
     * @return the combined key value or null if error or Event header was not
     *         found
     */
    protected static String getEventType(SipServletMessage m) {
        // Example "Event: foo; param=abcd; id=25"
        // Example "Event: refer; param=abcd; id=256565858 (id equal to CSeq of
        // REFER)"
        // TODO: replace the ' ' with proper isWhiteSpace algorithm according to
        // RFC
        // 3261 (BNF notation)
        String event = m.getHeader(Header.EVENT);

        if (event == null) {
            return null;
        }

        int posStart = 0;

        // trim
        while ((event.charAt(posStart) == ' ') && (posStart < event.length())) {
            ++posStart;
        }

        int posStop = posStart;

        // find first ';'
        while ((posStop < event.length()) && (event.charAt(posStop) != ';')) {
            ++posStop;
        }

        String eventType = null;

        if (posStop < event.length()) {
            eventType = event.substring(posStart, posStop);
        } else {
            // didn't find ';' only event without id parameter
            return event.substring(posStart, posStop);
        }

        posStart = posStop + 1;

        // find id parameter
        // TODO should ID be supported or only id
        while ((posStart < event.length()) &&
                (((event.charAt(posStart) != 'i') &&
                (event.charAt(posStart) != 'I')) ||
                ((event.charAt(posStart + 1) != 'd') &&
                (event.charAt(posStart + 1) != 'D')))) {
            ++posStart;
        }

        // lets make sure that the characters before id are OK
        int tmpPos = posStart - 1;

        while (event.charAt(tmpPos) == ' ') {
            --tmpPos;
        }

        if (event.charAt(tmpPos) != ';') {
            return eventType;
        }

        posStart = posStart + 2;

        // trim
        while ((event.charAt(posStart) == ' ') && (posStart < event.length())) {
            ++posStart;
        }

        // find id number after '='
        if ((posStart < event.length()) && (event.charAt(posStart) == '=')) {
            posStop = posStart++;

            // trim
            while ((posStop < event.length()) &&
                    (event.charAt(posStop) != ' ') &&
                    (event.charAt(posStop) != ';')) {
                ++posStop;
            }

            return eventType + event.substring(posStart, posStop);
        }

        return eventType;
    }

    /**
     * This FSM is responsible if the message is of method type: SUBSCRIBE, REFER
     * or NOTIFY.
     *
     * @param m
     *        the incoming message is the key to decide whether this FSM will
     *        match or not.
     */
    private static boolean isStaticResponsible(SipServletMessage m) {
        // SUBSCRIBE
        // Event: foo; id=25
        // REFER
        // CSeq: 11952389 REFER
        // NOTIFY
        // Event: refer; id=11952389 (NOTIFY to REFER)
        // Event: foo; id=25 (NOTIFY to SUBSCRIBE)
        // Subscription-State: terminated
        String method = m.getMethod();

        return method.equals("SUBSCRIBE") || method.equals("REFER") ||
        method.equals("NOTIFY");
    }

    public boolean isResponsible(SipServletMessage m) {
        return isStaticResponsible(m);
    }

    private void setDerivedOrOriginalSession(SipServletResponseImpl resp, UA ua) {
        // if the ua has a session with same to tag as the dialog, use it...
        if ((ua.getSipSession() != null) && !ua.getSipSession().hasNoToTag() &&
                (resp.getDialog().getToTag() != null) &&
                resp.getDialog().getToTag().equals(ua.getSipSession().getToTag())) {
            resp.setSession(ua.getSipSession());
        } else {
            // ...otherwise fetch or create one
            DialogFragment df = resp.getDialog();

            // lets update to-tag of dialog...
            SipSessionBase s = resp.getSessionImpl()
                                   .getOriginalOrDerivedSessionAndRegisterDialog(resp,
                    df);

            if (s.isDerived()) {
                // lets set the session cuz it is a derived session
                resp.setSession(s);
            }

            ua.setSipSession(s);
        }
    }

    private void doResponseUAC(SipServletResponseImpl resp, UA ua) {
        if (resp.hasToTag()) {
            String eventType = null;
            String method = resp.getMethod();

            if (method.equals("REFER")) {
                setDerivedOrOriginalSession(resp, ua);

                if (resp.getRequest().isInitial()) {
                    try {
                        ua.saveRouteSetRemoteTarget(resp);
                    } catch (ServletParseException e) {
                        throw new IllegalStateException(
                            "Parse problem of Contact or Record-Route");
                    }
                }

                eventType = "refer" + resp.getCSeqNumber();
                ua.addDialogSession(method, eventType);
            } else {
                // need to get Event header from request of response
                // because there is no Event header in response
                SipServletRequest req = resp.getRequest();
                eventType = getEventType(req);

                if (eventType != null) {
                    if (method.equals("SUBSCRIBE")) {
                        setDerivedOrOriginalSession(resp, ua);

                        if (resp.getRequest().isInitial()) {
                            try {
                                ua.saveRouteSetRemoteTarget(resp);
                            } catch (ServletParseException e) {
                                throw new IllegalStateException(
                                    "Parse problem of Contact or Record-Route");
                            }
                        }

                        ua.addDialogSession(method, eventType);
                    } else if (method.equals("NOTIFY") &&
                            isNotifyTerminatedState(req)) {
                        // TODO: this doesn't work if NOTIFY didn't use
                        // the CSeq number from REFER as id value
                        ua.removeDialogSession(method, eventType);
                    }
                }
            }
        }
    }

    private void doResponseUAS(SipServletResponseImpl resp, UA ua) {
        String eventType = null;
        String method = resp.getMethod();

        if (method.equals("REFER")) {
            if (!resp.hasToTag()) {
                resp.createTag(Header.TO);
                setDerivedOrOriginalSession(resp, ua);

                // make sure that getLocalParty() and getRemoteParty() change
                // behaviour
                SipSessionDialogImpl s = (SipSessionDialogImpl) resp.getSessionImpl();
                s.swapLocalRemote();

                try {
                    // need to save system headers
                    ua.saveContactRouteSetRemoteTarget(resp);
                } catch (ServletParseException e) {
                    throw new IllegalStateException(
                        "Parse problem of Contact or Record-Route");
                }

                eventType = "refer" + resp.getCSeqNumber();
                ua.addDialogSession(method, eventType);
            }
        } else {
            // need to get Event header from request of response
            // because there is no Event header in response
            SipServletRequest req = resp.getRequest();
            eventType = getEventType(req);

            int status = resp.getStatus();

            if (eventType != null) {
                if (method.equals("SUBSCRIBE")) {
                    if (!resp.hasToTag()) {
                        resp.createTag(Header.TO);

                        if ((status >= 200) && (status < 300)) {
                            setDerivedOrOriginalSession(resp, ua);

                            // make sure that getLocalParty() and getRemoteParty() change
                            // behaviour
                            SipSessionDialogImpl s = (SipSessionDialogImpl) resp.getSessionImpl();
                            s.swapLocalRemote();

                            try {
                                // need to save system headers
                                ua.saveContactRouteSetRemoteTarget(resp);
                            } catch (ServletParseException e) {
                                throw new IllegalStateException(
                                    "Parse problem of Contact or Record-Route");
                            }

                            ua.addDialogSession(method, eventType);
                        }
                    }
                } else if (method.equals("NOTIFY") &&
                        isNotifyTerminatedState(req)) {
                    // TODO: this doesn't work if NOTIFY didn't use
                    // the CSeq number from REFER as id value
                    ua.removeDialogSession(method, eventType);
                }
            } else {
                if ((status >= 200) && (status < 300)) {
                    // RFC3265 mandates Event header for SUBSCRIBE and NOTIFY request
                    throw new IllegalStateException(
                        "Original request missing Event header field");
                }
            }
        }
    }

    public void send(SipServletRequestImpl req, UA ua)
        throws IllegalStateException {
        if (m_Log.isLoggable(Level.FINEST)) {
            m_Log.log(Level.FINEST, req.toDebugString());
        }

        String method = req.getMethod();

        if (method.equals("SUBSCRIBE") || method.equals("NOTIFY")) {
            // RFC3265 mandates Event header for SUBSCRIBE and NOTIFY request
            if (!hasEventHeader(req)) {
                throw new IllegalStateException("Missing Event header field");
            }
        } else if (method.equals("REFER")) {
            // RFC3515 mandates Refer-To for REFER request.
            if (!checkReferToHeader(req)) {
                throw new IllegalStateException("Missing Refer-To header field");
            }
        }

        // the request need to be cloned before sending
        SipServletRequestImpl clone = (SipServletRequestImpl) req.clone();
        clone.setTransactionRequest(req);
        // lets run in new thread...
        super.send(clone, ua);
    }

    public void send(SipServletResponseImpl resp, UA ua)
        throws IllegalStateException {
        if (m_Log.isLoggable(Level.FINEST)) {
            m_Log.log(Level.FINEST, resp.toDebugString());
        }

        doResponseUAS(resp, ua);
       
        resp.getSessionImpl().updateSipSessionState(resp, ua.getType());
       
        if (!resp.isCommitted() && resp.getSessionImpl().isB2buaHelper()) {
          resp.getSessionImpl().addPendingMessage(resp, UAMode.UAS);
        }

        // the response need to be cloned before sending
        final SipServletResponseImpl clone = (SipServletResponseImpl) resp.clone();

        // if there is a transaction request, added it to the response...
        SipServletRequestImpl req = resp.getRequestImpl().getTransactionRequest();

        if (req != null) {
            clone.setRequest(req);
            clone.setSession(req.getSessionImpl());
        }

        IWRHandler.getInstance().handle(null, resp);
       
        // lets run in new thread...
        super.send(clone, ua);
    }

    private void setDerivedOrOriginalSession(SipServletRequestImpl req, UA ua)
        throws ServletParseException {
        // dialog creational NOTIFY
        if (req.getMethod().equals("NOTIFY")) {
            // if the context has a session with same to tag as the dialog, use
            // it...
            if (!ua.getSipSession().hasNoToTag() &&
                    (req.getDialog().getToTag() != null) &&
                    req.getDialog().getToTag()
                           .equals(ua.getSipSession().getToTag())) {
                req.setSession(ua.getSipSession());
            } else {
                // ...otherwise fetch or create one
                DialogFragment df = req.getDialog();

                // lets update to-tag of dialog...
                SipSessionBase s = ua.getSipSession()
                                     .getOriginalOrDerivedSessionAndRegisterDialog(req,
                        df);
                // lets set the session
                req.setSession(s);
                // update ua
                ua.setSipSession(s);
                // need to save system headers
                ua.saveContactRouteSetRemoteTarget(req);
            }
        } else {
            // the response should already have created
            // the dialog for all other methods...
            req.setSession(ua.getSipSession());
        }
    }

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

        String method = req.getMethod();

        if (method.equals("SUBSCRIBE") || method.equals("NOTIFY")) {
            if (!hasEventHeader(req)) {
                // RFC3265 mandates Event header for SUBSCRIBE and NOTIFY request
                SipServletResponseImpl resp = req.createTerminatingResponse(400,
                        "Missing Event header field");
                resp.popDispatcher().dispatch(resp);

                return;
            }
        } else if (method.equals("REFER")) {
            if (!checkReferToHeader(req)) {
                // RFC 3515 Refer Method. One Refer-To header is mandatory.
                SipServletResponseImpl resp = req.createTerminatingResponse(400,
                        "Missing Refer-To header field");
                resp.popDispatcher().dispatch(resp);

                return;
            }
        }

        try {
            setDerivedOrOriginalSession(req, ua);

            Servlet s = ua.getServlet(req.getSessionImpl().getHandler());

            if (s != null) {
                req.getSessionImpl().updateSipSessionState(req, ua.getType());
                if (!req.isCommitted() && req.getSessionImpl().isB2buaHelper()) {
                  req.getSessionImpl().addPendingMessage(req, UAMode.UAS);
                }
                s.service(req, null);
            } else {
                if (m_Log.isLoggable(Level.INFO)) {
                    m_Log.log(Level.INFO,
                        "Could not find servlet name: " +
                        req.getSessionImpl().getHandler() +
                        " in application: " +
                        req.getSessionImpl().getApplicationSessionImpl()
                           .getName());
                }
            }
        } catch (Exception e) {
            if (m_Log.isLoggable(Level.FINE)) {
                m_Log.log(Level.FINE, "Caught Exception: ", e);
            }

            // problem in servlet...

            // TR HH52078
            SipServletResponseImpl resp = req.createTerminatingResponse(500);

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

            // also remove session
            String eventType = getEventType(req);

            if (eventType != null) {
                // TODO must adjust
                ua.removeDialogSession(req.getMethod(), eventType);
            }
        }
    }

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

        doResponseUAC(resp, ua);

        try {
            Servlet s = ua.getServlet(resp.getSessionImpl().getHandler());

            if (s != null) {
                resp.getSessionImpl().updateSipSessionState(resp, ua.getType());
                resp.setIncoming();
               
                // first message will create the pending
                // message lists for UAC and UAS..
                if (resp.getRequestImpl().isB2buaHelper()) {
                  resp.getSessionImpl().createPendingMessageHelper();
                }
                // ...if pending lists exist, add pending uncommitted message
                if (!resp.isCommitted() && resp.getSessionImpl().isB2buaHelper()) {
                  resp.getSessionImpl().addPendingMessage(resp, UAMode.UAC);
                }
               
                s.service(null, resp);
            } else {
                if (m_Log.isLoggable(Level.INFO)) {
                    m_Log.log(Level.INFO,
                        "Could not find servlet name: " +
                        resp.getSessionImpl().getHandler() +
                        " in application: " +
                        resp.getSessionImpl().getApplicationSessionImpl()
                            .getName());
                }
            }
        } catch (Exception e) {
            // problem in servlet, lets drop response
            if (m_Log.isLoggable(Level.INFO)) {
                m_Log.log(Level.INFO, "Problem in servlet.", e);
            }
        }
    }

    /**
     * Will always return false since it's a Singleton class
     */
    public boolean isDeletable() {
        return false;
    }
}
TOP

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

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.