/*
* 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.persistence;
import com.ericsson.ssa.sip.DialogFragment;
import com.ericsson.ssa.sip.PathNode;
import com.ericsson.ssa.sip.RemoteLockRuntimeException;
import com.ericsson.ssa.sip.SipApplicationSessionImpl;
import com.ericsson.ssa.sip.SipSessionImplBase;
import com.sun.appserv.ha.uow.ReplicableEntity;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Replication "unit of work"
* This class represents a collection of entities that are buffered to be replicated
*
* @author epetstr
*/
public class ReplicationUnitOfWork extends com.sun.appserv.ha.uow.ReplicationUnitOfWork {
private static final Logger logger = LogUtil.SIP_LOGGER.getLogger();
private Set<SipApplicationSessionImpl> dialogLocks;
/**
* When this class is instantiated it is automagically assigned to the current thread
* The caller should only need to call save() after invoking service
* or delegate the save() to the ReplicationManager.
*
*/
public ReplicationUnitOfWork() {
this(true);
}
/**
* Creates a UOW.
* @param setToThread if true the UOW is set on the current thread; otherwise it is not
*/
public ReplicationUnitOfWork(boolean setToThread) {
super(setToThread);
}
public static ReplicationUnitOfWork getThreadLocalUnitOfWork() {
return (ReplicationUnitOfWork) threadLocalReplicationUnits.get();
}
/**
* Saves the entities in this U-O-W and optionally unlocks the dialog.
* <p>
* Note! Clears the thread local if the dialog shall be unlocked (unlockDialog=true)
* @param unlockDialog
*/
private void save(boolean unlockDialog) {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "ENTER: " + this + ", unlockDialog: " + unlockDialog);
}
Collection<ReplicableEntity> localWorkSet = null;
Collection<SipApplicationSessionImpl> localDialogLock = null;
synchronized (this) {
// Make sets local to shorten lock duration
// and avoid possibility for deadlocks
if (workSet != null) {
localWorkSet = workSet;
workSet = null;
}
if (unlockDialog && (dialogLocks != null)) {
localDialogLock = dialogLocks;
dialogLocks = null;
}
}
if (localWorkSet != null) {
for (ReplicableEntity entity : localWorkSet) {
entity.save();
entity.unlockForeground();
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "Saved and unlocked entity: " + entity);
}
}
}
if (unlockDialog) {
if (localDialogLock != null) {
for (SipApplicationSessionImpl sas : localDialogLock) {
sas.unlockForeground();
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "Unlocked SipApplicationSession: " + sas);
}
}
}
clearThreadLocal();
}
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "EXIT: " + this);
}
}
/**
* Saves the entities in this U-O-W but does NOT unlock the dialog structure/SAS.
*/
public void save() {
save(false);
}
/**
* Saves the entities in this U-O-W and unlocks the dialog structure/SAS.
*/
public void saveAndUnlock() {
save(true);
}
/**
* Locks the dialog structure, i.e. al SAS:es reachable from the DF.
* @param df
*/
public synchronized void lockDialog(DialogFragment df) {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "ENTER: " + this + ", df: " + df);
}
try {
for (Iterator<PathNode> it = df.getCallee2CallerPath(); it.hasNext();) {
SipSessionImplBase ss = (SipSessionImplBase) it.next().getSipSession();
if ((ss == null) || !ss.isValid()) {
/**
* Issue 860 : If the session is already invalidated and
* removed from theSessionManager by a different thread,
* then we should not proceed further.
*/
unlock();
return;
}
SipApplicationSessionImpl sas = (SipApplicationSessionImpl) ss.getApplicationSession();
if (sas != null) {
if (dialogLocks == null) {
dialogLocks = new HashSet<SipApplicationSessionImpl>();
}
if (dialogLocks.add(sas)) {
sas.lockForegroundWithRetry();
}
} else {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "attempted to lock df {0} but it has no sas. Not obtaining the lock", new Object[] {
df
});
}
throw new IncompleteDialogException("Sip Application could not be found for for Sip Session with ID: "+ss.getId());
}
// Check that we still have the SAS (could theoretically have migrated between first loading above and
// before we got the lock). Just force a load and the RemoteLockRuntimeException.
ss.getApplicationSessionImpl();
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "Locked SipApplicationSession: " + sas);
}
}
} catch (RemoteLockRuntimeException e) {
unlock();
throw e;
} finally {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "EXIT: " + this);
}
}
}
/**
* Unlocks the dialog structure and optionally all dirty objects.
* <p>
* Note! Clears the thread local.
*/
public synchronized void unlock() {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "ENTER: " + this);
}
if (workSet != null) {
for (ReplicableEntity re : workSet) {
try {
re.unlockForeground();
} catch (Throwable e) {
// Ignore and continue with next at any price!
}
}
workSet = null;
}
if (dialogLocks != null) {
for (SipApplicationSessionImpl sas : dialogLocks) {
try {
sas.unlockForeground();
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "Unlocked SipApplicationSession: " + sas);
}
} catch (Throwable e) {
// Ignore and continue with next at any price!
}
}
dialogLocks = null;
}
clearThreadLocal();
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "EXIT: " + this);
}
}
/**
* Locks the specified SAS
* @param sas
*/
public synchronized void lockApplicationSession(SipApplicationSessionImpl sas) {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "ENTER: " + this + ", df: " + sas);
}
if (sas != null) {
if (dialogLocks == null) {
dialogLocks = new HashSet<SipApplicationSessionImpl>();
}
if (dialogLocks.add(sas)) {
sas.lockForegroundWithRetry();
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "Locked SipApplicationSession: " + sas);
}
}
}
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "EXIT: " + this);
}
}
@Override
public synchronized String toString() {
// Calling dialogLocks.toString() is thread unsafe, hence the
// unusal synchronization of this method
return super.toString() + "{dialogLocks=" + dialogLocks + "}";
}
}