Package com.ericsson.ssa.sip.dialog

Source Code of com.ericsson.ssa.sip.dialog.DialogLifeCycle

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

import com.ericsson.ssa.sip.DialogFragment;
import com.ericsson.ssa.sip.DialogFragmentManager;
import com.ericsson.ssa.sip.PathNode;
import com.ericsson.ssa.sip.RemoteLockException;
import com.ericsson.ssa.sip.SipApplicationSessionImpl;
import com.ericsson.ssa.sip.SipSessionDialogImpl;
import com.ericsson.ssa.sip.SipSessionImplBase;
import com.ericsson.ssa.sip.persistence.ReplicationUnitOfWork;
import com.ericsson.ssa.sip.transaction.TransactionManager;

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

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
* This is the life cycle of a dialog. It handles various activities:
* - keeping track of UOW on dialog (inside or outside transaction)
* - orchestrating locking/unlocking and saving
* - handle removal of dialog structure at invalidation
* - (future) handle removal of obsolete DialogSet.
*
*/
public class DialogLifeCycle implements Cleanable {
    private final static Logger logger = LogUtil.SIP_LOGGER.getLogger();
    private volatile boolean removeDialog = false;
    private ReplicationUnitOfWork uow = new ReplicationUnitOfWork(false);
    private DialogFragment df;
    private Set<String> ongoingTransactionIds = new LinkedHashSet<String>();

    public DialogLifeCycle(DialogFragment df) {
        this.df = df;
    }

    /**
     * Mark this dialog to be removed when all transactions have finished (or
     * in case no transactions exist: when supervision period expires).
     */
    public void markRemoveWhenFinished() {
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "ENTER: {" + this + "}");
        }

        boolean doRegister = false;

        synchronized (this) {
            if (!removeDialog) { // Only do this the first time

                if (ongoingTransactionIds.isEmpty()) {
                    doRegister = true;
                }

                removeDialog = true;
            }
        }

        if (doRegister) {
            DialogCleaner.getInstance().registerForSupervision(this);
        }

        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "EXIT: {" + this + "}");
        }
    }

    /**
     * Associates the transaction to this dialog and connects the life cycle (
     * save UOW, release locks) with that transaction (and other possible
     * transactions working on the dialog).
     *
     * Unregister from supervision.
     *
     * @param transactionId
     */
    public void associateTransaction(String transactionId) {
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "ENTER: transactionId: " + transactionId + " {" + this + "}");
        }

        synchronized (this) {
            if ((transactionId == null) || !TransactionManager.getInstance().transactionExists(transactionId)) {
                if (logger.isLoggable(Level.FINEST)) {
                    logger.log(Level.FINEST, "Tried to associated transaction: " + transactionId + " with dialog: " + df.getDialogId() + " but transaction did not exist.");
                }

                return;
            } else {
                if (logger.isLoggable(Level.FINEST)) {
                    logger.log(Level.FINEST, "Associated transaction: " + transactionId + " with dialog: " + df.getDialogId());
                }

                ongoingTransactionIds.add(transactionId);
            }
        }

        DialogCleaner.getInstance().unregisterFromSupervision(this);

        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "EXIT: {" + this + "}");
        }
    }

    /**
     * Acts on the event that a transaction has been removed. If this was the
     * last of the associated transactions a possible UOW is saved and the
     * dialog is unlocked.
     *
     * @param transactionId
     * @param delay indicates that a possible finish shall be delayed until
     *                {@link #trigDelayedFinish()} is called; however, it will
     *                register the DLC for supervision.
     */
    public void onTransactionRemoved(String transactionId, boolean delay) {
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "ENTER: transactionId: " + transactionId + ", delay: " + delay + " {" + this + "}");
        }

        boolean doFinish = false;

        synchronized (this) {
            ongoingTransactionIds.remove(transactionId);

            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, "Removed transaction: " + transactionId + " from dialog: " + df.getDialogId());
            }

            if (ongoingTransactionIds.isEmpty()) {
                doFinish = true;
            }
        }

        if (doFinish) {
            if (delay) {
                // Delay until trigDelayedFinish() has been passed
                DialogCleaner.getInstance().registerForSupervision(this);
            } else {
                handleFinish();
            }
        }

        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "EXIT: {" + this + "}");
        }
    }

    public void trigDelayedFinish() {
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "ENTER: {" + this + "}");
        }

        boolean doFinish = false;

        synchronized (this) {
            if (ongoingTransactionIds.isEmpty()) {
                doFinish = true;
            }
        }

        if (doFinish) {
            DialogCleaner.getInstance().unregisterFromSupervision(this);
            handleFinish();
        }

        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "EXIT: {" + this + "}");
        }
    }

    /**
     * Force a save of the UOW (but keep the dialog lock).
     */
    public void save() {
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "ENTER: {" + this + "}");
        }

        uow.save();

        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "EXIT: {" + this + "}");
        }
    }

    /**
     * Acts on timeout from the supervision of dialogs that have no transaction
     * associated.
     */
    public void doCleanup() {
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "ENTER: {" + this + "}");
        }

        handleFinish();

        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "EXIT: {" + this + "}");
        }
    }

    /**
     * Starts a unit of work on this dialog.
     * @throws RemoteLockException
     */
    public void initUnitOfWork() {
        initUnitOfWork(null);
    }

    /**
     * Starts a unit of work on this dialog. Optionally locks dialog structure
     * based on the SAS.
     *
     * @param sas the SAS making up the dialog structure (for initial requests
     *                where no application path has been established); if null
     *                the dialog structure is deduced from the DF.
     * @throws RemoteLockException
     */
    public void initUnitOfWork(SipApplicationSessionImpl sas) {
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "ENTER: sas: " + sas + " {" + this + "}");
        }

        if (sas == null) {
            uow.lockDialog(df);
        } else {
            uow.lockApplicationSession(sas);
        }

        boolean doRegister = false;

        synchronized (this) {
            if (ongoingTransactionIds.isEmpty()) {
                doRegister = true;
            }
        }

        if (doRegister) {
            DialogCleaner.getInstance().registerForSupervision(this);
        }

        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "EXIT: {" + this + "}");
        }
    }

    /**
     * Activates the UOW of this dialog on the current thread, i.e. sets the thread local UOW.
     */
    public void setThreadLocalUnitOfWork() {
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "ENTER: {" + this + "}");
        }

        uow.setThreadLocal();

        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "EXIT: {" + this + "}");
        }
    }

    DialogFragment getDialogFragment() {
        return df;
    }

    private void handleFinish() {
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "Handle finish {" + this + "}");
        }

        uow.saveAndUnlock();

  boolean localRemove = false;
  synchronized(this) {
      localRemove = removeDialog;
      removeDialog = false;
  }
        if (localRemove) {
            removeDialog(df);
        }
    }

    /**
     * Checks if this dialog is associated with at least one ongoing transaction.
     * @return true if this UOW is associated with at least one ongoing transaction.
     */
    synchronized boolean hasOngoingTransaction() {
        return !ongoingTransactionIds.isEmpty();
    }

    private void removeDialog(DialogFragment df) {
        if (logger.isLoggable(Level.FINER)) {
            logger.log(Level.FINER, "Checked invalidation of dialog: " + df.getDialogId());
        }

        boolean validSSExists = false;
        boolean forcedInvalidation = false;

        for (Iterator<PathNode> pnIt = df.getCallee2CallerPath(); pnIt.hasNext();) {
            SipSessionDialogImpl ss;

            try {
                ss = (SipSessionDialogImpl) (SipSessionImplBase) pnIt.next().getSipSession();

                if (ss != null) {
                    if (ss.isValid()) {
                        validSSExists = true;
                    } else {
                        if (ss.invalidatedDueIWR() == false) {
                            // we have got a explicit or sas expire invalidation
                            // I.e SipSession ends before sip dialog ends, a.k.a "forcedInvalidation"
                            forcedInvalidation = true;

                            break;
                        } else {
                            ss.doCleanup();
                        }
                    }
                } else {
                    // This is quite OK if there is two or more sipsessions in df have different lifecycles
                    if (logger.isLoggable(Level.FINEST)) {
                        logger.log(Level.FINEST, "When removing dialog" + df.getDialogId() + ": Found a reference from a path node to a SipSession which was not found. Ignored, this is quite OK if there is two or more sipsessions in df having different lifecycles");
                    }
                }
            } catch (Throwable t) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "Exception at invalidation" + t + "; Continue with all other sessions in the DF");
                }
            }
        }

        if (forcedInvalidation) {
            // go through all SS'es again and this time invalidate all of them, no mercy :-)
            forcedInvalidate(df);
            validSSExists = false;
        }

        if (validSSExists && (df.getSessionCount() >= 1)) {
            // We keep the df if there is a valid ss still referring to it
            // But we also check the reference count to cater for the ss.reset() case
            // (ss.reset() means we will have a valid ss but df ref counter is down to zero --> df must be removed
            if (logger.isLoggable(Level.FINEST)) {
                logger.log(Level.FINEST, "Valid SipSession(s) remains, keeping DialogFragment: " + df.getDialogId());
            }
        } else {
            DialogFragmentManager.getInstance().removeDialogFragment(df, false);

            if (logger.isLoggable(Level.FINEST)) {
                logger.log(Level.FINEST, "Removed DialogFragment: " + df.getDialogId());
            }
        }
    }

    private void forcedInvalidate(DialogFragment df) {
        for (Iterator<PathNode> pnIt = df.getCallee2CallerPath(); pnIt.hasNext();) {
            SipSessionDialogImpl ss;

            try {
                ss = (SipSessionDialogImpl) (SipSessionImplBase) pnIt.next().getSipSession();

                if (ss != null) {
                    if (ss.isValid()) {
                        if (logger.isLoggable(Level.FINEST)) {
                            logger.log(Level.FINEST, "Forced invalidation of SipSession involved in a dialog, id: " + ss.getId());
                        }

                        ss.invalidate();
                    }

                    ss.doCleanup();
                }
            } catch (Throwable t) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "Exception at invalidation" + t + "; Continue with all other sessions in the DF");
                }
            }
        }
    }

    /**
     * Checks that this UOW is the current UOW in the thread.
     */
    public void checkUnitOfWork() {
        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "ENTER: {" + this + "}");
        }

        if (ReplicationUnitOfWork.getThreadLocalUnitOfWork() != uow) {
            assert false : "The thread local unit of work: " + ReplicationUnitOfWork.getThreadLocalUnitOfWork() + " is not the same as the local unit of work: " + uow;

            if (logger.isLoggable(Level.WARNING)) {
                logger.log(Level.WARNING, "The thread local unit of work: " + ReplicationUnitOfWork.getThreadLocalUnitOfWork() + " is not the same as the local unit of work: " + uow);
            }
        }

        if (logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "EXIT: {" + this + "}");
        }
    }

    @Override
    public String toString() {
        return super.toString()+"{df=" + df.getDialogId() + ",trs=" + ongoingTransactionIds + ", uow=" + uow + ", removeDialog=" + removeDialog+"}";
    }
}
TOP

Related Classes of com.ericsson.ssa.sip.dialog.DialogLifeCycle

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.