Package com.ericsson.ssa.sip.transaction

Source Code of com.ericsson.ssa.sip.transaction.InviteClientTransaction

/*
* 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.transaction;

import com.ericsson.ssa.container.NetworkManager;
import com.ericsson.ssa.container.SipContainerThreadPool;
import com.ericsson.ssa.container.startup.SipMonitoring;
import com.ericsson.ssa.sip.DialogFragment;
import com.ericsson.ssa.sip.Dispatcher;
import com.ericsson.ssa.sip.Header;
import com.ericsson.ssa.sip.MultiLineHeader;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.ViaImpl;
import com.ericsson.ssa.sip.persistence.ReplicationUnitOfWork;
import com.ericsson.ssa.sip.timer.GeneralTimer;
import com.ericsson.ssa.sip.timer.GeneralTimerBase;
import static com.ericsson.ssa.sip.transaction.TransactionState.CALLING;
import static com.ericsson.ssa.sip.transaction.TransactionState.COMPLETED;
import static com.ericsson.ssa.sip.transaction.TransactionState.ESTABLISHED;
import static com.ericsson.ssa.sip.transaction.TransactionState.PENDING;
import static com.ericsson.ssa.sip.transaction.TransactionState.PROCEEDING;
import static com.ericsson.ssa.sip.transaction.TransactionState.TERMINATED;
import static com.ericsson.ssa.sip.transaction.TransactionTimer.TimerA;
import static com.ericsson.ssa.sip.transaction.TransactionTimer.TimerB;
import static com.ericsson.ssa.sip.transaction.TransactionTimer.TimerC;
import static com.ericsson.ssa.sip.transaction.TransactionTimer.TimerD;
import static com.ericsson.ssa.sip.transaction.TransactionTimer.TimerRemove;
import static com.ericsson.ssa.sip.transaction.TransactionTimer.TimerRemoveTransaction;

import java.util.ListIterator;
import java.util.concurrent.Callable;
import java.util.logging.Level;



/**
* @author ekrigro TODO To change the template for this generated type comment
*         go to Window - Preferences - Java - Code Style - Code Templates
*/
public class InviteClientTransaction extends ClientTransaction {
    private static final int MINIMUM_TIMERC_VALUE_MILLISECONDS = 180000; // 3 min according to spec
    private static final int EXTRA_DELTA = 20000; // Extra time to keep transaction open until proxy (or other) has had time to send cancel.
    private GeneralTimer _timerA; // T1 -> T1*2 ->
    private GeneralTimer _timerB; // 64*T1
    private GeneralTimer _timerC; // >3min
    private GeneralTimer _timerD; // 32s for UDP 0s
                                  // TCP
    private GeneralTimer _timerRemove; // 64*T1
    private GeneralTimer _timerRemoveTransaction; // 64*T1
    private SipServletRequestImpl _ack = null;
    private boolean _has101To199Response = false;
    private int cTimerDelay;

    /**
     * @param state
     * @param req
     */
    public InviteClientTransaction(String id, SipServletRequestImpl req) {
        super(id, CALLING, req);
        getRequest().saveRetransmissionApplicationStack();

        int reqTimerC = req.getTimerC();
        // Always start Timer C, Proxy or UAC, the CTimer always expires after any Proxy branch is canceled.
        calculateTimerCDelay(reqTimerC);

        _timerC = _timerService.createTimer(this, cTimerDelay, TimerC);
        prolongDialogSet(cTimerDelay);

        // start timer B
        _timerB = _timerService.createTimer(this, 64 * T1, TimerB);

        if (!_reliableTransport) {
            // Start timer A
            _timerA = _timerService.createTimer(this, T1, TimerA);
        }
    }

    private void calculateTimerCDelay(int proposedTimerCSeconds) {
        cTimerDelay = ((proposedTimerCSeconds * 1000) > MINIMUM_TIMERC_VALUE_MILLISECONDS)
            ? ((proposedTimerCSeconds * 1000) + EXTRA_DELTA)
            : (MINIMUM_TIMERC_VALUE_MILLISECONDS + EXTRA_DELTA);
    }

    /*
     * (non-Javadoc)
     *
     * @see com.ericsson.ssa.sip.Dispatcher#dispatch(com.ericsson.ssa.sip.SipServletResponseImpl)
     */
    public synchronized boolean handle(SipServletResponseImpl resp) {
        // For every response got from the stack have to associate it to the req
        resp.setRequest(getRequest());

        int status = resp.getStatus() / 100;
        int xx = resp.getStatus() % 100;

        switch (_state) {
        case CALLING:

            if (status == 1) {
                if (_timerA != null) {
                    _timerA.cancel();
                    _timerA = null;
                }

                if (_timerB != null) {
                    _timerB.cancel();
                    _timerB = null;
                }

                resetTimerC(xx);
                _state = PROCEEDING;
            } else if (status == 2) {
                toEstablished(resp);
            } else {
                toCompleted(resp); // 3xx to 6xx
            }

            break;

        case PROCEEDING:

            if (status == 1) {
                resetTimerC(xx);

                break;
            } else if (status == 2) {
                toEstablished(resp);
            } else {
                toCompleted(resp); // 3xx to 6xx
            }

            break;

        case COMPLETED: // 3XX - 6XX for UDP resent ACK

            if ((status >= 2) && (status <= 6)) {
                // it's acceptable to block the transaction
                // during the ack transmission
                createHopAck(resp);
            }

            return true; // Stop chain

        case ESTABLISHED:

            // could get 2xx from same or different end-points,
            // multiple 2xx responses may arraive
            break;

        case TERMINATED:
            break; // Should not get any response from TU in this state

        case PENDING:

            if (status != 1) {
                terminate();
            }

            break;

        default:
            _log.log(Level.FINE, "IllegalState in ICT = " + _state);
        }

        return false; // Continue up the chain
    }

    public void timeout(GeneralTimer timer) {
        TransactionTimer tt = (TransactionTimer) timer.getInfo();
        Dispatcher d = null;
        SipServletResponseImpl resp = null;
        SipServletRequestImpl req = null;

        switch (tt) {
        case TimerA:

            synchronized (this) {
                if (_state == CALLING) {
                    // Have to dirty cast in order not to reimplement hole
                    // structure
                    long delay = ((GeneralTimerBase) timer).getDelay();
                    // calculate next timer*2
                    delay *= 2;
                    // schedule new timer
                    _timerA = _timerService.createTimer(this, delay, TimerA);
                    // resend the request
                    getRequest().restoreRetransmissionApplicationStack();
                    req = (SipServletRequestImpl) getRequest().clone();
                }
            }

            if (req != null) {
                // dispatch after synch block...
                d = req.popDispatcher();

                if (d != null) {
                    d.dispatch(req);
                }

                if (SipMonitoring.isEnabled(SipMonitoring.TRANSACTION_MANAGER)) {
                    updateLastAccessTimestamp();
                }
            }

            break;

        case TimerB:

            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE, "Timer B fired - terminating()");
            }

            synchronized (this) {
                resp = getRequest().createTerminatingResponse(408);
                popVia(resp);
                resp.setInternalTransportFailure(true);
                terminate(false);
            }
           
            // NEW: act as if received from the network (but skip the transaction manager layer)
            // better to start a new thread???
            final SipServletResponseImpl respToSend = resp;
            SipContainerThreadPool.getInstance().execute(new Callable() {
                public Object call() throws Exception {
                    try {
                      // UOW will be set by the Replication manager
                      TransactionManager.getInstance().invokeNextLayer(respToSend);
                    } finally {
                        ReplicationUnitOfWork.clearThreadLocal();
                    }
                    return null;
                }
            });

            cleanup();

            break; // Time to do some GC

        case TimerC:

            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE, "Timer C fired - terminating()");
            }

            boolean isRequestProxied = !getRequest()
                                            .isContactIndicated();

            synchronized (this) {
                if ((_state == CALLING) || (_state == PROCEEDING)) {
                    if (_has101To199Response && isRequestProxied && !getRequest().isNoCancel()) {
                        req = sendCancel();
                    } else {
                        resp = sendRequestTimeout();
                        terminate(false);
                    }
                }
            }

            // dispatch after synch block...Only send CANCEL if proxy         
            // lets CANCEL this branch...
            if (req != null) {
                //Pending state handles the case when no final response is recived even after the CANCEL is sent.
                toPending();

                d = req.popDispatcher();

                if (d != null) {
                    dispatchInUOW(d, req);
                }
            } else if (resp != null) {
                //send 408 to terminate
                d = resp.popDispatcher();

                if (d != null) {
                    dispatchInUOW(d, resp);
                }

                cleanup();
            }

            break; // Time to do some GC

        case TimerD:
            terminate();

            break;

        case TimerRemoveTransaction:

            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE,
                    "Timer RemoveTransaction fired - terminating()");
            }

            terminate();

            break;

        case TimerRemove:

            // lets send 408 Request Timeout...
            if (_log.isLoggable(Level.FINE)) {
                _log.log(Level.FINE,
                    "TimerRemove fired - send 408 Request Timeout");
            }

            resp = sendRequestTimeout();
            terminate(false);

            d = resp.popDispatcher();

            if (d != null) {
                dispatchInUOW(d, resp);
            }

            cleanup();

            break;

        default:
            _log.log(Level.FINE, "IllegalTimer in ICT = " + tt);
        }
    }

    /**
     * If provisonal response (except 100) and proxying or UAC, Timer C will be reset
     *
     * @param xx
     *        the xx of the 1xx response status code
     */
    private void resetTimerC(int xx) {
        if ((xx != 0)) {
            // reset Timer C
            if (_timerC != null) {
                _timerC.cancel();
            }

            _timerC = _timerService.createTimer(this, cTimerDelay, TimerC);
            _has101To199Response = true;
            prolongDialogSet(cTimerDelay);
        }
    }

    public void prolongTimerC(int newTimerCSeconds) {
        calculateTimerCDelay(newTimerCSeconds);

        if (_timerC != null) {
            _timerC.cancel();
        }

        _timerC = _timerService.createTimer(this, cTimerDelay, TimerC);
        prolongDialogSet(cTimerDelay);
    }

    private void prolongDialogSet(long delay) {
        DialogFragment d = getDialog();

        if ((d != null) && !d.isConfirmed() && d.isValid()) { // also check for valid dialog
            d.getDialogSet().prolongLifetime(delay + 10000);
        }
    }

    private SipServletRequestImpl sendCancel() {
        Header via = getRequest().getRawHeader(Header.VIA);
        ListIterator<String> li = via.getValues();
        String topVia = li.next();

        if (topVia != null) {
            SipServletRequestImpl cancel = getRequest().createCancelImpl();

            // set top via of original request to CANCEL
            Header viaOfCancel = new MultiLineHeader(Header.VIA, true);
            ViaImpl v = new ViaImpl(topVia);
            viaOfCancel.setValue(v.toString(), true);
            cancel.setHeader(viaOfCancel);

            return cancel;
        } else {
            _log.log(Level.SEVERE,
                "Can not send CANCEL because INVITE does not have via: " +
                getRequest().toString());

            return null;
        }
    }

    private SipServletResponseImpl sendRequestTimeout() {
        SipServletResponseImpl resp = getRequest().createTerminatingResponse(408);

        popVia(resp);

        // set session...
        SipServletRequestImpl req = getRequest().getTransactionRequest();

        //    TR HH52078
        if ((req != null) && (resp != null)) {
            resp.setRequest(req);
            resp.setSession(req.getSessionImpl());
        }

        return resp;
    }

    protected synchronized void terminate() {
        terminate(true);
    }

    /* TODO check synchronization */
    protected synchronized void terminate(boolean cleanup) {
        super.terminate(cleanup);

        if (_timerRemove != null) {
            _timerRemove.cancel();
            _timerRemove = null;
        }

        if (_timerA != null) {
            _timerA.cancel();
            _timerA = null;
        }

        if (_timerB != null) {
            _timerB.cancel();
            _timerB = null;
        }

        if (_timerC != null) {
            _timerC.cancel();
            _timerC = null;
        }

        if (_timerD != null) {
            _timerD.cancel();
            _timerD = null;
        }

        if (_timerRemoveTransaction != null) {
            _timerRemoveTransaction.cancel();
            _timerRemoveTransaction = null;
        }

        _state = TERMINATED;
    }

    private void toCompleted(SipServletResponseImpl resp) {
        // it's acceptable to block the transaction
        // during the ack transmission
        createHopAck(resp);

        if (_timerB != null) {
            _timerB.cancel();
            _timerB = null;
        }

        if (_timerC != null) {
            _timerC.cancel();
            _timerC = null;
        }

        if (!_reliableTransport) { // Start timer D

            if (SipMonitoring.isEnabled(SipMonitoring.TRANSACTION_MANAGER)) {
                updateLastAccessTimestamp();
            }

            if (_timerA != null) {
                _timerA.cancel();
                _timerA = null;
            }

            _state = COMPLETED;
            _timerD = _timerService.createTimer(this, 64 * T1, TimerD);
        } else {
            _state = TERMINATED; // TimerD = 0
            super.terminate();
        }
    }

    private void toEstablished(SipServletResponseImpl resp) {
        if (_timerB != null) {
            _timerB.cancel();
            _timerB = null;
        }

        if (!_reliableTransport) {
            if (SipMonitoring.isEnabled(SipMonitoring.TRANSACTION_MANAGER)) {
                updateLastAccessTimestamp();
            }

            if (_timerA != null) {
                _timerA.cancel();
                _timerA = null;
            }
        }

        // Start timer RemoveTransaction
        long delay = 64 * T1;
        _timerRemoveTransaction = _timerService.createTimer(this, delay,
                TimerRemoveTransaction);
        prolongDialogSet(delay);
        _state = ESTABLISHED;
    }

    private void toPending() {
        long delay = 64 * T1;
        _timerRemoveTransaction = _timerService.createTimer(this, delay,
                TimerRemove);
        prolongDialogSet(delay);
        _state = PENDING;
    }

    private void createHopAck(SipServletResponseImpl response) {
      if (response.isInternalTransportFailure()) {
        // skipping hop ack for internally generated transport errors
        return
      }
        if (_ack == null) {
            _ack = response.createHopAckImpl(getRequest());
        }

        // send it directly out on the network...
        NetworkManager.getInstance().dispatch(_ack);
    }
}
TOP

Related Classes of com.ericsson.ssa.sip.transaction.InviteClientTransaction

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.