/*
* 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.config.ConvergedContext;
import com.ericsson.ssa.config.ConvergedContextImpl;
import com.ericsson.ssa.container.datacentric.DataCentricUtilHolder;
import com.ericsson.ssa.sip.PathNode.Type;
import com.ericsson.ssa.sip.SipApplicationSessionImpl;
import com.ericsson.ssa.sip.SipFactoryImpl;
import com.ericsson.ssa.sip.SipSessionsUtilImpl;
import com.ericsson.ssa.sip.timer.GeneralTimer;
import com.ericsson.ssa.sip.timer.GeneralTimerListener;
import com.ericsson.ssa.sip.timer.ServletTimerImpl;
import com.ericsson.ssa.sip.timer.TimerServiceImpl;
import com.ericsson.ssa.utils.UUIDUtil;
import com.sun.enterprise.config.ConfigContext;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.serverbeans.Cluster;
import com.sun.enterprise.config.serverbeans.ClusterHelper;
import com.sun.enterprise.ee.web.sessmgmt.EEPersistenceTypeResolver;
import com.sun.enterprise.server.ServerContext;
import com.sun.enterprise.web.ServerConfigLookup;
import org.apache.catalina.Container;
import org.apache.catalina.Globals;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.util.LifecycleSupport;
import org.glassfish.comms.api.datacentric.DataCentricUtil;
import org.jvnet.glassfish.comms.deployment.backend.SipApplicationListeners;
import org.jvnet.glassfish.comms.gms.EventListener;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.io.Serializable;
import java.security.AccessController;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.sip.Address;
import javax.servlet.sip.ServletTimer;
import javax.servlet.sip.SipApplicationSession;
import javax.servlet.sip.SipApplicationSessionEvent;
import javax.servlet.sip.SipApplicationSessionListener;
import javax.servlet.sip.SipSession;
import javax.servlet.sip.TimerListener;
/**
* Base implementation of the SipSessionManager interface.
*
* @author jluehe
*/
public class SipSessionManagerBase implements SipSessionManager, Lifecycle {
protected static final Logger logger = LogUtil.SIP_LOGGER.getLogger();
protected SipFactoryImpl sipFactory = null;
// Has this SipSessionManager been started?
protected boolean started;
// Has this SipSessionManager been initialized?
protected boolean initialized;
protected int sessionTimeout;
// The associated converged context
protected ConvergedContextImpl convergedContext;
// The lifecycle event support.
protected LifecycleSupport lifecycle = new LifecycleSupport(this);
/** Handler for managing obsolote sessions. */
private EventListener obsoleteSessionHandler;
/*
* Active cache of SipApplicationSessions.
*
* 2-arg (<initialCapacity, loadFactor>) constructor not available on
* Java 5, specify DEFAULT_CONCURRENCY_LEVEL as 3rd argument
*/
protected ConcurrentHashMap<String, SipApplicationSessionImpl> applicationSessions = new ConcurrentHashMap<String, SipApplicationSessionImpl>(100, 0.75f, 16);
// Active cache of SipSessions
protected Map<String, SipSessionDialogImpl> sipSessions = new ConcurrentHashMap<String, SipSessionDialogImpl>();
// Active cache of ServletTimers
protected Map<String, ServletTimerImpl> servletTimers = new ConcurrentHashMap<String, ServletTimerImpl>();
protected boolean isRepairDuringFailure = true;
// The application id
protected String applicationId = null;
// The cluster id
protected String clusterId = null;
protected boolean isBeingReleased = false;
/**
* Attach to the Group Membership Service subsystem.
* Attachment to the GMS subsystem is only if data centric balancing is enabled and Session Replication is not enabled.
*/
private void attachEventListener() {
if (EEPersistenceTypeResolver.MEMORY_TYPE.equals(getPersistenceType()) &&
DataCentricUtilHolder.getInstance().isDataCentricEnabled()) {
obsoleteSessionHandler = new ObsoleteSessionHandler();
DataCentricUtilHolder.addEventListener(obsoleteSessionHandler);
}
}
/**
* Associates the given ConvergedContext with this SipSessionManager.
*
* @param context The ConvergedContext with which to associate this
* SipSessionManager
*/
public void setContext(ConvergedContext context) {
convergedContext = (ConvergedContextImpl) context;
}
/**
* Gets the ConvergedContext with which this SipSessionManager has been
* associated.
*
* @return The ConvergedContext with which this SipSessionManager has
* been associated.
*/
public ConvergedContext getContext() {
return convergedContext;
}
/**
* Creates a new SipApplicationSession, and adds it to this session
* manager's active cache.
*
* @param sipApplicationListeners The SipApplicationListeners whose
* SipApplicationSessionListeners need to be notified of the session
* creation
*
* @return The new SipApplicationSession
*/
public SipApplicationSessionImpl createSipApplicationSession(
SipApplicationListeners sipApplicationListeners) {
String bekey = DataCentricUtilHolder.getInstance().getLocalKey();
String id = SipApplicationSessionUtil.createSasId(
bekey, convergedContext.getAppName(), UUIDUtil.randomUUID());
SipApplicationSessionImpl sess = createNewSipApplicationSession(id);
// Add to active cache
initSipApplicationSession(sess, sipApplicationListeners);
addSipApplicationSession(sess);
return sess;
}
/**
* Creates a new SipApplicationSession with the given id, and adds it to
* this session manager's active cache.
*
* @param id The id to assign to the new SipApplicationSession
* @param sipApplicationListeners The SipApplicationListeners whose
* SipApplicationSessionListeners need to be notified of the session
* creation
*
* @return The new SipApplicationSession
*/
public SipApplicationSessionImpl createSipApplicationSession(
String id, SipApplicationListeners sipApplicationListeners) {
if (id == null) {
return createSipApplicationSession(sipApplicationListeners);
}
SipApplicationSessionImpl sess = null;
sess = applicationSessions.get(id);
if (sess == null) {
sess = createNewSipApplicationSession(id);
SipApplicationSessionImpl newsess =
applicationSessions.putIfAbsent(id, sess);
if (newsess != null) {
sess = newsess;
}
}
initSipApplicationSession(sess, sipApplicationListeners);
return sess;
}
/**
* Gets the SipApplicationSession with the given id.
*
* @return The SipApplicationSession with the given id, or null if not
* found
* @throws RemoteLockException
*/
public SipApplicationSessionImpl findSipApplicationSession(String id) throws RemoteLockException {
SipApplicationSessionImpl appSessImpl = applicationSessions.get(id);
if (appSessImpl == null)
return null;
initSipApplicationSession(appSessImpl, null);
return appSessImpl;
}
/**
* Gets the SipApplicationSession with the given id.
*
* @return The SipApplicationSession with the given id, or null if not
* found
* @throws RemoteLockException
*/
public SipApplicationSessionImpl findSipApplicationSession(String id, boolean force) throws RemoteLockException {
return findSipApplicationSession(id);
}
/**
* Removes the given SipApplicationSession from this session manager's
* active cache.
*
* @param sas The SipApplicationSession to be removed
*/
public void removeSipApplicationSession(SipApplicationSessionImpl sas) {
SipApplicationSessionImpl appSessImpl = applicationSessions.get(sas.getId());
if (sas == appSessImpl) {
SipApplicationSessionImpl val = applicationSessions.remove(sas.getId());
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "The application session with ID=" + sas.getId() + " has been removed from the active cache ref:"
+ sas.getObjectRefString());
}
} else {
// With sessionKey (SAK) feature of jsr289 it is possible to recreate a sas with a given ID
// When a sas is invalidated and requests targets same sas then ServletDispatcher may find the invalidated sas.
// In this case it removes the sas from manager and re-create a new sas instance with same ID.
// But the invalidated sas is also supervised by DialogCleaner.
// This fix checks that a removal via DialogCleaner only removes the instance that was invalidated
// and not the newly created one.
// Note. This class also has potensial non-atomic check-than-act issues but it was decided not to change synchronization policy for resolution of
// this Issue.
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "Ignore removal of sas with id {0} and referece {1}", new Object[] { sas.getId(), sas.getObjectRefString() });
}
}
SipSessionsUtilImpl ssu = convergedContext.getSipSessionsUtil();
if (ssu != null) {
ssu.removeSessionsMapping(sas);
}
}
/**
* Adds the given SipApplicationSession to this session manager's
* active cache.
*
* @param sas The SipApplicationSession to add
*
* @return the previous SipApplicationSession, or null
*/
public SipApplicationSession addSipApplicationSession(SipApplicationSessionImpl sas) {
SipApplicationSession ret = null;
if (sas != null) {
ret = applicationSessions.put(sas.getId(), sas);
}
return ret;
}
/**
* Creates a new SipSession and adds it to the given SipApplicationSession
* and this session manager's active cache.
*
* @param set
* @param to
* @param appSession
* @param handler
*
* @return The new SipSession
*/
public SipSessionDialogImpl createSipSession(DialogSet set, Address to, SipApplicationSessionImpl appSession, String handler) {
return createSipSession(set, to, appSession, handler, null);
}
/**
* Creates a new SipSession and adds it to the given SipApplicationSession
* and this session manager's active cache.
*
* @param set
* @param to
* @param appSession
* @param handler
* @type type
*
* @return The new SipSession
*/
public SipSessionDialogImpl createSipSession(DialogSet set, Address to, SipApplicationSessionImpl appSession, String handler, Type type) {
SipSessionDialogImpl sess = null;
if (type != null) {
sess = createNewSipSession(set, to, appSession, handler, type);
} else {
sess = createNewSipSession(set, to, appSession, handler, Type.Undefined);
}
sipSessions.put(sess.getId(), sess);
return sess;
}
/**
* Gets the SipSession with the given id.
*
* @return The SipSession with the given id, or null if not found
*/
public SipSessionDialogImpl findSipSession(String id) throws RemoteLockException {
return sipSessions.get(id);
}
public SipSessionDialogImpl findSipSession(String id, boolean loadDependencies) throws RemoteLockException {
return findSipSession(id);
}
/**
* Removes the given SipSession from this session manager's active cache.
*
* The SipSession is also removed from its SipApplicationSession parent,
* if it had been attached to any.
*
* @param sipSession The SipSession to remove
*/
public void removeSipSession(SipSessionDialogImpl sipSession) {
if (sipSession != null) {
sipSessions.remove(sipSession.getId());
}
}
/**
* Removes the given SipSession from this session manager's active cache.
*
* The SipSession is also removed from its SipApplicationSession parent,
* if it had been attached to any.
*
* @param sipSessionId The id of the SipSession to remove
*/
public void removeSipSession(String sipSessionId) {
if (sipSessionId != null) {
sipSessions.remove(sipSessionId);
}
}
/**
* Adds the given SipSession to this session manager's active cache.
*
* @param sipSession The SipSession to add
*
* @return the previous SipSession, or null
*/
public SipSession addSipSession(SipSessionDialogImpl sipSession) {
SipSession ret = null;
if (sipSession != null) {
ret = sipSessions.put(sipSession.getId(), sipSession);
}
return ret;
}
/**
* Creates a new ServletTimer.
*
* @return The new ServletTimer
*/
public ServletTimerImpl createServletTimer(SipApplicationSessionImpl sas, Serializable info, long delay, TimerListener listener, boolean isPersistent) {
ServletTimerImpl timer = createNewServletTimer(sas, info, delay, listener, isPersistent);
servletTimers.put(timer.getId(), timer);
return timer;
}
/**
* Creates a new ServletTimer.
*
* @return The new ServletTimer
*/
public ServletTimerImpl createServletTimer(SipApplicationSessionImpl sas, Serializable info, long delay, boolean fixedDelay, long period, TimerListener listener, boolean isPersistent) {
ServletTimerImpl timer = createNewServletTimer(sas, info, delay, fixedDelay, period, listener, isPersistent);
servletTimers.put(timer.getId(), timer);
return timer;
}
/**
* Gets the ServletTimer with the given id.
*
* @return The ServletTimer with the given id, or null if not found
*/
public ServletTimerImpl findServletTimer(String id) throws RemoteLockException {
return servletTimers.get(id);
}
public ServletTimerImpl findServletTimer(String id, boolean loadDependencies) throws RemoteLockException {
return findServletTimer(id);
}
/**
* Removes the given ServletTimer.
*
* @param timer The ServletTimer to be removed
*/
public void removeServletTimer(ServletTimerImpl timer) {
if (timer != null) {
servletTimers.remove(timer.getId());
}
}
/**
* Adds the given ServletTimer to this session manager's
* active cache.
*
* @param timer The ServletTimer to add
*
* @return the previous ServletTimer, or null
*/
public ServletTimer addServletTimer(ServletTimerImpl timer) {
ServletTimer ret = null;
if (timer != null) {
ret = servletTimers.put(timer.getId(), timer);
}
return ret;
}
/**
* Sets the session timeout.
*
* @param timeout The session timeout
*/
public void setSessionTimeout(int timeout) {
sessionTimeout = timeout;
}
/**
* Gets the session timeout.
*
* @return The session timeout
*/
public int getSessionTimeout() {
return sessionTimeout;
}
/**
* Sets the repairDuringFailure property of this session manager.
*
* @param isRepairDuringFailure The value of the repairDuringFailure
* property
*/
public void setRepairDuringFailure(boolean isRepairDuringFailure) {
this.isRepairDuringFailure = isRepairDuringFailure;
}
/**
* @return True if this session manager should participate in a repair
* during failure, and false otherwise
*/
public boolean isRepairDuringFailure() {
return isRepairDuringFailure;
}
/**
* Starts this SipSessionManager.
*
* This method must not be called outside the lifecycle of the
* associated converged context.
*/
public void start() throws LifecycleException {
if (!initialized) {
init();
}
if (started) {
return;
}
started = true;
sipFactory = SipFactoryImpl.getInstance();
SipSessionManagerRegistry reg = null;
if (Globals.IS_SECURITY_ENABLED) {
reg = (SipSessionManagerRegistry) AccessController.doPrivileged(new PrivilegedGetSipSessionManagerRegistry());
} else {
reg = SipSessionManagerRegistry.getInstance();
}
if (reg != null) {
reg.register(this);
}
}
/**
* Stops this SipSessionManager.
*
* This method must not be called outside the lifecycle of the
* associated converged context.
*/
public void stop() throws LifecycleException {
if (!started) {
throw new LifecycleException("Not started");
}
started = false;
SipSessionManagerRegistry reg = null;
if (Globals.IS_SECURITY_ENABLED) {
reg = (SipSessionManagerRegistry) AccessController.doPrivileged(new PrivilegedGetSipSessionManagerRegistry());
} else {
reg = SipSessionManagerRegistry.getInstance();
}
if (reg != null) {
reg.unregister(this);
}
// TBD Clean up sessions, etc.
}
/**
* Releases any resources held by this SipSessionManager during
* undeployment.
*
* This method also invalidates all of the active SipSessions managed
* by this SipSessionManager. The invalidation of a SipSession will
* in turn trigger the invalidation of its associated DialogFragment,
* which causes the DialogFragment to be removed from its manager.
*/
public void release() {
isBeingReleased = true;
if (obsoleteSessionHandler != null) {
DataCentricUtilHolder.removeEventListener(obsoleteSessionHandler);
}
synchronized (applicationSessions) {
Iterator<SipApplicationSessionImpl> it = applicationSessions.values().iterator();
while (it.hasNext()) {
try {
it.next().invalidate();
} catch (IllegalStateException ise) {
// Ignore
}
}
}
try {
DialogFragmentManager.getInstance().removeDialogFragments(getApplicationId());
} catch (IllegalStateException ise) {
// Ignore
}
// Clear all active sessions and timers
applicationSessions.clear();
sipSessions.clear();
servletTimers.clear();
}
/**
* Adds a lifecycle event listener to this SipSessionManager.
*
* @param listener The lifecycle event listener to add
*/
public void addLifecycleListener(LifecycleListener listener) {
lifecycle.addLifecycleListener(listener);
}
/**
* Gets the lifecycle event listeners of this SipSessionManager.
*
* @return Array (possibly of zero length) containing the lifecycle
* event listeners of this SipSessionManager
*/
public LifecycleListener[] findLifecycleListeners() {
return lifecycle.findLifecycleListeners();
}
/**
* Removes the give lifecycle event listener from this
* SipSessionManager.
*
* @param listener The lifecycle event listener to remove
*/
public void removeLifecycleListener(LifecycleListener listener) {
lifecycle.removeLifecycleListener(listener);
}
/**
* Creates a new SipSession.
*
* @param set
* @param to
* @param appSession
* @param handler
* @type type
*
* @return The new SipSession
*/
protected SipSessionDialogImpl createNewSipSession(DialogSet set, Address to, SipApplicationSessionImpl appSession, String handler, Type type) {
return new SipSessionDialogImpl(this, set, to, appSession, handler, type);
}
/**
* Creates a new SipApplicationSession.
*
* @param id The id of the new SipApplicationSession
*
* @return The new SipApplicationSession
*/
protected SipApplicationSessionImpl createNewSipApplicationSession(String id) {
return new SipApplicationSessionImpl(this, id);
}
/**
* Creates a new ServletTimer.
*
* @return The new ServletTimer
*/
protected ServletTimerImpl createNewServletTimer(SipApplicationSessionImpl sas, Serializable info, long delay, TimerListener listener, boolean isPersistent) {
return new ServletTimerImpl(this, sas, info, delay, listener, isPersistent);
}
/**
* Creates a new ServletTimer.
*
* @return The new ServletTimer
*/
protected ServletTimerImpl createNewServletTimer(SipApplicationSessionImpl sas, Serializable info, long delay, boolean fixedDelay, long period, TimerListener listener, boolean isPersistent) {
return new ServletTimerImpl(this, sas, info, delay, fixedDelay, period, listener, isPersistent);
}
/**
* Gets the persistence type of this session manager.
*
* @return The persistence type of this session manager.
*/
public String getPersistenceType() {
return EEPersistenceTypeResolver.MEMORY_TYPE;
}
public String getApplicationId() {
if (applicationId != null) {
return applicationId;
}
Container container = getContext();
StringBuilder sb = new StringBuilder(50);
//prepend "SIP:" to distinguish from non-sip manager
sb.append("SIP:");
String clusterId = getClusterId();
if (clusterId != null) {
sb.append(getClusterId());
}
ArrayList<String> list = new ArrayList<String>(5);
while (container != null) {
if (container.getName() != null) {
list.add(":" + container.getName());
}
container = container.getParent();
}
for (int i = (list.size() - 1); i > -1; i--) {
String nextString = (String) list.get(i);
sb.append(nextString);
}
applicationId = sb.toString();
return applicationId;
}
/**
* @return The cluster id as defined in domain.xml
*/
protected String getClusterId() {
if (clusterId == null) {
clusterId = getClusterIdFromConfig();
}
return clusterId;
}
/**
* @return The cluster id as defined in domain.xml
*/
private String getClusterIdFromConfig() {
ServerConfigLookup lookup = new ServerConfigLookup();
return lookup.getClusterIdFromConfig();
}
/**
* @return true if this SipSessionManager is being destroyed as part
* of its associated context being undeployed, and false otherwise
*/
public boolean isBeingReleased() {
return isBeingReleased;
}
/**
* Initializes this SipSessionManager.
*/
private void init() {
if (initialized) {
return;
}
attachEventListener();
initialized = true;
}
private void initSipApplicationSession(SipApplicationSessionImpl sas, SipApplicationListeners sipApplicationListeners) {
// If the SAS is already initialized don't enter synchronized block (issue 1953)
if (sas.isInitialized()) {
return;
}
synchronized (sas.getSasObjectLock()) {
if (sas.isInitialized()) {
return;
}
sas.setName(getContext().getAppName());
SipApplicationListeners appListeners =
sas.getSipApplicationListeners();
if (appListeners != null) {
ArrayList<SipApplicationSessionListener> listeners =
appListeners.getSipApplicationSessionListeners();
SipApplicationSessionEvent event =
new SipApplicationSessionEvent(sas);
for (Iterator<SipApplicationSessionListener> iter =
listeners.iterator();
iter.hasNext();) {
SipApplicationSessionListener listener = iter.next();
listener.sessionCreated(event);
}
}
sas.initAppSessionTimer(sessionTimeout);
sas.setIsInitialized(true);
}
}
public void logActiveObjects() {
if (logger.isLoggable(Level.FINEST)) {
StringBuffer sb = new StringBuffer();
sb.append("\nActive Objects in Application: ").append(getApplicationId()).append('\n');
for (Map.Entry<String, SipApplicationSessionImpl> sasEntry : applicationSessions.entrySet()) {
SipApplicationSessionImpl sas = sasEntry.getValue();
sb.append(" SAS: ").append(sasEntry.getKey()).append(" ref: ").append(sas.getObjectRefString()).append(" isValid:").append(sas.isValid()).append('\n');
}
for (Map.Entry<String, SipSessionDialogImpl> ssEntry : sipSessions.entrySet()) {
SipSessionDialogImpl ss = ssEntry.getValue();
String creationDate = " at: "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(ss.getCreationTime()));
sb.append(" SS: ").append(ssEntry.getKey()).append(creationDate).append(" isValid:").append(ss.isValid()).append('\n');
}
for (Map.Entry<String, ServletTimerImpl> stEntry : servletTimers.entrySet()) {
sb.append(" ST: ").append(stEntry.getKey()).append('\n');
}
logger.log(Level.FINEST, sb.toString());
}
}
/**
* Handling of obsolote sessions.
* During failover the sessions are served by another server instance accordingly to the datacentric balancing.
* When the failing node is returned back to service,
* the sessions needs to be removed and established on the current recoved instance.
*
* The procedure removes all the sip application sessions that are not longer handled by this server instance.
*
* @author Adrian Szwej
*
*/
class ObsoleteSessionHandler implements EventListener {
/** How long time to wait until starting the session iteration.
*/
public static final long BACK_OFF_TIMER = 32000L;
/** Timer handling the execution after backoff time has elapsed. */
private GeneralTimer backOffTimer = null;
/**
* Constructor.
* @param clusterName The name of the cluster to operate on.
* @throws CLBRuntimeException
*/
public ObsoleteSessionHandler(){
}
/**
* Triggered when an instance has joined the cluster and is in service.
* @param clusterName the source cluster
* @param instanceName the name of the instance that has become ready for service
*
*/
public void onRecovery(String clusterName, String instanceName, boolean isClusterStartup) {
startBackOffTimer();
}
public void onEnable(String clusterName, String instanceName) {
startBackOffTimer();
}
public void onAdd(String clusterName, String instanceName,
boolean lbEnabled) {
startBackOffTimer();
}
public void onFailure(String clusterName, String instanceName, boolean isClusterShutdown) {
}
public void onDisable(String clusterName, String instanceName) {
}
public void onDelete(String clusterName, String instanceName) {
}
/**
* Starts the backoff timer. If there is any timer already active, cancel that timer and issue a new one.
*/
private void startBackOffTimer() {
synchronized (getLock()) {
if (backOffTimer != null) {
backOffTimer.cancel();
}
backOffTimer = TimerServiceImpl.getInstance().createTimer(new BackOffTimerListener(), BACK_OFF_TIMER, null);
}
}
/**
* Gets the instance for this obsolote sessionhandler.
* @return reference of this handler.
*/
private Object getLock() {
return this;
}
/**
* Removes all the sessions that are not longer belonging to this instance.
*/
private Iterator<SipApplicationSession> removeObsoloteSessions() {
//separate Set to exclude the invalidation time for the sessions
Set<SipApplicationSession> invalidationSet = new HashSet<SipApplicationSession>();
synchronized (applicationSessions) {
Iterator<SipApplicationSessionImpl> it = applicationSessions.values().iterator();
while (it.hasNext()) {
SipApplicationSessionImpl sas = it.next();
if (!DataCentricUtilHolder.getInstance().isLocal(sas.getBeKey())) {
invalidationSet.add(sas);
}
}
}
return invalidationSet.iterator();
}
/**
* Perform the actual invalidation outside the synchronization block.
*/
private void performInvalidation(Iterator<SipApplicationSession> it) {
while (it.hasNext()) {
try {
it.next().invalidate();
} catch (IllegalStateException ise) {
// Ignore
}
}
}
/**
* This backoff timer will consider the switchover glitch where sas could be created and added just before the iteration.
* The backoff timer makes sure no more obsolote sas references are in the session manager after the sas iteration.
*
* A second purpose of backoff timer is to postpone the session iteration in situations
* where many instances are joining the cluster at the same time, e.g during HW upscale or cluster restart.
*/
private class BackOffTimerListener implements GeneralTimerListener {
public void timeout(GeneralTimer timer) {
Iterator<SipApplicationSession> it = null;
synchronized (getLock()) {
if (backOffTimer == timer) {
it = removeObsoloteSessions();
//unreference for garbage collection
backOffTimer = null;
} else {
//dont do anything, there is another timer started later on that will handle the invalidation process
}
}
if (it != null) {
performInvalidation(it);
}
}
}
}
}