/*
* 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.Constants;
import com.ericsson.ssa.config.SipFactoryFacade;
import com.ericsson.ssa.config.ConvergedContext;
import com.ericsson.ssa.container.sim.ApplicationDispatcher;
import com.ericsson.ssa.container.sim.ServletDispatcher;
import org.jvnet.glassfish.comms.deployment.backend.SipApplication;
import org.jvnet.glassfish.comms.deployment.backend.SipApplicationListeners;
import com.ericsson.ssa.sip.dialog.Cleanable;
import com.ericsson.ssa.sip.dialog.DialogCleaner;
import com.ericsson.ssa.sip.timer.GeneralTimer;
import com.ericsson.ssa.sip.timer.GeneralTimerImpl;
import com.ericsson.ssa.sip.timer.GeneralTimerListener;
import com.ericsson.ssa.sip.timer.ServletTimerImpl;
import com.ericsson.ssa.sip.timer.TimerServiceImpl;
import com.sun.enterprise.ee.web.sessmgmt.EEPersistenceTypeResolver;
import com.sun.enterprise.util.LocalStringManagerImpl;
import org.apache.catalina.session.SessionLock;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.util.logging.Logger;
import javax.servlet.http.HttpSession;
import javax.servlet.sip.ServletTimer;
import javax.servlet.sip.SipApplicationSession;
import javax.servlet.sip.SipApplicationSessionActivationListener;
import javax.servlet.sip.SipApplicationSessionEvent;
import javax.servlet.sip.SipApplicationSessionListener;
import javax.servlet.sip.SipSession;
import javax.servlet.sip.TimerListener;
import javax.servlet.sip.URI;
/**
* Implementation of the SSA interface <code>SipApplicationSession</code>.<br>
* These methods can be called from the servlet code.
* <ol>
* <li>{@link #encodeURI(URI)}} Not Implmented !</li>
* <li>{@link #getAttribute(String)}</li>
* <li>{@link #getAttributeNames()}</li>
* <li>{@link #getCreationTime()}</li>
* <li>{@link #getId()}</li>
* <li>{@link #getLastAccessedTime()} Not Implemented !</li>
* <li>{@link #getSessions()}</li>
* <li>{@link #getSessions(String)}</li>
* <li>{@link #getTimers()}</li>
* <li>{@link #invalidate()}</li>
* <li>{@link #removeAttribute(String)}</li>
* <li>{@link #setAttribute(String, Object)}</li>
* <li>{@link #setExpires(int)}</li>
* </ol>
*
* @author ehsroha
* @Etag reviewed by ehsroha, removeSession method.
* @reviewed ejoelbi 2007-jan-17
*/
public abstract class SipApplicationSessionBase implements SipApplicationSession,
GeneralTimerListener, Cleanable, Externalizable {
public static final String APP_LOCK_ATTR_NAME = "com.ericsson.ssa.sip.SipApplicationSession.applicationLock";
private static final long serialVersionUID = 1972145565288132288L;
/** The serialized format versioning. 1 = first version. */
private static final short serializedFormVersion = 1;
private static final int DEFAULT_SAS_TIMEOUT = 3; // 3 minutes -- in accordance with the spec
private static Logger logger = LogUtil.SIP_LOGGER.getLogger();
private static ApplicationDispatcher applicationDispatcher = ApplicationDispatcher.getInstance();
private static TimerServiceImpl timerService = TimerServiceImpl.getInstance();
protected boolean isValid = true;
/**
* Flag indicating whether application will be notified when this session
* transitions to ready-to-invalidate state.
*/
private volatile boolean invalidateWhenReady = true;
/**
* Flag indicating whether the session is in ready-to-invalidate
* state or not.
*/
private volatile boolean readyToInvalidate = false;
protected int deltaSessionTimeout = 0;
private String currentServlet = null;
protected transient GeneralTimerImpl sasTimer;
private final SessionLock sessionLock = new SessionLock();
// All SSA methods are synchronized using this lock.
private final Object sasObjectLock = new Object();
// The lock used by applications to lock the SAS and all its children.
private final Object sasApplicationLock = new Object();
private boolean invalidateInProgress;
private volatile boolean explicitlyInvalidated;
private boolean applicationLockUsed;
protected static final LocalStringManagerImpl localStrings =
new LocalStringManagerImpl(SipApplicationSessionBase.class);
private volatile boolean isInitialized = false;
/**
* Constructor for serialization.
*/
public SipApplicationSessionBase() {
}
public boolean isInitialized() {
return isInitialized;
}
public void setIsInitialized(boolean initialized) {
isInitialized = initialized;
}
// ---- SSA Methods ----
/**
* @see javax.servlet.sip.SipApplicationSession#encodeURI(URI)
*/
public void encodeURI(URI uri) {
synchronized (sasObjectLock) {
validateSessionState();
}
if (getBeKey() != null) {
UriUtil.setAndEncodeParameter(uri, URIImpl.BEKEY_PARAM, getBeKey());
}
UriUtil.setAndEncodeParameter(uri, URIImpl.SASID_PARAM, getId());
}
/**
* @see javax.servlet.sip.SipApplicationSession#getAttribute(java.lang.String)
*/
public Object getAttribute(String key) {
synchronized (sasObjectLock) {
validateSessionState();
if (APP_LOCK_ATTR_NAME.equals(key)) {
setApplicationLockUsed();
return sasApplicationLock;
}
return getPFieldApplicationAttribute(key);
}
}
/**
* @see javax.servlet.sip.SipApplicationSession#getAttributeNames()
*/
public Iterator<String> getAttributeNames() {
synchronized (sasObjectLock) {
validateSessionState();
return getPFieldApplicationAttributeNames();
}
}
/**
* @see javax.servlet.sip.SipApplicationSession#getCreationTime()
*/
public long getCreationTime() {
synchronized (sasObjectLock) {
validateSessionState();
return getPFieldCreationDate();
}
}
/**
* @see javax.servlet.sip.SipApplicationSession#getId()
*/
public abstract String getId();
/**
* Not implemented.
*
* @see javax.servlet.sip.SipApplicationSession#getLastAccessedTime()
*/
@SuppressWarnings("unchecked")
public long getLastAccessedTime() {
synchronized (sasObjectLock) {
long lastAccessedTime = -1;
Iterator<SipSession> sipSessIter = (Iterator<SipSession>)
getSessions(SipFactoryImpl.SIP_URI_PROTOCOL, true);
while (sipSessIter.hasNext()) {
long sessAccessTime = sipSessIter.next().getLastAccessedTime();
if (sessAccessTime > lastAccessedTime) {
lastAccessedTime = sessAccessTime;
}
}
Iterator<HttpSession> httpSessIter = (Iterator<HttpSession>)
getSessions(SipFactoryImpl.HTTP_URI_PROTOCOL, true);
while (httpSessIter.hasNext()) {
long sessAccessTime = httpSessIter.next().getLastAccessedTime();
if (sessAccessTime > lastAccessedTime) {
lastAccessedTime = sessAccessTime;
}
}
return lastAccessedTime;
}
}
/**
* @see javax.servlet.sip.SipApplicationSession#getSessions()
*/
public Iterator<?> getSessions() {
synchronized (sasObjectLock) {
validateSessionState();
ArrayList<Object> list = new ArrayList<Object>(5);
for (SipSessionBase sess : getPFieldSipSessions()) {
if (sess != null) {
list.add(sess);
}
}
for (HttpSession sess : getPFieldHttpSessions()) {
if (sess != null) {
list.add(sess);
}
}
return list.iterator();
}
}
/**
* @see javax.servlet.sip.SipApplicationSession#getSessions(java.lang.String)
*/
public Iterator<?> getSessions(String protocol) {
return getSessions(protocol, false);
}
/**
* Gets all sessions of the given protocol (HTTP or SIP).
*
* @param protocol The requested protocol
* @param activeOnly true if only the currently active sessions should
* be returned, false otherwise
*/
public Iterator<?> getSessions(String protocol, boolean activeOnly) {
if (protocol == null)
throw new NullPointerException("Argument protocol is null");
synchronized (sasObjectLock) {
validateSessionState();
if (SipFactoryImpl.SIP_URI_PROTOCOL.equalsIgnoreCase(protocol)) {
ArrayList<SipSession> ss = new ArrayList<SipSession>(5);
if (activeOnly) {
for (SipSessionBase sess : getPFieldSipSessionsActiveOnly()) {
if (sess != null) {
ss.add(sess);
}
}
} else {
for (SipSessionBase sess : getPFieldSipSessions()) {
if (sess != null) {
ss.add(sess);
}
}
}
return ss.iterator();
} else if (SipFactoryImpl.HTTP_URI_PROTOCOL.equalsIgnoreCase(
protocol)) {
ArrayList<HttpSession> sw = new ArrayList<HttpSession>(5);
if (activeOnly) {
for (HttpSession sess : getPFieldHttpSessionsActiveOnly()) {
if (sess != null) {
sw.add(sess);
}
}
} else {
for (HttpSession sess : getPFieldHttpSessions()) {
if (sess != null) {
sw.add(sess);
}
}
}
return sw.iterator();
} else {
throw new IllegalArgumentException("Protocol is not understood by container");
// return Collections.EMPTY_LIST.iterator();
}
}
}
/**
* @see javax.servlet.sip.SipApplicationSession#getTimers()
*/
public Collection<ServletTimer> getTimers() {
synchronized (sasObjectLock) {
validateSessionState();
return getPFieldApplicationTimers();
}
}
/**
* @see javax.servlet.sip.SipApplicationSession#invalidate()
*/
public void invalidate() {
synchronized (sasObjectLock) {
if (this.invalidateInProgress) {
this.explicitlyInvalidated = true;
return;
}
}
// if called from SipServlet code
invalidate(false);
}
public String getApplicationName() {
return getPFieldApplicationName();
}
public ServletTimer getTimer(String id) {
validateSessionState();
synchronized (sasObjectLock) {
return getPFieldApplicationTimer(id);
}
}
/**
* @see javax.servlet.sip.SipApplicationSession#removeAttribute(java.lang.String)
*/
public void removeAttribute(String key) {
synchronized (sasObjectLock) {
validateSessionState();
removePFieldApplicationAttribute(key);
}
}
/**
* @see javax.servlet.sip.SipApplicationSession#setAttribute(java.lang.String,
* java.lang.Object)
*/
public void setAttribute(String key, Object value) {
if (key == null)
throw new NullPointerException("Argument key is null");
if (value == null)
throw new NullPointerException("Argument valu is null");
synchronized (sasObjectLock) {
if (APP_LOCK_ATTR_NAME.equals(key)) {
return; // Ignore, it is not allowed to change the lock.
}
validateSessionState();
setPFieldApplicationAttribute(key, value);
}
}
/**
* @see javax.servlet.sip.SipApplicationSession#setExpires(int)
*/
public int setExpires(int deltaMinutes) {
synchronized (sasObjectLock) {
validateSessionState();
}
if (deltaMinutes < 1) {
// Container does not allow infinite expiration time
// In JSR289 returning 0 means rejection
return 0; // reject extention
}
/**
* During SAS timeout handling, if the extension is requested on the
* explicitly invalidated SAS then such a request should be rejected.
*/
if(explicitlyInvalidated) {
return 0;
}
synchronized (sasObjectLock) {
if (sasTimer != null) {
sasTimer.cancel();
}
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE,
"Started timer, session-timeout:" + deltaMinutes);
}
deltaSessionTimeout = deltaMinutes;
long expirationTime = System.currentTimeMillis() +
(deltaSessionTimeout * 60 * 1000L);
sasTimer = (GeneralTimerImpl) timerService.createTimer((GeneralTimerListener) this,
deltaSessionTimeout * 60 * 1000L, null);
setPFieldExpirationTime(expirationTime);
return deltaSessionTimeout;
}
}
// ---- Methods used internally by the container ----
/**
* Called when the object is created <br>
* see {@link SipFactoryFacade#createApplicationSession()}. <br>
* This method is <b>not</b> thread safe
*/
public void initAppSessionTimer(int sessionTimeout) {
// start applicationSessionTimer
int configuredSessionTimeout = -1;
if (sessionTimeout == SipApplication.UNKNOWN_TIMEOUT_VALUE) {
configuredSessionTimeout = DEFAULT_SAS_TIMEOUT;
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE,
"session-timeout not configured, using default:" +
configuredSessionTimeout);
}
} else {
configuredSessionTimeout = sessionTimeout;
}
if (configuredSessionTimeout >= 1) {
setPFieldExpirationTime(System.currentTimeMillis() +
(configuredSessionTimeout * 60 * 1000L));
// TODO currently assume this "internal" timer should not be
// included
// in m_applicatinonTimers
sasTimer = (GeneralTimerImpl) timerService.createTimer((GeneralTimerListener) this,
(configuredSessionTimeout * 60 * 1000L), null);
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE,
"Started timer defined in sip.xml : session-timeout:" +
configuredSessionTimeout);
}
} else {
setPFieldExpirationTime(-1L);
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE,
"No default timer, sip.xml : session-timeout:" +
configuredSessionTimeout);
}
}
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "app:" + getName() + " " + this);
}
}
protected void restartAppSessionTimer() {
long expTime = getPFieldExpirationTime();
if (expTime > 0) {
if (sasTimer != null) {
sasTimer.cancel();
}
if(expTime < System.currentTimeMillis()) {
//call timeout which either extends or invalidates
timeout(null);
} else {
sasTimer = (GeneralTimerImpl) timerService.createTimer((GeneralTimerListener) this,
expTime - System.currentTimeMillis(), null);
}
} else {
// nothing to do
//FIXME - doing nothing means that the infinite
//case truly never expires - do we want this?
}
}
/**
* Called when the timer associated with the <code>session-timeout</code>
* in sip.xml expires. see {@link #initAppSessionTimer()}. {@inheritDoc}
*/
public void timeout(GeneralTimer timer) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "app session timeout:" + getName());
}
// What happens if there are several listeners and they "disagree"
// about
// the extension of the session
// (1),(3),(2) --> last setExpire() wins The first two gets canceled
synchronized (sasObjectLock) {
if (!isValid){
// no need to do anything if we are already invalidated
return;
}
deltaSessionTimeout = 0; // reset delta, now let the listeners
// extend
// when the invalidateInProgress=true a call to invalidate() will
// have
// no effect.
this.invalidateInProgress = true;
}
// "lifetime"
boolean shouldExtend = false;
Iterator<SipApplicationSessionListener> iter = getSipApplicationListeners()
.getSipApplicationSessionListeners()
.iterator();
SipApplicationSessionEvent sasEvent = new SipApplicationSessionEvent(this);
while (iter.hasNext() && !explicitlyInvalidated) {
SipApplicationSessionListener listener = iter.next();
try {
listener.sessionExpired(sasEvent);
} catch (Throwable t) {
logger.log(Level.WARNING, "sas_listener_session_expired_invocation_error", this);
logger.log(Level.WARNING, t.getMessage(), t);
}
// Synced to capture accidental setExpires invocations during
// the timeout handling.
synchronized(sasObjectLock) {
if (deltaSessionTimeout >= 1) {
shouldExtend = true;
break;
}
}
}
synchronized (sasObjectLock) {
this.invalidateInProgress = false;
}
if ((!shouldExtend || explicitlyInvalidated) && isValid()) {
try {
invalidate(true);
} catch (IllegalStateException ignore) {
// short term solution for race (Issue 1371)
// sas might have been invalidated after isValid() returned....
// we can ignore this exception since invalidation already
// happened
}
}
}
/**
* Notifies any interested session attributes that this
* SipApplicationSession has been activated.
*/
protected void notifySessionDidActivate() {
SipApplicationSessionEvent sasEvent = new SipApplicationSessionEvent(this);
// Notify all SipApplicationSessionActivationListeners in xml
ArrayList<SipApplicationSessionActivationListener> listeners = getSipApplicationListeners().getSipApplicationSessionActivationListener();
for(SipApplicationSessionActivationListener l : listeners) {
l.sessionDidActivate(sasEvent);
}
// Notify all attributes implementing SipApplicationSessionActivationListener
Iterator<String> attrNames = getAttributeNames();
if (attrNames == null) {
return;
}
while (attrNames.hasNext()) {
String attrName = attrNames.next();
Object attrValue = getAttribute(attrName);
if (attrValue instanceof SipApplicationSessionActivationListener) {
((SipApplicationSessionActivationListener) attrValue).sessionDidActivate(sasEvent);
}
}
}
/**
* Notifies any interested session attributes that this
* SipApplicationSession is about to be passivated.
*/
protected void notifySessionWillPassivate() {
SipApplicationSessionEvent sasEvent = new SipApplicationSessionEvent(this);
// Notify all SipApplicationSessionActivationListeners in xml
ArrayList<SipApplicationSessionActivationListener> listeners = getSipApplicationListeners().getSipApplicationSessionActivationListener();
for(SipApplicationSessionActivationListener l : listeners) {
l.sessionWillPassivate(sasEvent);
}
// Notify all attributes implementing SipApplicationSessionActivationListener
Iterator<String> attrNames = getAttributeNames();
if (attrNames == null) {
return;
}
while (attrNames.hasNext()) {
String attrName = attrNames.next();
Object attrValue = getAttribute(attrName);
if (attrValue instanceof SipApplicationSessionActivationListener) {
((SipApplicationSessionActivationListener) attrValue).sessionWillPassivate(sasEvent);
}
}
}
/**
* See if the session is valid or not.
*
* @return true if the session has not been invalidated yet. no call to
* {@link #invalidate()} has been made or the session-timeout in
* sip.xml has not yet expired.
*/
public boolean isValid() {
synchronized (sasObjectLock) {
return isValid;
}
}
// -------- SIP Session Attributes -------------
/**
* @return Returns the name.
*/
public String getName() {
synchronized (sasObjectLock) {
return getPFieldApplicationName();
}
}
public void setName(String applicationName) {
synchronized (sasObjectLock) {
setPFieldApplicationName(applicationName);
}
}
public void addSession(SipSessionBase session) {
synchronized (sasObjectLock) {
validateSessionState();
addSipProtocolSession(session);
}
}
public void removeSession(SipSessionBase session) {
synchronized (sasObjectLock) {
// a SAS does not have to be valid in order to allow removal of a SS
// it can happen that the SAS is already invalid in some raceconditions
// validateSessionState();
removePFieldSipProtocolSession(session);
}
}
/**
* @param session
*/
public void addSession(HttpSession session) {
synchronized (sasObjectLock) {
addPFieldHttpProtocolSession(session);
}
}
public void removeSession(HttpSession session) {
removeSession(session, true);
}
public void removeSession(HttpSession session, boolean removeFromManager) {
synchronized (sasObjectLock) {
removePFieldHttpProtocolSession(session, removeFromManager);
}
}
/**
* @param st
*/
public void addServletTimer(ServletTimerImpl st) {
synchronized (sasObjectLock) {
addPFieldApplicationTimer(st);
}
}
/**
* @param st
*/
public void cancelServletTimer(ServletTimer st) {
synchronized (sasObjectLock) {
removePFieldApplicationTimer((ServletTimerImpl) st);
}
}
/**
* @return Returns the m_SipApplicationListeners.
*/
public abstract SipApplicationListeners getSipApplicationListeners();
/**
* @return
*/
public TimerListener getTimerListener() {
synchronized (sasObjectLock) {
validateSessionState();
return getSipApplicationListeners().getTimerListener();
}
}
public void setCurrentServlet(String servlet) {
synchronized (sasObjectLock) {
this.currentServlet = servlet;
}
}
public String getCurrentServlet() {
synchronized (sasObjectLock) {
return currentServlet;
}
}
public ServletDispatcher getServletDispatcher() {
synchronized (sasObjectLock) {
return applicationDispatcher.getServletDispatcher(getName());
}
}
/**
* Can be called from servlet Thread and the
* <code>m_applicationSessionTimer</code> thread. Note! All calls to this
* methods should synchronize on SIP_APP_LOCK
*
* @param hasTimedOut
*/
protected void invalidate(boolean hasTimedOut) {
synchronized (sasObjectLock) {
if(!isValid()) {
throw new IllegalStateException(localStrings.getLocalString(
"sas_already_invalid",
"Should not call invalidate() when the application session " +
"is not valid."));
}
// first cancel the application timer cancel if any
if (sasTimer != null) {
// do not call with mayInterruptIfRunning=true, because we may
// interrupt the current thread.
if (hasTimedOut) {
sasTimer.cancel();
} else {
// Try to force the timer to stop if invalid() is called from
// servlet code.
sasTimer.cancel(true);
}
}
cancelAllServletTimers();
notifyAttributesUnbound();
// Invalidate session, important that the m_IsValid before invalidating
// the SipSessions.
isValid = false;
}
// Call ApplicationSession listener session destroyed
ArrayList<SipApplicationSessionListener> listeners = getSipApplicationListeners()
.getSipApplicationSessionListeners();
SipApplicationSessionEvent sasEvent = new SipApplicationSessionEvent(this);
for (Iterator<SipApplicationSessionListener> lIter = listeners.iterator();
lIter.hasNext();) {
SipApplicationSessionListener list = lIter.next();
try {
list.sessionDestroyed(sasEvent);
} catch (Throwable t) {
logger.log(Level.WARNING, "sas_listener_session_destroyed_invocation_error", this);
logger.log(Level.WARNING, t.getMessage(), t);
}
}
Iterable<SipSessionBase> sipSessions = null;
Iterable<HttpSession> httpSessions = null;
synchronized (sasObjectLock) {
sipSessions = getPFieldSipSessions();
httpSessions = getPFieldHttpSessions();
}
boolean atLeastOneSSExisted = false;
for (SipSessionBase sess : sipSessions) {
// Session may have been explicitly invalidated....
atLeastOneSSExisted = true;
if ((sess != null) && sess.isValid()) {
try {
sess.invalidate(hasTimedOut);
} catch (Throwable t) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE,
"sip_session_unable_to_invalidate", sess);
logger.log(Level.FINE, t.getMessage(), t);
}
}
}
}
if (!atLeastOneSSExisted) {
DialogCleaner.getInstance().registerForSupervision(
this, DialogCleaner.NO_DIALOG_TIMEOUT);
}
// Clean the HttpSession
for (HttpSession sess : httpSessions) {
// Session may have been explicitly invalidated....
if (sess != null) {
try {
// Session may have been explicitly invalidated....
sess.invalidate();
} catch (Throwable t) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE,
"http_session_unable_to_invalidate", sess);
logger.log(Level.FINE, t.getMessage(), t);
}
}
}
}
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Done invalidating " + this);
}
}
public boolean isReadyToInvalidate() {
return isReadyToInvalidate(true);
}
public boolean getInvalidateWhenReady() {
validateSessionState();
return invalidateWhenReady;
}
public void setInvalidateWhenReady(boolean iwr) {
validateSessionState();
boolean wasInvalidateWhenReady;
synchronized (sasObjectLock) {
if(iwr == invalidateWhenReady) {
return;
}
wasInvalidateWhenReady = invalidateWhenReady;
invalidateWhenReady = iwr;
}
/*
* If IWR was already true, then we should do nothing.
* Otherwise we will cause an infinite loop if setInvalidateWhenReady(true)
* is called from listener's sessionReadyToInvalidate callback.
*/
if (!wasInvalidateWhenReady) {
if (checkSessionReadyToInvalidate()) { // we should notify the listeners in this case.
sessionReadyToInvalidate(); // don't hold ssLock while notifying.
}
}
if (logger.isLoggable(Level.FINE)) {
if (invalidateWhenReady) {
logger.log(Level.FINE,
"Invalidate-When-Ready machanism is enabled for " + this);
} else {
logger.log(Level.FINE,
"Invalidate-When-Ready machanism is disabled for " + this);
}
}
}
protected void initInvalidateWhenReady() {
SipSessionManager manager = getSipSessionManager();
if (manager != null) {
ConvergedContext ctx = manager.getContext();
if (ctx != null) {
SipApplication descriptor = ctx.getSipApplication();
if (descriptor != null &&
SipApplication.ApplicationVersion.VERSION_1_0.equals(
descriptor.getApplicationVersion())) {
invalidateWhenReady = false;
return;
}
}
}
invalidateWhenReady = true;
}
private boolean isReadyToInvalidate(boolean validateSession) {
if(validateSession) {
validateSessionState();
}
if (!readyToInvalidate) {
synchronized(sasObjectLock) {
readyToInvalidate = isEmpty();
}
}
return readyToInvalidate;
}
protected void setReadyToInvalidate(boolean val) {
readyToInvalidate = val;
}
/**
* This method does not check for the validity of the session.
*/
protected boolean checkSessionReadyToInvalidate() {
if(invalidateWhenReady && isReadyToInvalidate(false)) {
return true;
}
return false;
}
/**
* This method is called when this SAS becomes ready-to-invalidate.
* It can happen in the following cases:
*
* (a) The last protocol session belonging to this SAS is invalidated
* (b) The last ServletTimer associated with this SAS expires.
* (c) The application calls setInvalidateWhenReady(true) when IWR was false
* earlier but the SAS is already in ready-to-invalidate state.
*
*/
protected void sessionReadyToInvalidate() {
if (isValid()) {
notifySessionReadyToInvalidate(); // notify the application listeners.
if (invalidateWhenReady && isValid()) {
// application is OK to do invalidaion & has not invalidated explicitly.
if(logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Invalidating " + this + " which is in" +
" ready-to-invalidate state");
}
invalidate();
}
}
}
protected void notifySessionReadyToInvalidate() {
SipApplicationListeners listeners = getSipApplicationListeners();
if (listeners == null) {
return;
}
List<SipApplicationSessionListener> sasListeners =
listeners.getSipApplicationSessionListeners();
Iterator<SipApplicationSessionListener> iter = sasListeners.iterator();
SipApplicationSessionEvent event = new SipApplicationSessionEvent(this);
/**
* Stop notifying the listeners if any of the following occur:
*
* (a) one of the listener invoked setInvalidateWhenReady(false)
* (b) this session is no longer valid, may be because the listener
* has invoked invalidate() explicitly.
*/
while (iter.hasNext() && invalidateWhenReady && isValid()) {
SipApplicationSessionListener listener = iter.next();
try {
listener.sessionReadyToInvalidate(event);
} catch (Throwable t) {
logger.log(Level.WARNING, t.getMessage(), t);
}
}
}
protected abstract void notifyAttributesUnbound();
/**
* Note! All calls to this MUST synchronize on SIP_APP_LOCK.
*/
private void cancelAllServletTimers() {
for (ServletTimer timer : getTimers()) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "cancel timer:" + timer.toString());
}
// terminate timer with force i.e. thread may be interrupted
// to avoid "zombie" timers
// application should cancel its own timers if thread
// interruption
// during appsession invalidate is not acceptable
((ServletTimerImpl) timer).cancel(true);
}
}
private void validateSessionState() {
if(!isValid()) {
throw new IllegalStateException(localStrings.getLocalString(
"sas_invalid", "SipApplicationSession is not valid."));
}
}
public SipSession getSipSession(String id) {
if (id == null) {
throw new NullPointerException();
}
for (Iterator<?> i = getSessions(SipFactoryImpl.SIP_URI_PROTOCOL);
i.hasNext();) {
SipSession s = (SipSession) i.next();
if (s.getId().equals(id)) {
// match...
return s;
}
}
// no match...
return null;
}
public Object getSession(String id, Protocol protocol) {
if(id == null) {
throw new NullPointerException(localStrings.getLocalString(
"get_session_null_id",
"Can not retrieve a session using null id."));
}
if(protocol == null) {
throw new NullPointerException(localStrings.getLocalString(
"get_session_null_protocol",
"Can not retrieve a session using null protocol."));
}
if(!isValid()) {
throw new IllegalStateException(localStrings.getLocalString(
"get_session_sas_invalid",
"Can not call getSession after the SipApplicationSession has been invalidated."));
}
if(protocol.equals(Protocol.SIP)) {
return getPFieldSipSession(id);
}
if(protocol.equals(Protocol.HTTP)) {
return getPFieldHttpSession(id);
}
return null;
}
public URL encodeURL(URL url) {
validateSessionState();
if (url == null) {
return url;
}
String path = url.getPath();
String query = url.getQuery();
String anchor = "";
int pound = path.indexOf('#');
if (pound >= 0) {
anchor = path.substring(pound);
path = path.substring(0, pound);
}
StringBuilder encoded = new StringBuilder(url.getProtocol());
encoded.append("://");
encoded.append(url.getHost());
encoded.append(":");
encoded.append(url.getPort());
encoded.append(path);
encoded.append(Constants.SAS_ID_URI_PARAMETER);
encoded.append(getId());
String bekey = getBeKey();
if (bekey != null) {
encoded.append(Constants.BEKEY_URI_PARAMETER);
encoded.append(bekey);
}
if (anchor != null) {
encoded.append(anchor);
}
if (query != null) {
encoded.append("?");
encoded.append(query);
}
URL encodedUrl = null;
try {
encodedUrl = new URL(encoded.toString());
} catch (MalformedURLException e) {
// XXX FIXME log warning
}
return encodedUrl;
}
public long getExpirationTime() {
validateSessionState();
return getPFieldExpirationTime();
}
/**
* Gets the SipSessionManager of this SipApplicationSession.
*
* @return The SipSessionManager of this SipApplicationSession
*/
public abstract SipSessionManager getSipSessionManager();
/**
* get this session locked for foreground if the session is found to be
* presently background locked; retry logic in a time-decay polling loop
* waits for background lock to clear after 6 attempts (12.6 seconds) it
* unlocks the session and acquires the foreground lock
*/
public boolean lockForegroundWithRetry() {
boolean result = false;
long pollTime = 200L;
int tryNumber = 0;
int maxNumberOfRetries = 7;
boolean keepTrying = true;
// try to lock up to numTries (i.e. 7) times
// poll and wait starting with 200 ms
while (keepTrying) {
boolean lockResult = lockForeground();
if (lockResult) {
keepTrying = false;
result = true;
break;
}
tryNumber++;
if (tryNumber < maxNumberOfRetries) {
pollTime = pollTime * 2L;
try {
Thread.sleep(pollTime);
} catch (InterruptedException e) {
;
}
} else {
// unlock the background so we can take over
// FIXME: need to log warning for this situation
unlockBackground();
}
}
return result;
}
/**
* return whether this session is currently foreground locked
*/
public synchronized boolean isForegroundLocked() {
return sessionLock.isForegroundLocked();
}
/**
* lock the session for foreground returns true if successful; false if
* unsuccessful
*/
public synchronized boolean lockBackground() {
return sessionLock.lockBackground();
}
/**
* lock the session for background returns true if successful; false if
* unsuccessful
*/
public synchronized boolean lockForeground() {
return sessionLock.lockForeground();
}
/**
* unlock the session completely irregardless of whether it was foreground
* or background locked
*/
public synchronized void unlockForegroundCompletely() {
sessionLock.unlockForegroundCompletely();
}
/**
* unlock the session from foreground
*/
public synchronized void unlockForeground() {
sessionLock.unlockForeground();
}
/**
* unlock the session from background
*/
public synchronized void unlockBackground() {
sessionLock.unlockBackground();
}
/**
* return the Session lock
*/
public SessionLock getSessionLock() {
return sessionLock;
}
/**
* Returns true if this SipApplicationSession is replicable, false
* otherwise.
*
* @return true if this SipApplicationSession is replicable, false otherwise
*/
public boolean isReplicable() {
return EEPersistenceTypeResolver.REPLICATED_TYPE.equals(getSipSessionManager()
.getPersistenceType());
}
/**
* Gets the lock that locks this SAS.
*
* @return the lock that locks this SAS
*/
public Object getSasObjectLock() {
return sasObjectLock;
}
/**
* Gets the lock used by applications to lock the SAS and all its children.
*
* @return the lock used by applications to lock the SAS and all its
* children
*/
public Object getSasApplicationLock() {
return sasApplicationLock;
}
/**
* Checks if the application has used the SAS application lock and atomically reset the state of this flag.
* @return true if the SAS application lock has been used
*/
public boolean getAndResetApplicationLockUsed() {
synchronized (sasObjectLock) {
boolean retVal = applicationLockUsed;
applicationLockUsed = false;
return retVal;
}
}
private void setApplicationLockUsed() {
synchronized (sasObjectLock) {
applicationLockUsed = true;
}
}
/**
* Sets the hash key which was used to route the initial request that caused this
* SipApplicationSession to be created.
* @param beKey the hash key
* @deprecated This method has no effect anymore. When calling {@link #getBeKey()}, the bekey
* is always extracted from the ID {@link #getId()}.
*/
@Deprecated
public void setBeKey(String beKey) {
// Has no effect anymore, after fix of Issue#1365. The bekey is always extracted
// from SAS-ID
}
/**
* Gets the hash key which was used to route the initial request that caused this
* SipApplicationSession to be created.
* @return the hash key which was used to route the initial request that caused this
* SipApplicationSession to be created
*/
public String getBeKey() {
return SipApplicationSessionUtil.getSipApplicationKey(getId());
}
// ---- Persisted Fields (PField) ----
/**
* Gets the actual value of the creation date.
*
* @return the actual value of the creation date
*/
protected abstract long getPFieldCreationDate();
/**
* Gets the actual value of the application name.
*
* @return the actual value of the application name.
*/
protected abstract String getPFieldApplicationName();
/**
* Sets the actual value of the application name.
*
* @param applicationName the actual value of the application name.
*/
protected abstract void setPFieldApplicationName(String applicationName);
/**
* Gets the value of the application attribute identified by the given key.
*
* @param key the key
* @return the value of the application attribute identified by the given
* key
*/
protected abstract Object getPFieldApplicationAttribute(String key);
/**
* Sets the value of the application attribute identified by the given key.
*
* @param key the key
* @param value the value
*/
protected abstract void setPFieldApplicationAttribute(String key,
Object value);
/**
* Removes the application attribute identified by the given key.
*
* @param key the key
*/
protected abstract void removePFieldApplicationAttribute(String key);
/**
* Gets the names of all application attributes.
*
* @return the names of all application attributes
*/
protected abstract Iterator<String> getPFieldApplicationAttributeNames();
/**
* Adds a SIP protocol session.
*
* @param session the session
*/
protected abstract void addSipProtocolSession(SipSessionBase session);
/**
* Removes a SIP protocol session.
*
* @param session the session to remove
*/
protected abstract void removePFieldSipProtocolSession(
SipSessionBase session);
/**
* Gets the SIP protocol sessions.
*
* @return the SIP protocol sessions
*/
protected abstract Iterable<SipSessionBase> getPFieldSipSessions();
/**
* Gets all SIP protocol sessions that are currently active.
*
* @return the currently active SIP protocol sessions
*/
protected abstract Iterable<SipSessionBase> getPFieldSipSessionsActiveOnly();
/**
* Gets the SIP protocol session for a given ID.
*
* @param id SipSession ID
* @return null if the SipSession with given ID does not exist or any
* internal error happens while retrieving; otherwise a SIP protocol session.
*/
protected abstract SipSessionBase getPFieldSipSession(String id);
/**
* Removes a HTTP protocol session.
*
* @param session the session to remove
*/
protected abstract void removePFieldHttpProtocolSession(HttpSession session);
/**
* Removes a HTTP protocol session.
*
* @param session the session to remove
* @param removeFromManager true if the session also needs to be removed from
* its manager, false otherwise
*/
protected abstract void removePFieldHttpProtocolSession(
HttpSession session, boolean removeFromManager);
/**
* Adds a HTTP protocol session.
*
* @param session the session
*/
protected abstract void addPFieldHttpProtocolSession(HttpSession session);
/**
* Gets the HTTP protocol sessions.
*
* @return the HTTP protocol sessions
*/
protected abstract Iterable<HttpSession> getPFieldHttpSessions();
/**
* Gets all HTTP protocol sessions that are currently active.
*
* @return the currently active HTTP protocol sessions
*/
protected abstract Iterable<HttpSession> getPFieldHttpSessionsActiveOnly();
/**
* Gets the HTTP protocol session for a given ID.
*
* @param id HttpSession ID
* @return null if the HttpSession with given ID does not exist or any
* internal error happens while retrieving; otherwise a HTTP protocol session.
*/
protected abstract HttpSession getPFieldHttpSession(String id);
/**
* Gets the application timers
*
* @return the application timers
*/
protected abstract Collection<ServletTimer> getPFieldApplicationTimers();
/**
* Gets the application timer with the given id.
*/
protected abstract ServletTimer getPFieldApplicationTimer(String id);
/**
* Adds an application timer.
*
* @param timer the timer
*/
protected abstract void addPFieldApplicationTimer(ServletTimerImpl timer);
/**
* Removes an application timer
*
* @param timer the timer
*/
protected abstract void removePFieldApplicationTimer(ServletTimerImpl timer);
/**
* Gets the SAS lifetime timer timeout value.
*
* @return the SAS lifetime timer expiration time
*/
protected abstract long getPFieldExpirationTime();
/**
* Sets the SAS lifetime timer timeout value.
*
* @param expirationTime the SAS lifetime timer expiration time
*/
protected abstract void setPFieldExpirationTime(long expirationTime);
protected abstract boolean isEmpty();
/**
* @serialData See serialized form version 1 in
* #readExternal(ObjectInput in)
*
* @param out the stream to write the object members
*
* @throws IOException
* @throws ClassNotFoundException
*/
public void writeExternal(ObjectOutput out) throws IOException {
out.writeShort(serializedFormVersion);
out.writeBoolean(invalidateWhenReady);
out.writeBoolean(readyToInvalidate);
}
/**
* @serialData first field is an short and represents the
* serializedFormVersion.<br><br>
* <h3>Data layout for serializedFormVersion = 1 follows</h3>
*
* <li>field is a <b>boolean</b> and represents invalidateWhenReady
* field</li>
* <li>field is a <b>boolean</b> and represents readyToInvalidate
* field</li>
*
* @param in the stream to read the object members
*
* @throws IOException is thrown when unsupported version is detected
* @throws ClassNotFoundException
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
// Only the fully created SAS will only be serialized always,
// So it is safe to make isInitialized=true upon deserialization.
isInitialized = true;
short readSerializedFormVersion = in.readShort();
switch(readSerializedFormVersion) {
case 1:
invalidateWhenReady = in.readBoolean();
readyToInvalidate = in.readBoolean();
break;
default:
throw new IOException("Unable to deserialize into "
+ this.getClass().getName()
+ " with serialVersionUID = " + serialVersionUID
+ " due to unknown serializedFormVersion of "
+ readSerializedFormVersion);
}
}
}