Package com.ericsson.ssa.sip

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

/*
* 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.container.SipBindingResolver;
import com.ericsson.ssa.container.SipContainerThreadPool;
import com.ericsson.ssa.container.reporter.Reporter;
import com.ericsson.ssa.container.sim.ApplicationDispatcher;
import com.ericsson.ssa.sip.ProxyImpl.ProxyFacade;
import com.ericsson.ssa.sip.persistence.ReplicationUnitOfWork;
import com.ericsson.ssa.sip.timer.GeneralTimer;
import com.ericsson.ssa.sip.timer.GeneralTimerListener;
import com.ericsson.ssa.sip.timer.TimerServiceImpl;
import com.ericsson.ssa.sip.transaction.ClientTransaction;
import com.ericsson.ssa.sip.transaction.ClientTransactionRegistrationListener;
import com.ericsson.ssa.sip.transaction.InviteClientTransaction;

import org.glassfish.comms.api.proxy.ProxyBranchEvent;
import org.glassfish.comms.api.proxy.ProxyBranchListener;

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

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

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

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.sip.ProxyBranch;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.TooManyHopsException;
import javax.servlet.sip.URI;
import javax.servlet.sip.Address;
import javax.servlet.sip.ar.SipApplicationRoutingDirective;


/**
* Part of the Proxy implementation.
*
* @author ehsroha
*/
public class ProxyBranchImpl extends TargetSet implements ProxyBranch,
    GeneralTimerListener, ClientTransactionRegistrationListener {
    protected static final Logger _log = LogUtil.SIP_LOGGER.getLogger();
    static int TimerC = 180; // >3min
    private SipServletResponseImpl _bestResponse = null;
    private SipServletResponse _lastResponse = null;
    private final SipServletRequestImpl _request;
    private GeneralTimer _timer = null;
    private int _proxyBranchTimeout;
    private List<ProxyBranchImpl> _proxyBranches = null;
    private boolean _isCanceled = false;
    private boolean _pendingCancel = false;
    private Header _pendingCancelReason;
    private SipServletResponseImpl _provisional = null;
    private boolean isStarted = false;
    private OutboundInterface oi = null;
    private java.util.List<Address> routesForCancel = new ArrayList<Address>();

    /**
     * Whether there is a specific setting to recurse on ProxyBranch level.
     * This value is null unless explicitely set. A null value means that
     * it should be taken from proxy level.
     */
    private Boolean _recurse = null;

    /**
     * Whether there is a specific setting to recordRoute on ProxyBranch level.
     * This value is null unless explicitely set. A null value means that
     * it should be taken from proxy level.
     */
    private Boolean _recordRoute = null;

    /**
     * Whether there is a specific setting to addToPath on ProxyBranch level.
     * This value is null unless explicitely set. A null value means that
     * it should be taken from proxy level.
     */
    private Boolean _addToPath = null;

    /**
    * Client transaction for this request
    * Is set when the client transaction is
    * created. May remain null if there is no
    * client transaction associated with this
    * branch. This is the case when there are
    * more path nodes in the application path.
    */
    private ClientTransaction clientTransaction;
    private ProxyBranchInternalRequest internalReq;

    /**
     * The constructor of the Proxy Branch implementation.
     *
     * @param request
     *           the request which this branch should proxy to
     */
    public ProxyBranchImpl(ProxyImpl p, SipServletRequestImpl request) {
        super(p);
        _request = request;
        _proxyBranchTimeout = p.getProxyTimeout();
    }

    protected List<TargetSet> getTargetSets() {
        if (getRecursedProxyBranchesImpl() == null) {
            return super.getTargetSets();
        }

        return new ArrayList<TargetSet>(getRecursedProxyBranchesImpl());
    }

    public synchronized SipServletResponseImpl findBestResponse() {
        if (_bestResponse != null) {
            // Already 3xx redirection?
            if (_bestResponse.isAlreadyRedirected() &&
                    (getRecursedProxyBranchesImpl() != null)) {
                return super.findBestResponse();
            }
        }

        return _bestResponse;
    }

    /**
     * Returns the best response.
     *
     * @return the best response.
     */
    public synchronized SipServletResponseImpl getBestResponse() {
        return _bestResponse;
    }

    /**
     * Rule 1: Set best response if no response priviously existed for this
     * branch.
     * <p>
     * Rule 2: 2xx response will override any response
     * <p>
     * Rule 3: 6xx response will override any response except 2xx
     * <p>
     * Rule 4: Except rule 1 and 2 a response 3xx-5xx will override a previous
     * set response, which had a higher status code.
     *
     * @param resp
     */
    private synchronized void setBestResponse(SipServletResponseImpl resp) {
        // 700 is one higher than possible
        int bestStatus = (_bestResponse == null) ? 700 : _bestResponse.getStatus();

        if ((bestStatus / 100) != 2) {
            int status = resp.getStatus() / 100;

            if (status == 2) {
                _bestResponse = resp;
            } else if (status == 6) {
                _bestResponse = resp;
            } else if (((status > 2) && (status < 6) &&
                    ((bestStatus / 100) != 6)) &&
                    (resp.getStatus() < bestStatus)) {
                _bestResponse = resp;
            }
        }
    }

    /**
     * Whether this ProxyBranchImpl is matching the URI or not.
     *
     * @param uri
     *           the incoming uri
     * @return this instance if it is matching the URI otherwise null
     */
    public ProxyBranchImpl findBranch(URI uri) {
        if (getRecurse()) {
            ProxyBranchImpl branch = super.findBranch(uri);

            if (branch != null) {
                return branch;
            }
        }

        return uri.equals(_request.getRequestURI()) ? this : null;
    }

    public void addTopLevelBranch(List<ProxyBranchImpl> branches) {
        branches.add(this);
    }

    private void startTimer(SipServletResponseImpl resp) {
        if (_timer == null) {
            synchronized (this) {
                if (_timer == null) {
                    int time = getProxyBranchTimeout() * 1000;

                    if (_log.isLoggable(Level.FINE)) {
                        _log.log(Level.FINE,
                            "start timer " + (time / 1000) +
                            " sec for branch = " + _request.getRequestURI());
                    }

                    // needed for CANCEL to reach next hop
                    if (resp != null) {
                        setProvisionalResponse(resp);
                    }

                    // start timer
                    _timer = TimerServiceImpl.getInstance()
                                             .createTimer(this, time, null);
                }
            }
        }
    }

    private synchronized void stopTimer() {
        if (_timer != null) {
            _timer.cancel();
            _timer = null;
        }
    }

    /**
     * Response management of the proxy branch
     * @param resp
     * @param pc
     * @return true if it is a virtual proxy branch otherwise false
     */
    public boolean doInitialResponse(SipServletResponseImpl resp,
        ProxyContext pc) {
        setLastResponse(resp);

        int status = resp.getStatus() / 100;

        if (status == 1) {
            if (resp.getMethod().equals("INVITE") && isPendingCancel()) {
                final SipServletRequestImpl cancel;

                synchronized (this) {
                    // needed for cancel to reach next hop-node
                    setProvisionalResponse(resp);

                    if (_log.isLoggable(Level.INFO)) {
                        _log.log(Level.INFO,
                            "CANCEL is pending, lets cancel this branch " +
                            _request.getRequestURI() + " for response " +
                            resp.getStatus());
                    }

                    cancel = cancelIntern(_pendingCancelReason);

                    if (cancel != null) {
                        setCanceled();
                    }
                }

                if (cancel != null) {
                    Reporter reporter = ApplicationDispatcher.getInstance().getServletReporters();
                    if (reporter != null) {
                        String interceptedAt = cancel.getApplicationSessionImpl().getApplicationName() + "/" + cancel.getSessionImpl().getHandler();
                        reporter.logOutgoingRequest(Reporter.InterceptionType.SERVLET, cancel, interceptedAt);
                    }

                    // lets CANCEL this branch outside synchronized block...
                    SipContainerThreadPool.getInstance().execute(new Callable() {
                            // execute in another thread...
                            public Object call() throws Exception {
                              DialogFragment dialog = cancel.getDialog();
                              if (dialog != null) {
                                dialog.getDialogLifeCycle().setThreadLocalUnitOfWork();
                                try {
                                  cancel.popDispatcher().dispatch(cancel);
                                } finally {
                                  ReplicationUnitOfWork.clearThreadLocal();
                                }
                              } else {
                                if (_log.isLoggable(Level.INFO)) {
                                  _log.log(Level.INFO, "can not send message {0} since the dialog is null", new Object[]{cancel});
                                }
                              }
                              return null;
                            }
                        });
                }
            } else {
                // lets start proxy branch timer
                startTimer(resp);
            }

            getProxyImpl().invokeServletAndForward(resp, pc);
        } else {
            // we have a final response
            setBestResponse(resp);

            // need to stop timer
            stopTimer();

            if ((status == 3) && getProxy().getRecurse()) {
                // If recurse is set and this is a 3xx response,
                // fork new requests to list of Contact
                AddressImpl address = null;
                URI uri = null;
                List<URI> contacts = new ArrayList<URI>(5);

                for (ListIterator<String> contactsIt = resp.getHeaders(
                            Header.CONTACT); contactsIt.hasNext();) {
                    String contact = (String) contactsIt.next();

                    try {
                        address = new AddressImpl(contact);
                        uri = address.getURI();
                        contacts.add(uri);
                    } catch (ServletParseException e) {
                        if (_log.isLoggable(Level.WARNING)) {
                            _log.log(Level.WARNING,
                                "Could not recurse on Contact = " + contact);
                        }
                    }
                }

                if (contacts.size() > 0) {
                    List<ProxyBranchImpl> branches = getProxyImpl()
                                                         .recurseTo(resp.getRequestImpl(),
                            contacts);

                    if (!branches.isEmpty()) {
                        addRecursedProxyBranches(branches);
                    }
                }

                // inform that this 3xx redirect has forked to all its contacts.
                resp.setAlreadyRedirected();
            } else {
                // continue to next branch for 3xx-6xx response
                if (status != 2) {
                    // invoke next branch of parent target set
                    if (getParent().hasNext()) {
                        if (_log.isLoggable(Level.FINE)) {
                            _log.log(Level.FINE, "start next branch");
                        }

                        getParent().next();
                    }
                }
            }
        }

        return false;
    }

    public ProxyImpl.ProxyFacade getProxy() {
        try {
            return (ProxyFacade) getProxyImpl().getOriginalRequestImpl()
                                     .getProxy();
        } catch (TooManyHopsException e) {
            return null;
        }
    }

    public int getProxyBranchTimeout() {
        return _proxyBranchTimeout;
    }

    private synchronized List<ProxyBranchImpl> getRecursedProxyBranchesImpl() {
        return _proxyBranches;
    }

    private synchronized void addRecursedProxyBranches(
        List<ProxyBranchImpl> branches) {
        if (_proxyBranches == null) {
            _proxyBranches = branches;
        } else {
            _proxyBranches.addAll(branches);
        }
    }

    public List<ProxyBranch> getRecursedProxyBranches() {
        List<ProxyBranchImpl> branches = getRecursedProxyBranchesImpl();

        return (branches == null) ? null : new ArrayList<ProxyBranch>(branches);
    }

    private synchronized void setLastResponse(SipServletResponse resp) {
        _lastResponse = resp;
    }

    public synchronized SipServletResponse getResponse() {
        return _lastResponse;
    }

    public SipServletRequest getRequest() {
        if (internalReq == null ) {
            internalReq = new ProxyBranchInternalRequest(_request);
        }
        return internalReq;
    }

    public SipServletRequestImpl getRequestImpl() {
        return _request;
    }

    @Override
    public boolean getParallel() {
        return getParent().getParallel();
    }

    @Override
    public boolean isStarted() {       
        return isStarted;
    }

    @Override
    public boolean getRecurse() {
        if (_recurse != null) {
            // Setting override that of proxy level.
            return _recurse;
        }

        return getProxyImpl().getRecurse();
    }

    public void setRecurse(boolean recurse) {
        _recurse = Boolean.valueOf(recurse);
    }

    public boolean getRecordRoute() {
        if (_recordRoute != null) {
            // Setting override that of proxy level.
            return _recordRoute;
        }

        return getProxyImpl().getRecordRoute();
    }

    public void setRecordRoute(boolean rr) {
        if (isStarted()) {
            throw new IllegalStateException("Proxy branch is already started");
        }

        _recordRoute = Boolean.valueOf(rr);
    }

    public boolean getAddToPath() {
        if (_addToPath != null) {
            // Setting override that of proxy level.
            return _addToPath;
        }

        return getProxyImpl().getAddToPath();
    }

    public void setAddToPath(boolean addToPath) {
        // ProxyImpl.validateSetAddToPath(_request);
        if (_request.getMethod().equals("REGISTER")) {
            _addToPath = Boolean.valueOf(addToPath);
        } // else ignore as spec and TCK don't like exceptions
    }

    public SipURI getRecordRouteURI() {
        if (!getRecordRoute()) {
            throw new IllegalStateException("record-routing is not enabled");
        }
        return new ProxyImpl.RecordRouteURI(_request);
    }

    public SipURI getPathURI() {
        return new ProxyImpl.PathURI(_request);
    }

    /**
     * Not doing anything inside the method.
     */
    public void setOutboundInterface(InetSocketAddress address) {       
        getProxyImpl().validateOutboundInterface(address.getAddress());
        validateSession();
        this.oi = new OutboundInterface(address);
    }

    /**
     * Not doing anything inside the method.
     */
    public void setOutboundInterface(InetAddress address) {
        getProxyImpl().validateOutboundInterface(address);
        validateSession();
        this.oi = new OutboundInterface(address);
    }

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

    public void setOutboundInterface(OutboundInterface oi) {
        this.oi = oi;
    }
   
    private void validateSession() {
        if (!getRequestImpl().getSession().isValid()) {
            throw new IllegalArgumentException("Session is invalid");
        }
    }

    public void setProxyBranchTimeout(int seconds) {
        validateProxyBranchTimeoutArgument(seconds);

        this._request.setTimerC(seconds);
        _proxyBranchTimeout = seconds;
    }

    private void validateProxyBranchTimeoutArgument(int seconds) {
        if (seconds <= 0) {
            throw new IllegalArgumentException(
                "Invalid BranchTimeout must be > 0");
        }

        if (getParallel() && (seconds > getProxyImpl().getProxyTimeout())) {
            throw new IllegalArgumentException(
                "BranchTimeout can not be greater than the overall proxy timeout value");
        }
    }

    public void timeout(GeneralTimer timer) {
        if (_log.isLoggable(Level.FINE)) {
            _log.log(Level.FINE,
                "timer fired for branch = " + _request.getRequestURI());
        }

        if (getBestResponse() == null) {
            // Give ProxyBranchListners a chance to extend timer
            int newTimer = callBranchExpired();

            if (newTimer == -1) {
                // Not extended
                branchExpired();
            } else {
                // Timer extended

                // Extent this timer
                setProxyBranchTimeout(newTimer);
                stopTimer();
                startTimer(null);

                // Extend client transaction timer
                if ((clientTransaction != null) &&
                        clientTransaction instanceof InviteClientTransaction) {
                    ((InviteClientTransaction) clientTransaction).prolongTimerC(newTimer);
                }
            }
        }
    }

    private void branchExpired() {
        // cancel this branch if cancel is allowed (RFC3841)
      if (!getProxyImpl().getNoCancel() || getProxyImpl().has6xx()) {
        cancel();
      }

        // invoke next branch of parent target set
        if (getParent().hasNext()) {
            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE, "start next branch");
            }

            getParent().next();
        }
    }

    /**
     * Call all the ProxyBranchListeners with branchExpired.
     *
     * @return new (extended) proxyBranchTimeout if called by a proxybranchListener, -1 otherwise.
     */
    private int callBranchExpired() {
        List<ProxyBranchListener> proxyBranchListeners = getProxyImpl()
                                                             .getSipSession()
                                                             .getApplicationSessionImpl()
                                                             .getSipApplicationListeners()
                                                             .getProxyBranchListeners();

        // Have result of invocation as final int[],
        // since it need to be final and mutable at the same time.
        final int[] anInt = new int[] { -1 };

        if (proxyBranchListeners.size() > 0) {
            // Create a dynamic proxy object that acts as a proxy for this ProcyBranch.
            // calls to setProxyBranchTimeout are intercepted.
            ProxyBranch proxyBranchDynamicProxy = (ProxyBranch) Proxy.newProxyInstance(getClass()
                                                                                           .getClassLoader(),
                    new Class[] { ProxyBranch.class },
                    new InvocationHandler() {
                        public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                            // Intercept setProxyBranchTimeout calls
                            if (method.getName().equals("setProxyBranchTimeout")) {
                                validateProxyBranchTimeoutArgument((Integer) args[0]);
                                // Remember (last) call to setProxyBranchTimeout
                                anInt[0] = (Integer) args[0];

                                return null;
                            } else {
                                // Default, forward call to corresponding method on this Proxy
                                return method.invoke(ProxyBranchImpl.this, args);
                            }
                        }
                    });

            // Fire event with dynamic proxy in event
            ProxyBranchEvent event = new ProxyBranchEvent(proxyBranchDynamicProxy);

            for (ProxyBranchListener proxyBranchListener : proxyBranchListeners) {
                proxyBranchListener.branchExpired(event);
            }
        }

        // return last call to proxyBranchTimeout. -1 in no such.
        return anInt[0];
    }

    public void proxyTo() throws IllegalStateException {
        prepareProxyTo();

        // the request need to be cloned
        final SipServletRequestImpl forward = (SipServletRequestImpl) getRequestImpl()
                                                                          .clone();

        if (getRecordRoute()) {
            // state Record Route in request to inform
            // that a RecordRoute header must be added
            forward.indicateRecordRoute();
        }

        if (getAddToPath()) {
            // state Path in request to inform
            // that a Path header shall be added
            forward.indicatePath();
        }

        if (getProxyImpl().getNoCancel()) {
          forward.setNoCancel();
        }
       
        // JSR289: when a request is proxied, its routing directive should
        // be implicitly set to CONTINUE. See also issue 773.
        forward.setImplicitRoutingDirective(SipApplicationRoutingDirective.CONTINUE,
            null);
        // no need to copy region, expected to be set already



        forward.setTransactionRequest(getRequestImpl());

        Reporter reporter = ApplicationDispatcher.getInstance().getServletReporters();
        if (reporter != null) {
            String interceptedAt = forward.getApplicationSessionImpl().getApplicationName() + "/" + forward.getSessionImpl().getHandler();
            reporter.logOutgoingRequest(Reporter.InterceptionType.SERVLET, forward, interceptedAt);
        }
       
        if (getOutboundInterface() == null) {
            setOutboundInterface(getProxyImpl().getOutboundInterface());
        }
       
        OutboundInterface.resolve(forward, this);

        //Copy the routes...
        forward.setPushedRouteList(routesForCancel);

        SipContainerThreadPool.getInstance().execute(new Callable() {
                public Object call() throws Exception {
                    if (_log.isLoggable(Level.FINER)) {
                        _log.log(Level.FINER,
                            "Proxy request " + forward.toDebugString() + ", " +
                            toString());
                    }

                    // Activate the dialog lifecycle u-o-w
                    forward.getDialog().getDialogLifeCycle()
                           .setThreadLocalUnitOfWork();

                    try {
                        // push the request to the dispatcher
                        forward.setClientTransactionRegistrationListener(ProxyBranchImpl.this);
                        forward.popDispatcher().dispatch(forward);

                        return null;
                    } finally {
                        ReplicationUnitOfWork.clearThreadLocal();
                    }
                }
            });

        isStarted = true;
        if (_log.isLoggable(Level.FINEST)) {
            _log.log(Level.FINEST, "Lets leave...");
        }
    }

    protected void prepareProxyTo() throws IllegalStateException {
        if (getParent() == null) {
            throw new IllegalStateException(
                "Need to add parent target set before calling.");
        }

        // analyze how to add proxy to path...
        if (getProxyImpl().isFirstProxyBranchSetAndTest()) {
            // lets add the proxy context to the application path
            getRequestImpl().getDialog()
                .addToPath(getRequestImpl().getProxyContext());
        } else {
            // need to clone the dialog if this is not the first branch
            DialogFragment clone = (DialogFragment) getRequestImpl().getDialog()
                                                        .cloneFromCallerToCalleeUntil(getRequestImpl()
                                                                                          .getProxyContext(),
                    false);
            // update dialog of request...
            getRequestImpl().setDialog(clone);
            // need to add proper fragment id...
            getRequestImpl().setFragmentId(clone.getFragmentId());

            if (getRequestImpl().isContactIndicated()) {
                // since it's a new fragment id the contact must be replaced...
                Header contact = getRequestImpl().getRawHeader(Header.CONTACT);
                contact.setReadOnly(false);
                contact.removeValues();
                DialogManager.getInstance().addContact(getRequestImpl());
            }

            // need to update transaction path since there are new nodes.
            // lets start by cleaning the top of the stack...
            PathNode p = null;
            int size = 0;

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

            if (i.hasNext()) {
                // just skip the first since its this proxy context...
                i.next();
            }

            while (i.hasNext()) {
                p = i.next();

                if (getRequestImpl().getSupervised()) {
                    // this is the same as poping the stack, but poping the
                    // stack on a request will pop application stack
                    size = getRequestImpl()._transactionStack.size();
                    getRequestImpl()._transactionStack.remove(size - 1);
                }
            }

            // ...and replace it with the new nodes.
            i = clone.getCallee2CallerPath();

            if (i.hasNext()) {
                // update the request with the new clone...
                ProxyContext pc = (ProxyContext) i.next();
                getRequestImpl().setProxyContext(pc);
            }

            while (i.hasNext()) {
                // add the rest of the nodes..
                p = i.next();

                if (getRequestImpl().getSupervised()) {
                    getRequestImpl().pushTransactionDispatcher(p);
                }
            }
        }

        // Add the Proxy to transaction path of the request
        // so that a 487 to a Cancel is propagated
        // via the proxy as one of the candidates for
        // best response.
        _request.pushTransactionDispatcher(_request.getProxyContext());
       
        if (_log.isLoggable(Level.FINEST)) {
            _log.log(Level.FINEST,
                getRequestImpl().toDebugString() + ", " + toString());
        }
    }

    private void setProvisionalResponse(SipServletResponseImpl resp) {
        _provisional = resp;
    }

    private SipServletResponseImpl getProvisionalResponse() {
        return _provisional;
    }

    private SipServletRequestImpl createCancel() {
        SipServletRequestImpl cancel = _request.createCancelImpl();

        if (routesForCancel != null && !routesForCancel.isEmpty()) {
            MultiLineHeader routeHdr = new MultiLineHeader(Header.ROUTE, true);
            for (Address route : routesForCancel) {
                routeHdr.setAddressValue(route, false);
            }
            cancel.setHeader(routeHdr);
        }

        SipServletResponseImpl provisonal = getProvisionalResponse();

        // add saved top via from provisional response...
        if (provisonal.getCancelVia() != null) {
            cancel.setHeader(provisonal.getCancelVia());
        }

        // find next pathNode if any...
        PathNode next = provisonal.getPreviousVisited();

        if (next != null) {
            cancel.pushApplicationDispatcher(next);
            cancel.pushTransactionDispatcher(next);
        } else if (_request.getProxyContext() != null) {
          cancel.pushTransactionDispatcher(_request.getProxyContext());
        }

        // no need to keep reference to provisional anymore, save memory
        setProvisionalResponse(null);

        return cancel;
    }

    public boolean isPendingCancel() {
        return _pendingCancel;
    }

    public void setPendingCancel(Header reason) {
        _pendingCancel = true;
        _pendingCancelReason = reason;
    }

    private synchronized boolean isCanceled() {
        return _isCanceled;
    }

    private void setCanceled() {
        _isCanceled = true;
    }

    public void cancel(java.lang.String[] protocol, int[] reasonCode,
        java.lang.String[] reasonText) {
        Header reasonHeader = ProxyImpl.makeReasonHeader(protocol, reasonCode,
                reasonText);
        cancel(reasonHeader);
    }

    public void cancel() {
        cancel(null);
    }

    public void cancel(Header reasonHeader) {
        SipServletRequestImpl cancel = null;

        synchronized (this) {
            cancel = cancelIntern(reasonHeader);
        }

        if (cancel != null) {
            // lets CANCEL this branch...
            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE,
                    cancel + " will be sent for branch = " +
                    _request.getRequestURI());
            }

            cancel.popDispatcher().dispatch(cancel);

            synchronized (this) {
                setCanceled();
            }
        }
    }

    private SipServletRequestImpl cancelIntern(Header reason) {
        // don't need to CANCEL if branch has final
        // response already or it has been canceled
        // before or waiting for provisonal response
        SipServletRequestImpl cancel = null;

        if (!"INVITE".equals(_request.getMethod())) {
            // FIXME Consider stopping or extending timer F in NoninviteClientTransaction
            return null;
        }

        if (!isCanceled()) {
            // already final response?
            SipServletResponseImpl bestResp = getBestResponse();

            if (bestResp == null) {
                // no final response exist for the branch
                if (getProvisionalResponse() == null) {
                    // have not got provisonal response
                    if (_log.isLoggable(Level.FINE)) {
                        _log.log(Level.FINE,
                            "pending CANCEL for branch = " +
                            _request.getRequestURI());
                    }

                    // set pending cancel...
                    setPendingCancel(reason);
                } else {
                    // time to create CANCEL request
                    cancel = createCancel();

                    if (reason != null) {
                        cancel.addHeader(reason);
                    }

                    if (_log.isLoggable(Level.FINE)) {
                        _log.log(Level.FINE, cancel.toDebugString());
                    }
                }
            } else {
                if (_log.isLoggable(Level.FINE)) {
                    _log.log(Level.FINE,
                        "Will not CANCEL, already got final response " +
                        bestResp.getStatus() + " for current branch = " +
                        _request.getRequestURI());
                }
            }
        } else {
            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE,
                    "CANCEL already for branch = " + _request.getRequestURI());
            }
        }

        return cancel;
    }

    public void registerClientTransaction(ClientTransaction t) {
        this.clientTransaction = t;
    }
}
TOP

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

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.