[jboss-cvs] JBossAS SVN: r78731 - branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Sat Sep 20 17:50:49 EDT 2008
Author: bstansberry at jboss.com
Date: 2008-09-20 17:50:48 -0400 (Sat, 20 Sep 2008)
New Revision: 78731
Modified:
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
Log:
[JBAS-5778] Add pluggable policy for deciding whether to emit servlet spec notifications around events affecting clustered sessions
Modified: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java 2008-09-20 21:50:34 UTC (rev 78730)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java 2008-09-20 21:50:48 UTC (rev 78731)
@@ -50,6 +50,10 @@
import org.apache.catalina.util.StringManager;
import org.jboss.logging.Logger;
import org.jboss.metadata.WebMetaData;
+import org.jboss.web.tomcat.service.session.notification.ClusteredSessionManagementStatus;
+import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationCause;
+import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationPolicy;
+import org.jboss.web.tomcat.service.session.notification.LegacyClusteredSessionNotificationPolicy;
/**
* Abstract base class for session clustering based on StandardSession. Different session
@@ -175,6 +179,13 @@
* Has this session only been accessed once?
*/
protected transient boolean firstAccess;
+
+ /**
+ * Policy that drives whether we issue servlet spec notifications.
+ */
+ protected transient ClusteredSessionNotificationPolicy notificationPolicy;
+
+ protected transient ClusteredSessionManagementStatus clusterStatus;
/**
* The string manager for this package.
@@ -200,6 +211,7 @@
invalidationPolicy = manager.getInvalidateSessionPolicy();
this.useJK = useJK;
this.firstAccess = true;
+ this.notificationPolicy = new LegacyClusteredSessionNotificationPolicy();
checkAlwaysReplicateMetadata();
}
@@ -555,11 +567,14 @@
throw new IllegalArgumentException
(sm.getString("clusteredSession.setAttribute.iae"));
+ ClusteredSessionNotificationPolicy policy = getNotificationPolicy();
+
// Construct an event with the new value
HttpSessionBindingEvent event = null;
// Call the valueBound() method if necessary
- if (value instanceof HttpSessionBindingListener)
+ if (value instanceof HttpSessionBindingListener
+ && policy.isHttpSessionBindingListenerInvocationAllowed(this.clusterStatus, ClusteredSessionNotificationCause.MODIFY, name, true))
{
event = new HttpSessionBindingEvent(getSession(), name, value);
try
@@ -577,7 +592,8 @@
// Call the valueUnbound() method if necessary
if ((unbound != null) && (unbound != value) &&
- (unbound instanceof HttpSessionBindingListener))
+ (unbound instanceof HttpSessionBindingListener) &&
+ policy.isHttpSessionBindingListenerInvocationAllowed(this.clusterStatus, ClusteredSessionNotificationCause.MODIFY, name, true))
{
try
{
@@ -591,71 +607,74 @@
}
// Notify interested application event listeners
- Context context = (Context) manager.getContainer();
- Object listeners[] = context.getApplicationEventListeners();
- if (listeners == null)
- return;
- for (int i = 0; i < listeners.length; i++)
+ if (policy.isHttpSessionAttributeListenerInvocationAllowed(this.clusterStatus, ClusteredSessionNotificationCause.MODIFY, name, true))
{
- if (!(listeners[i] instanceof HttpSessionAttributeListener))
- continue;
- HttpSessionAttributeListener listener =
- (HttpSessionAttributeListener) listeners[i];
- try
+ Context context = (Context) manager.getContainer();
+ Object lifecycleListeners[] = context.getApplicationEventListeners();
+ if (lifecycleListeners == null)
+ return;
+ for (int i = 0; i < lifecycleListeners.length; i++)
{
- if (unbound != null)
- {
- fireContainerEvent(context,
- "beforeSessionAttributeReplaced",
- listener);
- if (event == null)
- {
- event = new HttpSessionBindingEvent
- (getSession(), name, unbound);
- }
- listener.attributeReplaced(event);
- fireContainerEvent(context,
- "afterSessionAttributeReplaced",
- listener);
- }
- else
- {
- fireContainerEvent(context,
- "beforeSessionAttributeAdded",
- listener);
- if (event == null)
- {
- event = new HttpSessionBindingEvent
- (getSession(), name, value);
- }
- listener.attributeAdded(event);
- fireContainerEvent(context,
- "afterSessionAttributeAdded",
- listener);
- }
- }
- catch (Throwable t)
- {
+ if (!(lifecycleListeners[i] instanceof HttpSessionAttributeListener))
+ continue;
+ HttpSessionAttributeListener listener =
+ (HttpSessionAttributeListener) lifecycleListeners[i];
try
{
if (unbound != null)
{
fireContainerEvent(context,
+ "beforeSessionAttributeReplaced",
+ listener);
+ if (event == null)
+ {
+ event = new HttpSessionBindingEvent
+ (getSession(), name, unbound);
+ }
+ listener.attributeReplaced(event);
+ fireContainerEvent(context,
"afterSessionAttributeReplaced",
listener);
}
else
{
fireContainerEvent(context,
+ "beforeSessionAttributeAdded",
+ listener);
+ if (event == null)
+ {
+ event = new HttpSessionBindingEvent
+ (getSession(), name, value);
+ }
+ listener.attributeAdded(event);
+ fireContainerEvent(context,
"afterSessionAttributeAdded",
listener);
}
}
- catch (Exception e)
+ catch (Throwable t)
{
- ;
+ try
+ {
+ if (unbound != null)
+ {
+ fireContainerEvent(context,
+ "afterSessionAttributeReplaced",
+ listener);
+ }
+ else
+ {
+ fireContainerEvent(context,
+ "afterSessionAttributeAdded",
+ listener);
+ }
+ }
+ catch (Exception e)
+ {
+ ;
+ }
+ manager.getContainer().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
}
- manager.getContainer().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
}
}
}
@@ -692,7 +711,7 @@
boolean notify = true;
boolean localCall = true;
boolean localOnly = false;
- expire(notify, localCall, localOnly);
+ expire(notify, localCall, localOnly, ClusteredSessionNotificationCause.INVALIDATE);
}
@@ -757,7 +776,7 @@
{
boolean localCall = true;
boolean localOnly = true;
- expire(notify, localCall, localOnly);
+ expire(notify, localCall, localOnly, ClusteredSessionNotificationCause.TIMEOUT);
}
/**
@@ -782,8 +801,9 @@
* cluster nodes should be made aware of the expiration.
* Only meaningful if <code>localCall</code> is
* <code>true</code>.
+ * @param cause the cause of the expiration
*/
- public void expire(boolean notify, boolean localCall, boolean localOnly)
+ public void expire(boolean notify, boolean localCall, boolean localOnly, ClusteredSessionNotificationCause cause)
{
if (log.isDebugEnabled())
{
@@ -810,18 +830,20 @@
// Notify interested application event listeners
// FIXME - Assumes we call listeners in reverse order
Context context = (Context) manager.getContainer();
- Object listeners[] = context.getApplicationLifecycleListeners();
- if (notify && (listeners != null))
+ Object lifecycleListeners[] = context.getApplicationLifecycleListeners();
+ if (notify
+ && (lifecycleListeners != null)
+ && getNotificationPolicy().isHttpSessionListenerInvocationAllowed(this.clusterStatus, cause, localCall))
{
HttpSessionEvent event =
new HttpSessionEvent(getSession());
- for (int i = 0; i < listeners.length; i++)
+ for (int i = 0; i < lifecycleListeners.length; i++)
{
- int j = (listeners.length - 1) - i;
- if (!(listeners[j] instanceof HttpSessionListener))
+ int j = (lifecycleListeners.length - 1) - i;
+ if (!(lifecycleListeners[j] instanceof HttpSessionListener))
continue;
HttpSessionListener listener =
- (HttpSessionListener) listeners[j];
+ (HttpSessionListener) lifecycleListeners[j];
try
{
fireContainerEvent(context,
@@ -848,6 +870,7 @@
}
}
}
+
if (ACTIVITY_CHECK) {
accessCount.set(0);
}
@@ -861,7 +884,7 @@
// JBAS-1360 -- Unbind any objects associated with this session
String keys[] = keys();
for (int i = 0; i < keys.length; i++)
- removeAttributeInternal(keys[i], localCall, localOnly, notify);
+ removeAttributeInternal(keys[i], localCall, localOnly, notify, cause);
// Remove this session from our manager's active sessions
removeFromManager(localCall, localOnly);
@@ -1034,6 +1057,8 @@
lastReplicated = 0;
maxUnreplicatedInterval = 0;
this.alwaysReplicateMetadata = true;
+ this.notificationPolicy = null;
+ this.clusterStatus = null;
}
/**
@@ -1057,7 +1082,20 @@
// Parse the real id first, as super.setId() calls add(),
// which depends on having the real id
parseRealId(id);
- super.setId(id);
+
+ // TODO -- should we bypass this if realId hasn't changed? We're removing
+ // and readding every time we fail over, when all we want is a
+ // jvmRoute change to the session id
+
+ if ((this.id != null) && (manager != null))
+ manager.remove(this);
+
+ this.id = id;
+
+ this.clusterStatus = new ClusteredSessionManagementStatus(this.realId, true, null, null);
+
+ if (manager != null)
+ manager.add(this);
}
/**
@@ -1099,6 +1137,53 @@
sessionMetadataDirty();
}
+ @Override
+ public void tellNew()
+ {
+ tellNew(ClusteredSessionNotificationCause.CREATE);
+ }
+
+ public void tellNew(ClusteredSessionNotificationCause cause)
+ {
+ // Notify interested session event listeners
+ fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
+
+ // Notify interested application event listeners
+ if (getNotificationPolicy().isHttpSessionListenerInvocationAllowed(this.clusterStatus, cause, true))
+ {
+ Context context = (Context) manager.getContainer();
+ Object lifecycleListeners[] = context.getApplicationLifecycleListeners();
+ if (lifecycleListeners != null)
+ {
+ HttpSessionEvent event = new HttpSessionEvent(getSession());
+ for (int i = 0; i < lifecycleListeners.length; i++)
+ {
+ if (!(lifecycleListeners[i] instanceof HttpSessionListener))
+ continue;
+ HttpSessionListener listener = (HttpSessionListener) lifecycleListeners[i];
+ try
+ {
+ fireContainerEvent(context, "beforeSessionCreated", listener);
+ listener.sessionCreated(event);
+ fireContainerEvent(context, "afterSessionCreated", listener);
+ }
+ catch (Throwable t)
+ {
+ try
+ {
+ fireContainerEvent(context, "afterSessionCreated", listener);
+ }
+ catch (Exception e)
+ {
+ ;
+ }
+ manager.getContainer().getLogger().error(sm.getString("standardSession.sessionEvent"), t);
+ }
+ }
+ }
+ }
+ }
+
public String toString()
{
StringBuffer buf = new StringBuffer();
@@ -1156,6 +1241,10 @@
// as a proxy for when replication occurred
this.lastReplicated = this.thisAccessedTime;
+ this.notificationPolicy = new LegacyClusteredSessionNotificationPolicy();
+
+ this.clusterStatus = new ClusteredSessionManagementStatus(this.realId, true, null, null);
+
checkAlwaysReplicateMetadata();
// TODO uncomment when work on JBAS-1900 is completed
@@ -1257,9 +1346,20 @@
return excluded;
}
+
+ protected ClusteredSessionNotificationPolicy getNotificationPolicy()
+ {
+ return notificationPolicy;
+ }
+
+ protected void setNotificationPolicy(ClusteredSessionNotificationPolicy notificationPolicy)
+ {
+ this.notificationPolicy = notificationPolicy;
+ }
+
// -------------------------------------- Internal protected method override
-
+
/**
* Method inherited from Tomcat. Return zero-length based string if not found.
*/
@@ -1278,7 +1378,7 @@
{
boolean localCall = true;
boolean localOnly = false;
- removeAttributeInternal(name, localCall, localOnly, notify);
+ removeAttributeInternal(name, localCall, localOnly, notify, ClusteredSessionNotificationCause.MODIFY);
}
/**
@@ -1294,11 +1394,13 @@
* @param localOnly <code>true</code> if the removal should not be
* replicated around the cluster
* @param notify <code>true</code> if listeners should be notified
+ * @param cause the cause of the removal
*/
protected void removeAttributeInternal(String name,
boolean localCall,
boolean localOnly,
- boolean notify)
+ boolean notify,
+ ClusteredSessionNotificationCause cause)
{
// Remove this attribute from our collection
@@ -1310,53 +1412,59 @@
return;
}
+ ClusteredSessionNotificationPolicy policy = getNotificationPolicy();
+
// Call the valueUnbound() method if necessary
HttpSessionBindingEvent event = null;
- if (value instanceof HttpSessionBindingListener)
+ if (value instanceof HttpSessionBindingListener
+ && policy.isHttpSessionBindingListenerInvocationAllowed(this.clusterStatus, cause, name, localCall))
{
event = new HttpSessionBindingEvent(getSession(), name, value);
((HttpSessionBindingListener) value).valueUnbound(event);
}
// Notify interested application event listeners
- Context context = (Context) manager.getContainer();
- Object listeners[] = context.getApplicationEventListeners();
- if (listeners == null)
- return;
- for (int i = 0; i < listeners.length; i++)
+ if (policy.isHttpSessionAttributeListenerInvocationAllowed(this.clusterStatus, cause, name, localCall))
{
- if (!(listeners[i] instanceof HttpSessionAttributeListener))
- continue;
- HttpSessionAttributeListener listener =
- (HttpSessionAttributeListener) listeners[i];
- try
+ Context context = (Context) manager.getContainer();
+ Object lifecycleListeners[] = context.getApplicationEventListeners();
+ if (lifecycleListeners == null)
+ return;
+ for (int i = 0; i < lifecycleListeners.length; i++)
{
- fireContainerEvent(context,
- "beforeSessionAttributeRemoved",
- listener);
- if (event == null)
- {
- event = new HttpSessionBindingEvent
- (getSession(), name, value);
- }
- listener.attributeRemoved(event);
- fireContainerEvent(context,
- "afterSessionAttributeRemoved",
- listener);
- }
- catch (Throwable t)
- {
+ if (!(lifecycleListeners[i] instanceof HttpSessionAttributeListener))
+ continue;
+ HttpSessionAttributeListener listener =
+ (HttpSessionAttributeListener) lifecycleListeners[i];
try
{
fireContainerEvent(context,
+ "beforeSessionAttributeRemoved",
+ listener);
+ if (event == null)
+ {
+ event = new HttpSessionBindingEvent
+ (getSession(), name, value);
+ }
+ listener.attributeRemoved(event);
+ fireContainerEvent(context,
"afterSessionAttributeRemoved",
listener);
}
- catch (Exception e)
+ catch (Throwable t)
{
- ;
+ try
+ {
+ fireContainerEvent(context,
+ "afterSessionAttributeRemoved",
+ listener);
+ }
+ catch (Exception e)
+ {
+ ;
+ }
+ manager.getContainer().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
}
- manager.getContainer().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
}
}
Modified: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java 2008-09-20 21:50:34 UTC (rev 78730)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java 2008-09-20 21:50:48 UTC (rev 78731)
@@ -47,6 +47,7 @@
super(manager, manager.getUseJK());
int maxUnrep = manager.getMaxUnreplicatedInterval() * 1000;
setMaxUnreplicatedInterval(maxUnrep);
+ establishNotificationPolicy();
establishProxy();
}
@@ -58,9 +59,10 @@
*/
public void initAfterLoad(AbstractJBossManager manager)
{
- // Our manager and proxy may have been lost if we were recycled,
+ // Our manager, notification policy and proxy may have been lost if we were replicated,
// so reestablish them
setManager(manager);
+ establishNotificationPolicy();
establishProxy();
// Since attribute map may be transient, we may need to populate it
@@ -158,4 +160,8 @@
boolean localCall,
boolean localOnly);
+ protected void establishNotificationPolicy()
+ {
+ setNotificationPolicy(((JBossCacheManager)manager).getNotificationPolicy());
+ }
}
Modified: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java 2008-09-20 21:50:34 UTC (rev 78730)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java 2008-09-20 21:50:48 UTC (rev 78731)
@@ -49,6 +49,10 @@
import org.jboss.metadata.WebMetaData;
import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.web.tomcat.service.JBossWeb;
+import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationCapability;
+import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationCause;
+import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationPolicy;
+import org.jboss.web.tomcat.service.session.notification.LegacyClusteredSessionNotificationPolicy;
/**
* Implementation of a clustered session manager for
@@ -62,7 +66,7 @@
public class JBossCacheManager
extends JBossManager
implements JBossCacheManagerMBean
-{
+{
/**
* Informational name for this Catalina component
*/
@@ -132,6 +136,9 @@
private boolean trace;
private int maxUnreplicatedInterval_ = WebMetaData.DEFAULT_MAX_UNREPLICATED_INTERVAL;
+
+ private String notificationPolicyClass_;
+ private ClusteredSessionNotificationPolicy notificationPolicy_ = new LegacyClusteredSessionNotificationPolicy();
// ---------------------------------------------------------- Constructors
@@ -173,6 +180,12 @@
this.maxUnreplicatedInterval_ = maxUnrep.intValue();
}
+ this.notificationPolicyClass_ = webMetaData.getClusteredSessionNotificationPolicy();
+ if (this.notificationPolicyClass_ == null || this.notificationPolicyClass_.length() == 0)
+ {
+ this.notificationPolicyClass_ = System.getProperty("jboss.web.clustered.session.notification.policy");
+ }
+
if (proxy_ == null)
proxy_ = new JBossCacheService(cacheObjectNameString_);
@@ -408,8 +421,18 @@
this.maxUnreplicatedInterval_ = maxUnreplicatedInterval;
}
+ public ClusteredSessionNotificationPolicy getNotificationPolicy()
+ {
+ return notificationPolicy_;
+ }
+
// JBossCacheManagerMBean-methods -------------------------------------
+ public void setNotificationPolicy_(ClusteredSessionNotificationPolicy notificationPolicy_)
+ {
+ this.notificationPolicy_ = notificationPolicy_;
+ }
+
public void expireSession(String sessionId)
{
Session session = findSession(sessionId);
@@ -550,7 +573,7 @@
boolean localOnly = true;
try
{
- ses.expire(notify, localCall, localOnly);
+ ses.expire(notify, localCall, localOnly, ClusteredSessionNotificationCause.UNDEPLOY);
}
catch (Throwable t)
{
@@ -637,6 +660,7 @@
}
session.setId(sessionId); // Setting the id leads to a call to add()
+ session.tellNew(ClusteredSessionNotificationCause.CREATE);
if (log_.isDebugEnabled())
{
@@ -814,8 +838,11 @@
if (session != null)
{
add(session);
- // TODO should we advise of a new session?
- //tellNew();
+ // We now notify, since we've added a policy to allow listeners
+ // to discriminate. But the default policy will not allow the
+ // notification to be emitted for FAILOVER, so the standard
+ // behavior is unchanged.
+ session.tellNew(ClusteredSessionNotificationCause.FAILOVER);
}
}
else if (session != null && session.isOutdated())
@@ -1331,7 +1358,7 @@
Thread.currentThread().setContextClassLoader(tcl_);
synchronized (session)
{
- session.removeAttributeInternal(attrKey, localCall, localOnly, notify);
+ session.removeAttributeInternal(attrKey, localCall, localOnly, notify, ClusteredSessionNotificationCause.MODIFY);
}
if (trace)
log_.trace("processRemoteAttributeRemoval: removed attribute " +
@@ -1378,7 +1405,7 @@
try
{
Thread.currentThread().setContextClassLoader(tcl_);
- session.expire(notify, localCall, localOnly);
+ session.expire(notify, localCall, localOnly, ClusteredSessionNotificationCause.TIMEOUT);
}
finally
{
@@ -1454,6 +1481,8 @@
{
super.start();
+ initClusteredSessionNotificationPolicy();
+
// Start the JBossCacheService
// Will need to pass the classloader that is associated with this
//web app so de-serialization will work correctly.
@@ -1521,8 +1550,10 @@
log_.error(msg, e);
throw new LifecycleException(msg, e);
}
- }
+ }
+ initClusteredSessionNotificationPolicy();
+
// Validate attributes
if ("SET_AND_GET".equalsIgnoreCase(replTriggerString_))
@@ -1683,6 +1714,29 @@
}
return (mserver_);
}
+
+ private void initClusteredSessionNotificationPolicy()
+ {
+ if (this.notificationPolicyClass_ != null && this.notificationPolicyClass_.length() == 0)
+ {
+ try
+ {
+ this.notificationPolicy_ = (ClusteredSessionNotificationPolicy) Thread.currentThread().getContextClassLoader().loadClass(this.notificationPolicyClass_).newInstance();
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Failed to instantiate " +
+ ClusteredSessionNotificationPolicy.class.getName() +
+ " " + this.notificationPolicyClass_, e);
+ }
+
+ this.notificationPolicy_.setClusteredSessionExpriationNotificationCapability(new ClusteredSessionNotificationCapability());
+ }
+ }
/**
* Gets the ids of all sessions in the distributed cache and adds
More information about the jboss-cvs-commits
mailing list