[jboss-cvs] JBossAS SVN: r77439 - in trunk: tomcat/src/main/org/jboss/web/tomcat/service/session and 1 other directory.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Mon Aug 25 11:16:30 EDT 2008
Author: bstansberry at jboss.com
Date: 2008-08-25 11:16:30 -0400 (Mon, 25 Aug 2008)
New Revision: 77439
Modified:
trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
Log:
[JBAS-5875] Clustered webapps session manager to use same ObjectName pattern as non-clustered
[JBAS-5876] Expose StandardManager attributes/operations in JBossCacheManagerMBean
[JBAS-5884] Access MBeanServer via Tomcat Registry
General refactor/cleanup of JBossCacheManager/JBossManager. Move stuff to correct class; impose order on completely disorganized methods
Modified: trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockSession.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockSession.java 2008-08-25 15:10:56 UTC (rev 77438)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockSession.java 2008-08-25 15:16:30 UTC (rev 77439)
@@ -27,6 +27,7 @@
import javax.servlet.http.HttpSession;
import org.apache.catalina.session.StandardSessionFacade;
+import org.jboss.metadata.web.jboss.ReplicationTrigger;
import org.jboss.web.tomcat.service.session.AbstractJBossManager;
import org.jboss.web.tomcat.service.session.ClusteredSession;
@@ -46,7 +47,7 @@
*/
public MockSession(MockJBossManager manager)
{
- super(manager, true);
+ super(manager, ReplicationTrigger.SET_AND_NON_PRIMITIVE_GET, true);
}
@Override
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java 2008-08-25 15:10:56 UTC (rev 77438)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AbstractJBossManager.java 2008-08-25 15:16:30 UTC (rev 77439)
@@ -23,10 +23,9 @@
import javax.servlet.http.HttpServletResponse;
-import org.apache.catalina.Session;
import org.apache.catalina.Manager;
+import org.apache.catalina.Session;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
-import org.jboss.metadata.web.jboss.ReplicationTrigger;
/** Common interface for the http session replication managers.
*
@@ -35,19 +34,16 @@
*/
public interface AbstractJBossManager extends Manager
{
- /** Initialize the manager with the web metadata and
- * @param name
- * @param webMetaData
+ /**
+ * Initialize the manager with the web metadata
+ *
+ * @param name the name of the manager, in the form //hostname/context_path
+ * @param webMetaData metadata for the containing web application
* @throws ClusteringNotSupportedException
*/
public void init(String name, JBossWebMetaData webMetaData)
throws ClusteringNotSupportedException;
- /**
- * Gets the event type(s) that indicate a need to replicate the session.
- */
- public ReplicationTrigger getReplicationTrigger();
-
/**
* Retrieve the JvmRoute for the enclosing Engine.
*
@@ -64,7 +60,7 @@
/**
* Remove the active session locally from the manager without replicating to the cluster. This can be
- * useful when the session is exipred, for example, where there is not need to propagate the expiration.
+ * useful when the session is expired, for example, where there is not need to propagate the expiration.
*
* @param session
*/
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java 2008-08-25 15:10:56 UTC (rev 77438)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java 2008-08-25 15:16:30 UTC (rev 77439)
@@ -117,29 +117,14 @@
if(version != null)
{
String realId = getIdFromFqn(fqn, isBuddy);
-
- ClusteredSession session = manager_.findLocalSession(realId);
- if (session == null)
- {
- String owner = isBuddy ? getBuddyOwner(fqn) : null;
- // Notify the manager that an unloaded session has been updated
- manager_.unloadedSessionChanged(realId, owner,
+ String owner = isBuddy ? getBuddyOwner(fqn) : null;
+ // Notify the manager that a session has been updated
+ boolean updated = manager_.sessionChangedInDistributedCache(realId, owner,
+ version.intValue(),
(SessionTimestamp) data.get(JBossCacheService.TIMESTAMP_KEY),
(SessionMetadata) data.get(JBossCacheService.METADATA_KEY));
- }
- else if (session.isNewData(version.intValue()))
+ if (!updated && !isBuddy)
{
- // Need to invalidate the loaded session
- session.setOutdatedVersion(version.intValue());
- if(log_.isTraceEnabled())
- {
- log_.trace("nodeDirty(): session in-memory data is " +
- "invalidated with id: " + realId + " and version: " +
- version.intValue());
- }
- }
- else if (!isBuddy)
- {
log_.warn("Possible concurrency problem: Replicated version id " +
version + " matches in-memory version for session " + realId);
}
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java 2008-08-25 15:10:56 UTC (rev 77438)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java 2008-08-25 15:16:30 UTC (rev 77439)
@@ -118,7 +118,7 @@
protected transient SessionMetadata metadata = new SessionMetadata();
/**
- * The last version that was passed to {@link #setOutdatedVersion} or
+ * The last version that was passed to {@link #setDistributedVersion} or
* <code>0</code> if <code>setIsOutdated(false)</code> was subsequently called.
*/
protected transient int outdatedVersion;
@@ -178,10 +178,10 @@
protected static StringManager sm =
StringManager.getManager(ClusteredSession.class.getPackage().getName());
- protected ClusteredSession(AbstractJBossManager manager, boolean useJK)
+ protected ClusteredSession(AbstractJBossManager manager, ReplicationTrigger replicationTrigger, boolean useJK)
{
super(manager);
- invalidationPolicy = manager.getReplicationTrigger();
+ invalidationPolicy = replicationTrigger;
this.useJK = useJK;
this.firstAccess = true;
checkAlwaysReplicateTimestamp();
@@ -198,12 +198,6 @@
return thisAccessedTime < outdatedTime;
}
- public void setOutdatedVersion(int version)
- {
- this.outdatedVersion = version;
- outdatedTime = System.currentTimeMillis();
- }
-
public void clearOutdated()
{
// Only overwrite the access time if access() hasn't been called
@@ -277,37 +271,37 @@
return useJK;
}
- /**
- * Check to see if the input version number is greater than I am. If it is,
- * it means we will need to invalidate the in-memory cache.
- * @param version
- * @return
- */
- public boolean isNewData(int version)
- {
- return (this.version < version);
- }
-
public int getVersion()
{
return version;
}
-
- public void setVersion(int version)
- {
- this.version = version;
- }
/**
- * There are couple ways to generate this version number.
- * But we will stick with the simple one of incrementing for now.
+ * Increment our version due to local changes.
*
* @return the new version
*/
- public int incrementVersion()
+ public int incrementVersionFromLocalActivity()
{
return version++;
}
+
+ /**
+ * Update our version due to changes in the distributed cache.
+ *
+ * @param version the distributed cache version
+ * @return <code>true</code>
+ */
+ public boolean setVersionFromDistributedCache(int version)
+ {
+ boolean outdated = this.version < version;
+ if (outdated)
+ {
+ this.outdatedVersion = version;
+ outdatedTime = System.currentTimeMillis();
+ }
+ return outdated;
+ }
/**
* Overrides the superclass to calculate
@@ -387,6 +381,16 @@
{
return null;
}
+
+ /**
+ * Gets the sessions creation time, skipping any validity check.
+ *
+ * @return the creation time
+ */
+ public long getCreationTimeInternal()
+ {
+ return creationTime;
+ }
/**
* This is called after loading a session to initialize the transient values.
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java 2008-08-25 15:10:56 UTC (rev 77438)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java 2008-08-25 15:16:30 UTC (rev 77439)
@@ -45,7 +45,7 @@
*/
public JBossCacheClusteredSession(JBossCacheManager manager)
{
- super(manager, manager.getUseJK());
+ super(manager, manager.getReplicationTrigger(), manager.getUseJK());
int maxUnrep = manager.getMaxUnreplicatedInterval() * 1000;
setMaxUnreplicatedInterval(maxUnrep);
establishProxy();
@@ -135,7 +135,7 @@
log.trace("processSessionRepl(): session is dirty. Will increment " +
"version from: " + getVersion() + " and replicate.");
}
- this.incrementVersion();
+ this.incrementVersionFromLocalActivity();
proxy_.putSession(realId, this);
// Allow subclasses to replicate attributes if needed
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java 2008-08-25 15:10:56 UTC (rev 77438)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java 2008-08-25 15:16:30 UTC (rev 77439)
@@ -22,7 +22,6 @@
package org.jboss.web.tomcat.service.session;
import java.security.AccessController;
-import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -32,15 +31,13 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
-import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
+import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.TransactionManager;
-import javax.transaction.RollbackException;
import org.apache.catalina.Context;
-import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Session;
import org.apache.catalina.Valve;
@@ -58,7 +55,6 @@
import org.jboss.metadata.web.jboss.ReplicationGranularity;
import org.jboss.metadata.web.jboss.ReplicationTrigger;
import org.jboss.metadata.web.jboss.SnapshotMode;
-import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.util.loading.ContextClassLoaderSwitcher;
import org.jboss.web.tomcat.service.session.ClusteredSession.SessionMetadata;
import org.jboss.web.tomcat.service.session.ClusteredSession.SessionTimestamp;
@@ -76,61 +72,55 @@
extends JBossManager
implements JBossCacheManagerMBean
{
-
+ // --------------------------------------------------------------- Constants
+
/**
* Informational name for this Catalina component
*/
- static final String info_ = "JBossCacheManager/1.0";
+ private static final String info_ = "JBossCacheManager/1.0";
- // -- Class attributes ---------------------------------
+ // ------------------------------------------------------------------ Fields
- /**
- * The transaction manager.
- */
+ /** The transaction manager. */
private TransactionManager tm;
- /**
- * Proxy-object for the JBossCacheService
- */
+ /** Proxy-object for the JBossCache */
private JBossCacheService proxy_;
- /**
- * Id/timestamp of sessions in cache that we haven't loaded
- */
+ /** Id/timestamp of sessions in distributedcache that we haven't loaded locally*/
private Map<String, OwnedSessionUpdate> unloadedSessions_ =
new ConcurrentHashMap<String, OwnedSessionUpdate>();
+ /** Number of passivated sessions */
private AtomicInteger passivatedCount_ = new AtomicInteger();
- private int maxPassivatedCount_;
- private int maxLocalActiveCounter_;
+ /** Maximum number of concurrently passivated sessions */
+ private AtomicInteger maxPassivatedCount_ = new AtomicInteger();
+ /** Injected pojo cache */
private PojoCache pojoCache_;
+ /** Injected plain cache */
private Cache plainCache_;
- /**
- * If set to true, will add a JvmRouteFilter to the request.
- */
+ /** If set to true, will add a JvmRouteFilter to the request. */
private Boolean useJK_;
/** Are we running embedded in JBoss? */
private boolean embedded_ = false;
- /** Our JMX Server */
- private MBeanServer mserver_ = null;
-
/** Our ClusteredSessionValve's snapshot mode. */
private SnapshotMode snapshotMode_ = null;
/** Our ClusteredSessionValve's snapshot interval. */
private int snapshotInterval_ = 0;
- /**
- * Replication granularity.
- */
+ /** Replication granularity. */
private ReplicationGranularity replicationGranularity_;
+ /** Policy to determine if a session is dirty */
+ private ReplicationTrigger replicationTrigger_;
+
/**
* Whether we use batch mode replication for field level granularity.
* We store this in a Boolean rather than a primitive so JBossCacheCluster
@@ -141,17 +131,13 @@
/** Class loader for this web app. */
private ClassLoader tcl_;
- /**
- * The snapshot manager we are using.
- */
+ /** The snapshot manager we are using. */
private SnapshotManager snapshotManager_;
- /**
- * The name of our cache's configuration
- */
+ /** The name of our cache's configuration */
private String cacheConfigName_;
- /** Did we get our cache from a cache manager */
+ /** Did we get our cache from a cache manager? */
private boolean cacheFromCacheManager_;
private int maxUnreplicatedInterval_ = -1;
@@ -175,22 +161,28 @@
this.plainCache_ = cache;
}
+ // ---------------------------------------------------- AbstractJBossManager
+
/**
- * Initializes this Manager when running in embedded mode.
+ * {@inheritDoc}
* <p>
* <strong>NOTE:</strong> This method should not be called when
* running unembedded.
* </p>
*/
+ @Override
public void init(String name, JBossWebMetaData webMetaData)
throws ClusteringNotSupportedException
{
super.init(name, webMetaData);
ReplicationConfig repCfg = webMetaData.getReplicationConfig();
+ if (repCfg != null)
+ {
+ replicationTrigger_ = repCfg.getReplicationTrigger();
+ replicationGranularity_ = repCfg.getReplicationGranularity();
+ }
- replicationGranularity_ = repCfg.getReplicationGranularity();
-
// Only configure JK usage if it was explicitly set; otherwise
// wait until we're starting when we can check for a jvmRoute
// in our containing Engine
@@ -213,6 +205,7 @@
}
log_.debug("init(): replicationGranularity_ is " + replicationGranularity_ +
+ " and replicationTrigger is " + replicationTrigger_ +
" and replicationFieldBatchMode is " + replicationFieldBatchMode_ +
" and useJK is " + useJK_ +
" and snapshotMode is " + snapshotMode_ +
@@ -232,598 +225,54 @@
}
/**
- * Accesses the underlying cache and creates the proxy
- *
- * @throws ClusteringNotSupportedException
+ * {@inheritDoc}
+ * <p>
+ * Removes the session from this Manager's collection of actively managed
+ * sessions. Also removes the session from this server's copy of the
+ * distributed cache (but does not remove it from other servers'
+ * distributed cache).
+ * </p>
*/
- private void initCacheProxy() throws ClusteringNotSupportedException
+ public void removeLocal(Session session)
{
- // We are likely going to cause creation and start of a cache here;
- // we don't want to leak the TCCL to cache/jgroups threads,
- // so we switch it to our classloader
- ContextClassLoaderSwitcher switcher = (ContextClassLoaderSwitcher) AccessController.doPrivileged(ContextClassLoaderSwitcher.INSTANTIATOR);
- ContextClassLoaderSwitcher.SwitchContext switchContext = switcher.getSwitchContext(getClass().getClassLoader());
- try
+ ClusteredSession clusterSess = (ClusteredSession) session;
+ synchronized (clusterSess)
{
- if (ReplicationGranularity.FIELD == replicationGranularity_)
- {
- PojoCache pojoC = getPojoCache();
- if (pojoC == null)
- {
- pojoC = Util.findPojoCache(cacheConfigName_);
-
- cacheFromCacheManager_ = true;
-
- if (pojoC.getCache().getCacheStatus() != CacheStatus.STARTED)
- pojoC.getCache().start();
- }
- proxy_ = new FieldBasedJBossCacheService(pojoC);
- }
- else
- {
- Cache pc = getPlainCache();
- if (pc == null)
- {
- pc = Util.findPlainCache(cacheConfigName_);
-
- cacheFromCacheManager_ = true;
-
- if (pc.getCacheStatus() != CacheStatus.STARTED)
- pc.start();
- }
- proxy_ = new JBossCacheService(pc);
- }
- }
- finally
- {
- // Restore the TCCL
- switchContext.reset();
- }
- }
+ String realId = clusterSess.getRealId();
+ if (realId == null) return;
- // ------------------------------------------------------------- Properties
-
- /**
- * Gets the <code>FieldBasedJBossCacheService</code> through which we interact
- * with the PojoCache.
- *
- * @throws IllegalStateException if we are not using field based replication
- */
- public FieldBasedJBossCacheService getPojoCacheService()
- {
- if (proxy_ != null && !(proxy_ instanceof FieldBasedJBossCacheService))
- throw new IllegalStateException("PojoCache not being used");
- return (FieldBasedJBossCacheService) proxy_;
- }
-
- /**
- * Gets the <code>JBossCacheService</code> through which we interact
- * with the <code>Cache</code>.
- */
- public JBossCacheService getCacheService()
- {
- return proxy_;
- }
-
- /**
- * Gets when sessions are replicated to the other nodes.
- * The default value, "instant", synchronously replicates changes
- * to the other nodes. In this case, the "SnapshotInterval" attribute
- * is not used.
- * The "interval" mode, in association with the "SnapshotInterval"
- * attribute, indicates that Tomcat will only replicate modified
- * sessions every "SnapshotInterval" miliseconds at most.
- *
- * @see #getSnapshotInterval()
- */
- public SnapshotMode getSnapshotMode()
- {
- return snapshotMode_;
- }
-
- /**
- * Sets when sessions are replicated to the other nodes. Valid values are:
- * <ul>
- * <li>instant</li>
- * <li>interval</li>
- * </ul>
- */
- public void setSnapshotMode(SnapshotMode snapshotMode)
- {
- this.snapshotMode_ = snapshotMode;
- }
-
- /**
- * Gets how often session changes should be replicated to other nodes.
- * Only relevant if property {@link #getSnapshotMode() snapshotMode} is
- * set to <code>interval</code>.
- *
- * @return the number of milliseconds between session replications.
- */
- public int getSnapshotInterval()
- {
- return snapshotInterval_;
- }
-
- /**
- * Sets how often session changes should be replicated to other nodes.
- *
- * @param snapshotInterval the number of milliseconds between
- * session replications.
- */
- public void setSnapshotInterval(int snapshotInterval)
- {
- this.snapshotInterval_ = snapshotInterval;
- }
-
- /**
- * Gets whether the <code>Engine</code> in which we are running
- * uses <code>mod_jk</code>.
- */
- public boolean getUseJK()
- {
- return useJK_ == null ? false : useJK_.booleanValue();
- }
-
- /**
- * Sets whether the <code>Engine</code> in which we are running
- * uses <code>mod_jk</code>.
- */
- public void setUseJK(boolean useJK)
- {
- this.useJK_ = Boolean.valueOf(useJK);
- }
-
- /**
- * Returns the replication granularity.
- *
- * @see JBossWebMetaData#REPLICATION_GRANULARITY_ATTRIBUTE
- * @see JBossWebMetaData#REPLICATION_GRANULARITY_FIELD
- * @see JBossWebMetaData#REPLICATION_GRANULARITY_SESSION
- */
- public ReplicationGranularity getReplicationGranularity()
- {
- return replicationGranularity_;
- }
-
- /**
- * Sets the granularity of session data replicated across the cluster.
- * Valid values are:
- * <ul>
- * <li>SESSION</li>
- * <li>ATTRIBUTE</li>
- * <li>FIELD</li>
- * </ul>
- */
- public void setReplicationGranularity(ReplicationGranularity granularity)
- {
- this.replicationGranularity_ = granularity;
- }
-
- /**
- * Returns the replication granularity.
- *
- * @see JBossWebMetaData#REPLICATION_GRANULARITY_ATTRIBUTE
- * @see JBossWebMetaData#REPLICATION_GRANULARITY_FIELD
- * @see JBossWebMetaData#REPLICATION_GRANULARITY_SESSION
- */
- public String getReplicationGranularityString()
- {
- return replicationGranularity_ == null ? null : replicationGranularity_.toString();
- }
-
- /**
- * Sets the granularity of session data replicated across the cluster.
- * Valid values are:
- * <ul>
- * <li>SESSION</li>
- * <li>ATTRIBUTE</li>
- * <li>FIELD</li>
- * </ul>
- */
- public void setReplicationGranularityString(String granularity)
- {
- setReplicationGranularity(granularity == null ? null :
- ReplicationGranularity.fromString(granularity.toUpperCase()));
- }
-
- public String getReplicationTriggerString()
- {
- return replicationTrigger_ == null ? null : replicationTrigger_.toString();
- }
-
- public void setReplicationTriggerString(String trigger)
- {
- setReplicationTrigger(trigger == null ? null : ReplicationTrigger.fromString(trigger.toUpperCase()));
- }
-
- /**
- * Gets whether, if replication granularity is set to <code>FIELD</code>,
- * replication should be done in batch mode. Ignored if field-level
- * granularity is not used.
- */
- public Boolean isReplicationFieldBatchMode()
- {
- return replicationFieldBatchMode_;
- }
-
- /**
- * Sets whether, if replication granularity is set to <code>FIELD</code>,
- * replication should be done in batch mode. Ignored if field-level
- * granularity is not used.
- */
- public void setReplicationFieldBatchMode(boolean replicationFieldBatchMode)
- {
- this.replicationFieldBatchMode_ = Boolean.valueOf(replicationFieldBatchMode);
- }
-
- public int getMaxUnreplicatedInterval()
- {
- return maxUnreplicatedInterval_;
- }
-
- public void setMaxUnreplicatedInterval(int maxUnreplicatedInterval)
- {
- this.maxUnreplicatedInterval_ = maxUnreplicatedInterval;
- }
-
-
- // JBossCacheManagerMBean-methods -------------------------------------
-
- public void expireSession(String sessionId)
- {
- Session session = findSession(sessionId);
- if (session != null)
- session.expire();
- }
-
- public String getLastAccessedTime(String sessionId)
- {
- Session session = findSession(sessionId);
- if(session == null) {
- if (log_.isTraceEnabled())
+ if (trace_)
{
- log_.trace("getLastAccessedTime(): Session " + sessionId +
- " not found");
+ log_.trace("Removing session from local store with id: " + realId);
}
- return "";
- }
- return new Date(session.getLastAccessedTime()).toString();
- }
- public Object getSessionAttribute(String sessionId, String key)
- {
- ClusteredSession session = (ClusteredSession) findSession(sessionId);
- return (session == null) ? null : session.getAttribute(key);
- }
-
- public String getSessionAttributeString(String sessionId, String key)
- {
- Object attr = getSessionAttribute(sessionId, key);
- return (attr == null) ? null : attr.toString();
- }
-
- public String listLocalSessionIds()
- {
- return reportSessionIds(sessions_.keySet());
- }
-
- public String listSessionIds()
- {
- Set ids = new HashSet(sessions_.keySet());
- ids.addAll(unloadedSessions_.keySet());
- return reportSessionIds(ids);
- }
-
- private String reportSessionIds(Set ids)
- {
- StringBuffer sb = new StringBuffer();
- boolean added = false;
- for (Iterator it = ids.iterator(); it.hasNext(); )
- {
- if (added)
- {
- sb.append(',');
+ try {
+ // Ignore any cache notifications that our own work generates
+ SessionReplicationContext.startCacheActivity();
+ clusterSess.removeMyselfLocal();
}
- else
+ finally
{
- added = true;
- }
-
- sb.append(it.next());
- }
- return sb.toString();
- }
+ SessionReplicationContext.finishCacheActivity();
+
+ // We don't want to replicate this session at the end
+ // of the request; the removal process took care of that
+ SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
+
+ sessions_.remove(realId);
+ stats_.removeStats(realId);
- public long getLocalActiveSessionCount()
- {
- return activeCounter_;
- }
-
- public long getMaxLocalActiveSessionCount()
- {
- return super.getMaxActiveSessionCount();
- }
-
- public long getMaxPassivatedSessionCount()
- {
- return 0;
- }
-
- public long getPassivatedSessionCount()
- {
- return passivatedCount_.get();
- }
-
- @Override
- public int getActiveSessions()
- {
- return calcActiveSessions();
- }
-
- @Override
- public long getActiveSessionCount()
- {
- return calcActiveSessions();
- }
-
- public long getPassivationMaxIdleTime()
- {
- return passivationMaxIdleTime_;
- }
-
- public long getPassivationMinIdleTime()
- {
- return passivationMinIdleTime_;
- }
-
-
- // Manager-methods -------------------------------------
-
- /**
- * Start this Manager
- *
- * @throws org.apache.catalina.LifecycleException
- *
- */
- public void start() throws LifecycleException
- {
- if (embedded_)
- {
- startEmbedded();
- }
- else
- {
- startUnembedded();
- }
-
- log_.debug("JBossCacheManager for " + getContainer().getName() + " started");
- }
-
- public void stop() throws LifecycleException
- {
- if (!started_)
- {
- throw new IllegalStateException("Manager not started");
- }
-
- log_.debug("Stopping");
-
- // Block for any ongoing backgroundProcess, then disable
- synchronized (backgroundProcessAllowed)
- {
- backgroundProcessAllowed.set(false);
- }
-
- resetStats();
-
- // Notify our interested LifecycleListeners
- lifecycle_.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
-
- clearSessions();
-
- // Don't leak the classloader
- tcl_ = null;
-
- proxy_.stop();
- proxy_ = null;
-
- tm = null;
-
- if (cacheFromCacheManager_)
- {
- // Let the manager know we are done with the cache
- releaseCacheToManager();
- }
-
- snapshotManager_.stop();
-
- // Clean up maps
- sessions_.clear();
- unloadedSessions_.clear();
-
- passivatedCount_.set(0);
-
- started_ = false;
-
- // Notify our interested LifecycleListeners
- lifecycle_.fireLifecycleEvent(AFTER_STOP_EVENT, this);
-
- try
- {
- unregisterMBeans();
- }
- catch (Exception e)
- {
- log_.error("Could not unregister ManagerMBean from MBeanServer", e);
- }
- }
-
- /**
- * Clear the underlying cache store.
- */
- protected void clearSessions()
- {
- boolean passivation = isPassivationEnabled();
- // First, the sessions we have actively loaded
- ClusteredSession[] sessions = findLocalSessions();
- for(int i=0; i < sessions.length; i++)
- {
- ClusteredSession ses = sessions[i];
- // JBCLUSTER-15
- // if session passivation is enabled, passivate sessions instead of expiring them which means
- // they'll be available to the manager for activation after a restart.
- if (log_.isTraceEnabled())
- {
- log_.trace("clearSessions(): clear session by expiring or passivating: " + ses);
+ // Compute how long this session has been alive, and update
+ // our statistics accordingly
+ int timeAlive = (int) ((System.currentTimeMillis() - clusterSess.getCreationTimeInternal())/1000);
+ sessionExpired(timeAlive);
}
- boolean notify = true;
- boolean localCall = true;
- boolean localOnly = true;
- try
- {
- if(passivation && ses.isValid())
- {
- processSessionPassivation(ses.getRealId());
- }
- else
- {
- ses.expire(notify, localCall, localOnly);
- }
- }
- catch (Throwable t)
- {
- log_.warn("clearSessions(): Caught exception expiring or passivating session " +
- ses.getIdInternal(), t);
- }
- finally
- {
- // Guard against leaking memory if anything is holding a
- // ref to the session by clearing its internal state
- ses.recycle();
- }
- }
-
- String action = passivation ? "evicting" : "removing";
- Set<Map.Entry<String, OwnedSessionUpdate>> unloaded =
- unloadedSessions_.entrySet();
- for (Iterator<Map.Entry<String, OwnedSessionUpdate>> it = unloaded.iterator(); it.hasNext();)
- {
- Map.Entry<String, OwnedSessionUpdate> entry = it.next();
- String realId = entry.getKey();
- try
- {
- if (passivation)
- {
- OwnedSessionUpdate osu = entry.getValue();
- // Ignore the marker entries for our passivated sessions
- if (!osu.passivated)
- {
- proxy_.evictSession(realId, osu.owner);
- }
- }
- else
- {
- proxy_.removeSessionLocal(realId, false);
- }
- }
- catch (Exception e)
- {
- // Not as big a problem; we don't own the session
- log_.debug("Problem " + action + " session " + realId + " -- " + e);
- }
- it.remove();
}
}
/**
- * Create a new session with a generated id.
+ * {@inheritDoc}
*/
- public Session createSession()
- {
- return createSession(null);
- }
-
- /**
- * Create a new session.
- *
- * @param sessionId the id to use, or <code>null</code> if we should
- * generate a new id
- *
- * @return the session
- *
- * @throws IllegalStateException if the current number of active sessions
- * exceeds the maximum number allowed
- */
- public Session createSession(String sessionId)
- {
- // First check if we've reached the max allowed sessions,
- // then try to expire/passivate sessions to free memory
- // maxActive_ -1 is unlimited
- // We check here for maxActive instead of in add(). add() gets called
- // when we load an already existing session from the distributed cache
- // (e.g. in a failover) and we don't want to fail in that situation.
-
- if(maxActive_ != -1 && calcActiveSessions() >= maxActive_)
- {
- if (log_.isTraceEnabled())
- {
- log_.trace("createSession(): active sessions = " + calcActiveSessions() +
- " and max allowed sessions = " + maxActive_);
- }
-
- processExpires();
-
- if (calcActiveSessions() >= maxActive_)
- {
- // Exceeds limit. We need to reject it.
- rejectedCounter_++;
- // Catalina api does not specify what happens
- // but we will throw a runtime exception for now.
- String msgEnd = (sessionId == null) ? "" : " id " + sessionId;
- throw new IllegalStateException("createSession(): number of " +
- "active sessions exceeds the maximum limit: " +
- maxActive_ + " when trying to create session" + msgEnd);
- }
- }
-
- ClusteredSession session = createEmptyClusteredSession();
-
- session.setNew(true);
- session.setCreationTime(System.currentTimeMillis());
- session.setMaxInactiveInterval(this.maxInactiveInterval_);
- session.setValid(true);
-
- if (sessionId == null)
- {
- sessionId = this.getNextId();
-
- // We are using mod_jk for load balancing. Append the JvmRoute.
- if (getUseJK())
- {
- if (log_.isTraceEnabled())
- {
- log_.trace("createSession(): useJK is true. Will append JvmRoute: " + this.getJvmRoute());
- }
- sessionId += "." + this.getJvmRoute();
- }
- }
-
- session.setId(sessionId); // Setting the id leads to a call to add()
-
- if (log_.isTraceEnabled())
- {
- log_.trace("Created a ClusteredSession with id: " + sessionId);
- }
-
- createdCounter_++; // the call to add() handles the other counters
-
- // Add this session to the set of those potentially needing replication
- SessionReplicationContext.bindSession(session, snapshotManager_);
-
- return session;
- }
-
public boolean storeSession(Session baseSession)
{
boolean stored = false;
@@ -833,7 +282,7 @@
synchronized (session)
{
- if (log_.isTraceEnabled())
+ if (trace_)
{
log_.trace("check to see if needs to store and replicate " +
"session with id " + session.getIdInternal());
@@ -857,7 +306,7 @@
stored = true;
stats_.updateReplicationStats(realId, elapsed);
}
- else if (log_.isTraceEnabled())
+ else if (trace_)
{
log_.trace("Session " + session.getIdInternal() +
" did not require replication.");
@@ -868,6 +317,11 @@
return stored;
}
+ // ----------------------------------------------------------------- Manager
+
+ /**
+ * {@inheritDoc}
+ */
public void add(Session session)
{
if (session == null)
@@ -917,12 +371,13 @@
storeSession(session);
}
+ // Update counters
calcActiveSessions();
- if (log_.isTraceEnabled())
+ if (trace_)
{
log_.trace("Session with id=" + session.getIdInternal() + " added. " +
- "Current active sessions " + activeCounter_);
+ "Current active sessions " + localActiveCounter_.get());
}
}
}
@@ -931,27 +386,91 @@
// createEmptyClusteredSession to avoid a cast
public Session createEmptySession()
{
- log_.trace("Creating an empty ClusteredSession");
+ if (trace_)
+ {
+ log_.trace("Creating an empty ClusteredSession");
+ }
return createEmptyClusteredSession();
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public Session createSession()
+ {
+ return createSession(null);
+ }
- private JBossCacheClusteredSession createEmptyClusteredSession()
- {
+ /**
+ * {@inheritDoc}
+ */
+ public Session createSession(String sessionId)
+ {
+ // First check if we've reached the max allowed sessions,
+ // then try to expire/passivate sessions to free memory
+ // maxActiveAllowed_ -1 is unlimited
+ // We check here for maxActive instead of in add(). add() gets called
+ // when we load an already existing session from the distributed cache
+ // (e.g. in a failover) and we don't want to fail in that situation.
- JBossCacheClusteredSession session = null;
- switch (replicationGranularity_)
+ if(maxActiveAllowed_ != -1 && calcActiveSessions() >= maxActiveAllowed_)
{
- case ATTRIBUTE:
- session = new AttributeBasedClusteredSession(this);
- break;
- case FIELD:
- session = new FieldBasedClusteredSession(this);
- break;
- default:
- session = new SessionBasedClusteredSession(this);
- break;
+ if (trace_)
+ {
+ log_.trace("createSession(): active sessions = " + calcActiveSessions() +
+ " and max allowed sessions = " + maxActiveAllowed_);
+ }
+
+ processExpirationPassivation();
+
+ if (calcActiveSessions() >= maxActiveAllowed_)
+ {
+ // Exceeds limit. We need to reject it.
+ rejectedCounter_.incrementAndGet();
+ // Catalina api does not specify what happens
+ // but we will throw a runtime exception for now.
+ String msgEnd = (sessionId == null) ? "" : " id " + sessionId;
+ throw new IllegalStateException("createSession(): number of " +
+ "active sessions exceeds the maximum limit: " +
+ maxActiveAllowed_ + " when trying to create session" + msgEnd);
+ }
}
+
+ ClusteredSession session = createEmptyClusteredSession();
+
+ session.setNew(true);
+ session.setCreationTime(System.currentTimeMillis());
+ session.setMaxInactiveInterval(this.maxInactiveInterval_);
+ session.setValid(true);
+
+ if (sessionId == null)
+ {
+ sessionId = this.getNextId();
+
+ // We are using mod_jk for load balancing. Append the JvmRoute.
+ if (getUseJK())
+ {
+ if (trace_)
+ {
+ log_.trace("createSession(): useJK is true. Will append JvmRoute: " + this.getJvmRoute());
+ }
+ sessionId += "." + this.getJvmRoute();
+ }
+ }
+
+ session.setId(sessionId); // Setting the id leads to a call to add()
+
+ if (trace_)
+ {
+ log_.trace("Created a ClusteredSession with id: " + sessionId);
+ }
+
+ createdCounter_.incrementAndGet(); // the call to add() handles the other counters
+
+ // Add this session to the set of those potentially needing replication
+ SessionReplicationContext.bindSession(session, snapshotManager_);
+
return session;
}
@@ -987,7 +506,7 @@
if (session == null
&& !SessionReplicationContext.isSessionBoundAndExpired(realId, snapshotManager_))
{
- if (log_.isTraceEnabled())
+ if (trace_)
log_.trace("Checking for session " + realId + " in the distributed cache");
session = loadSession(realId);
@@ -1000,7 +519,7 @@
}
else if (session != null && session.isOutdated())
{
- if (log_.isTraceEnabled())
+ if (trace_)
log_.trace("Updating session " + realId + " from the distributed cache");
// Need to update it from the cache
@@ -1039,7 +558,7 @@
// Make a thread-safe copy of the new id list to work with
Set ids = new HashSet(unloadedSessions_.keySet());
- if(log_.isTraceEnabled()) {
+ if(trace_) {
log_.trace("findSessions: loading sessions from distributed cache: " + ids);
}
@@ -1053,37 +572,14 @@
}
/**
- * Returns all the sessions that are being actively managed by this manager.
- * This includes those that were created on this server, those that were
- * brought into local management by a call to
- * {@link #findLocalSession(String)} as well as all sessions brought into
- * local management by a call to {@link #findSessions()}.
+ * {@inheritDoc}
*/
- public ClusteredSession[] findLocalSessions()
+ public String getInfo()
{
- Collection coll = sessions_.values();
- ClusteredSession[] sess = new ClusteredSession[coll.size()];
- sess = (ClusteredSession[]) coll.toArray(sess);
- return sess;
+ return info_;
}
/**
- * Returns the given session if it is being actively managed by this manager.
- * An actively managed session is on that was either created on this server,
- * brought into local management by a call to
- * {@link #findLocalSession(String)} or brought into local management by a
- * call to {@link #findSessions()}.
- *
- * @param realId the session id, with any trailing jvmRoute removed.
- *
- * @see #getRealId(String)
- */
- public ClusteredSession findLocalSession(String realId)
- {
- return (ClusteredSession) sessions_.get(realId);
- }
-
- /**
* Removes the session from this Manager's collection of actively managed
* sessions. Also removes the session from the distributed cache, both
* on this server and on all other server to which this one replicates.
@@ -1097,7 +593,7 @@
if (realId == null)
return;
- if (log_.isTraceEnabled())
+ if (trace_)
{
log_.trace("Removing session from store with id: " + realId);
}
@@ -1116,278 +612,607 @@
sessions_.remove(realId);
stats_.removeStats(realId);
-// activeCounter_--;
+
+ // Compute how long this session has been alive, and update
+ // our statistics accordingly
+ int timeAlive = (int) ((System.currentTimeMillis() - clusterSess.getCreationTimeInternal())/1000);
+ sessionExpired(timeAlive);
}
}
}
+ // --------------------------------------------------------------- Lifecycle
+
/**
- * Removes the session from this Manager's collection of actively managed
- * sessions. Also removes the session from this server's copy of the
- * distributed cache (but does not remove it from other servers'
- * distributed cache).
+ * {@inheritDoc}
*/
- public void removeLocal(Session session)
+ @Override
+ public void start() throws LifecycleException
{
- ClusteredSession clusterSess = (ClusteredSession) session;
- synchronized (clusterSess)
+ if (embedded_)
{
- String realId = clusterSess.getRealId();
- if (realId == null) return;
+ startEmbedded();
+ }
+ else
+ {
+ startUnembedded();
+ }
+
+ log_.debug("JBossCacheManager for " + getContainer().getName() + " started");
+ }
- if (log_.isTraceEnabled())
- {
- log_.trace("Removing session from local store with id: " + realId);
- }
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void stop() throws LifecycleException
+ {
+ if (!started_)
+ {
+ throw new IllegalStateException("Manager not started");
+ }
+
+ log_.debug("Stopping");
+
+ // Block for any ongoing backgroundProcess, then disable
+ synchronized (backgroundProcessAllowed)
+ {
+ backgroundProcessAllowed.set(false);
+ }
+
+ resetStats();
+
+ // Notify our interested LifecycleListeners
+ lifecycle_.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+
+ clearSessions();
+
+ // Don't leak the classloader
+ tcl_ = null;
+
+ proxy_.stop();
+ proxy_ = null;
+
+ tm = null;
+
+ if (cacheFromCacheManager_)
+ {
+ // Let the manager know we are done with the cache
+ releaseCacheToManager();
+ }
+
+ snapshotManager_.stop();
+
+ // Clean up maps
+ sessions_.clear();
+ unloadedSessions_.clear();
+
+ passivatedCount_.set(0);
+
+ started_ = false;
+
+ // Notify our interested LifecycleListeners
+ lifecycle_.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+
+ unregisterManagerMBean();
+ }
+
- try {
- // Ignore any cache notifications that our own work generates
- SessionReplicationContext.startCacheActivity();
- clusterSess.removeMyselfLocal();
- }
- finally
- {
- SessionReplicationContext.finishCacheActivity();
-
- // We don't want to replicate this session at the end
- // of the request; the removal process took care of that
- SessionReplicationContext.sessionExpired(clusterSess, realId, snapshotManager_);
-
- sessions_.remove(realId);
- stats_.removeStats(realId);
+ // -------------------------------------------------- JBossCacheManagerMBean
- // Update counters.
- // It's a bit ad-hoc to do it here. But since we currently call
- // this when session expires ...
- expiredCounter_++;
-// activeCounter_--;
- }
- }
+ /**
+ * {@inheritDoc}
+ */
+ public void expireSession(String sessionId)
+ {
+ Session session = findSession(sessionId);
+ if (session != null)
+ session.expire();
}
/**
- * Loads a session from the distributed store. If an existing session with
- * the id is already under local management, that session's internal state
- * will be updated from the distributed store. Otherwise a new session
- * will be created and added to the collection of those sessions under
- * local management.
- *
- * @param realId id of the session-id with any jvmRoute removed
- *
- * @return the session or <code>null</code> if the session cannot be found
- * in the distributed store
+ * {@inheritDoc}
*/
- protected ClusteredSession loadSession(String realId)
+ public String getCacheConfigName()
{
- if (realId == null)
+ return (cacheFromCacheManager_ ? cacheConfigName_ : null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getCreationTime(String sessionId)
+ {
+ Session session = findSession(sessionId);
+ if(session == null)
{
- return null;
+ log_.info("getCreationTime(): Session " + sessionId +
+ " not found");
+ return "";
}
+ return new Date(session.getCreationTime()).toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getDuplicates()
+ {
+ return duplicates_.get();
+ }
- long begin = System.currentTimeMillis();
- boolean mustAdd = false;
- JBossCacheClusteredSession session = (JBossCacheClusteredSession) sessions_.get(realId);
-
- if (session == null)
- {
- // This is either the first time we've seen this session on this
- // server, or we previously expired it and have since gotten
- // a replication message from another server
- mustAdd = true;
- session = createEmptyClusteredSession();
+ /**
+ * {@inheritDoc}
+ */
+ public String getLastAccessedTime(String sessionId)
+ {
+ Session session = findSession(sessionId);
+ if(session == null) {
+ log_.info("getLastAccessedTime(): Session " + sessionId +
+ " not found");
+ return "";
}
+ return new Date(session.getLastAccessedTime()).toString();
+ }
- synchronized (session)
+ /**
+ * {@inheritDoc}
+ */
+ public String getSessionAttribute(String sessionId, String key)
+ {
+ Object attr = null;
+ ClusteredSession session = (ClusteredSession) findSession(sessionId);
+ if (session != null)
{
- boolean doTx = false;
- try
- {
- // We need transaction so any data gravitation replication
- // is sent in batch.
- // Don't do anything if there is already transaction context
- // associated with this thread.
- if(tm.getTransaction() == null)
- doTx = true;
+ attr = session.getAttribute(key);
+ }
+
+ return attr == null ? null : attr.toString();
+ }
- if(doTx)
- tm.begin();
-
- // Ignore cache notifications we may generate for this
- // session if data gravitation occurs.
- SessionReplicationContext.startCacheActivity();
-
- session = proxy_.loadSession(realId, session);
-
- if (session != null)
- {
- session.initAfterLoad(this);
- }
- }
- catch (Exception ex)
- {
- try
- {
-// if(doTx)
- // Let's set it no matter what.
- tm.setRollbackOnly();
- }
- catch (Exception exn)
- {
- log_.error("Caught exception rolling back transaction", exn);
- }
- // We will need to alert Tomcat of this exception.
- if (ex instanceof RuntimeException)
- throw (RuntimeException) ex;
-
- throw new RuntimeException("loadSession(): failed to load session " +
- realId, ex);
- }
- finally
- {
- try {
- if(doTx)
- endTransaction(realId);
- }
- finally {
- SessionReplicationContext.finishCacheActivity();
- }
- }
+ /**
+ * {@inheritDoc}
+ */
+ public long getMaxPassivatedSessionCount()
+ {
+ return maxPassivatedCount_.get();
+ }
- if (session != null)
- {
- if (mustAdd)
- add(session, false); // don't replicate
- long elapsed = System.currentTimeMillis() - begin;
- stats_.updateLoadStats(realId, elapsed);
+ /**
+ * {@inheritDoc}
+ */
+ public int getMaxUnreplicatedInterval()
+ {
+ return maxUnreplicatedInterval_;
+ }
- if (log_.isTraceEnabled())
- {
- log_.trace("loadSession(): id= " + realId + ", session=" + session);
- }
- }
- else if (log_.isTraceEnabled())
- {
- log_.trace("loadSession(): session " + realId +
- " not found in distributed cache");
- }
- }
+ /**
+ * {@inheritDoc}
+ */
+ public void setMaxUnreplicatedInterval(int maxUnreplicatedInterval)
+ {
+ this.maxUnreplicatedInterval_ = maxUnreplicatedInterval;
+ }
- return session;
+ /**
+ * {@inheritDoc}
+ */
+ public long getPassivatedSessionCount()
+ {
+ return passivatedCount_.get();
}
/**
- * Places the current session contents in the distributed cache and
- * replicates them to the cluster
- *
- * @param session the session. Cannot be <code>null</code>.
+ * {@inheritDoc}
*/
- protected void processSessionRepl(ClusteredSession session)
+ public long getPassivationMaxIdleTime()
{
- // If we are using SESSION granularity, we don't want to initiate a TX
- // for a single put
- boolean notSession = (replicationGranularity_ != ReplicationGranularity.SESSION);
- boolean doTx = false;
- try
- {
- // We need transaction so all the replication are sent in batch.
- // Don't do anything if there is already transaction context
- // associated with this thread.
- if(notSession && tm.getTransaction() == null)
- doTx = true;
+ return passivationMaxIdleTime_;
+ }
- if(doTx)
- tm.begin();
+ /**
+ * {@inheritDoc}
+ */
+ public long getPassivationMinIdleTime()
+ {
+ return passivationMinIdleTime_;
+ }
- // Tell the proxy to ignore cache notifications we are about
- // to generate for this session. We have to do this
- // at this level because we don't want to resume handling
- // notifications until any compensating changes resulting
- // from a tx rollback are done.
- SessionReplicationContext.startCacheActivity();
+ /**
+ * {@inheritDoc}
+ */
+ public ReplicationGranularity getReplicationGranularity()
+ {
+ return replicationGranularity_;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ReplicationTrigger getReplicationTrigger()
+ {
+ return this.replicationTrigger_;
+ }
- session.processSessionRepl();
+ /**
+ * {@inheritDoc}
+ */
+ public int getSnapshotInterval()
+ {
+ return snapshotInterval_;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public SnapshotMode getSnapshotMode()
+ {
+ return snapshotMode_;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean getUseJK()
+ {
+ return useJK_ == null ? false : useJK_.booleanValue();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isPassivationEnabled()
+ {
+ return (passivationMode_ && proxy_.isCachePassivationEnabled());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Boolean isReplicationFieldBatchMode()
+ {
+ return replicationFieldBatchMode_;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String listLocalSessionIds()
+ {
+ return reportSessionIds(sessions_.keySet());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String listSessionIds()
+ {
+ Set ids = new HashSet(sessions_.keySet());
+ ids.addAll(unloadedSessions_.keySet());
+ return reportSessionIds(ids);
+ }
+
+ // -------------------------------- Callbacks from Distributed Caching Layer
+
+ /**
+ * Notifies the manager that a session in the distributed cache has
+ * been invalidated
+ *
+ * @param realId the session id excluding any jvmRoute
+ */
+ public void notifyRemoteInvalidation(String realId)
+ {
+ // Remove the session from our local map
+ ClusteredSession session = (ClusteredSession) sessions_.remove(realId);
+ if (session == null)
+ {
+ // We weren't managing the session anyway. But remove it
+ // from the list of cached sessions we haven't loaded
+ if (unloadedSessions_.remove(realId) != null)
+ {
+ if (trace_)
+ log_.trace("Removed entry for session " + realId + " from unloaded session map");
+ }
}
- catch (Exception ex)
+ else
{
- log_.debug("processSessionRepl(): failed with exception", ex);
+ // Expire the session
+ // DON'T SYNCHRONIZE ON SESSION HERE -- isValid() and
+ // expire() are meant to be multi-threaded and synchronize
+ // properly internally; synchronizing externally can lead
+ // to deadlocks!!
+ boolean notify = false; // Don't notify listeners. SRV.10.7
+ // allows this, and sending notifications
+ // leads to all sorts of issues; e.g.
+ // circular calls with ClusteredSSO
+ boolean localCall = false; // this call originated from the cache;
+ // we have already removed session
+ boolean localOnly = true; // Don't pass attr removals to cache
+ // Ensure the correct TCL is in place
+ ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
try
{
- //if(doTx)
- // Let's setRollbackOnly no matter what.
- // (except if there's no tx due to SESSION (JBAS-3840))
- if (notSession)
- tm.setRollbackOnly();
+ Thread.currentThread().setContextClassLoader(tcl_);
+ session.expire(notify, localCall, localOnly);
}
- catch (Exception exn)
+ finally
{
- log_.error("Caught exception rolling back transaction", exn);
+ Thread.currentThread().setContextClassLoader(prevTcl);
}
-
- // We will need to alert Tomcat of this exception.
- if (ex instanceof RuntimeException)
- throw (RuntimeException) ex;
-
- throw new RuntimeException("JBossCacheManager.processSessionRepl(): " +
- "failed to replicate session.", ex);
+
+ // Remove any stats for this session
+ stats_.removeStats(realId);
}
- finally
+ }
+
+ /**
+ * Callback from the distributed cache notifying of a local modification
+ * to a session's attributes. Meant for use with FIELD granularity,
+ * where the session may not be aware of modifications.
+ *
+ * @param realId the session id excluding any jvmRoute
+ */
+ public void notifyLocalAttributeModification(String realId)
+ {
+ ClusteredSession session = (ClusteredSession) sessions_.get(realId);
+ if (session != null)
{
- try {
- if(doTx)
- endTransaction(session.getId());
- }
- finally {
- SessionReplicationContext.finishCacheActivity();
- }
+ session.sessionAttributesDirty();
}
+ else
+ {
+ log_.warn("");
+ }
}
-
- protected void endTransaction(String id)
+
+ public void sessionActivated()
{
- if (tm == null)
+ int pc = passivatedCount_.decrementAndGet();
+ // Correct for drift since we don't know the true passivation
+ // count when we started. We can get activations of sessions
+ // we didn't know were passivated.
+ // FIXME -- is the above statement still correct? Is this needed?
+ if (pc < 0)
{
- log_.warn("JBossCacheManager.endTransaction(): tm is null for id: " +id);
- return;
+ // Just reverse our decrement.
+ passivatedCount_.incrementAndGet();
}
-
- try
+ }
+
+ /**
+ * Callback from the distributed cache to notify us that a session
+ * has been modified remotely.
+ *
+ * @param realId the session id, without any trailing jvmRoute
+ * @param dataOwner the owner of the session. Can be <code>null</code> if
+ * the owner is unknown.
+ * @param distributedVersion the session's version per the distributed cache
+ * @param timestamp the session's timestamp per the distributed cache
+ * @param metadata the session's metadata per the distributed cache
+ */
+ public boolean sessionChangedInDistributedCache(String realId,
+ String dataOwner,
+ int distributedVersion,
+ SessionTimestamp timestamp,
+ SessionMetadata metadata)
+ {
+ boolean updated = true;
+
+ ClusteredSession session = findLocalSession(realId);
+ if (session != null)
{
- if(tm.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
+ // Need to invalidate the loaded session. We get back whether
+ // this an actual version increment
+ updated = session.setVersionFromDistributedCache(distributedVersion);
+ if (updated && trace_)
+ {
+ log_.trace("session in-memory data is invalidated for id: " + realId +
+ " new version: " + distributedVersion);
+ }
+ }
+ else
+ {
+ long lastMod = timestamp == null ? System.currentTimeMillis() : timestamp.timestamp;
+ int maxLife = metadata == null ? getMaxInactiveInterval() : metadata.maxInactiveInterval;
+
+ Object existing = unloadedSessions_.put(realId, new OwnedSessionUpdate(dataOwner, lastMod, maxLife, false));
+ if (existing == null)
{
- tm.commit();
+ calcActiveSessions();
+ if (trace_)
+ {
+ log_.trace("New session " + realId + " added to unloaded session map");
+ }
}
- else
+ else if (trace_)
{
- log_.info("JBossCacheManager.endTransaction(): rolling back tx for id: " +id);
- tm.rollback();
+ log_.trace("Updated timestamp for unloaded session " + realId);
}
}
- catch (RollbackException re)
- {
- // Do nothing here since cache may rollback automatically.
- log_.warn("JBossCacheManager.endTransaction(): rolling back transaction with exception: " +re);
- }
- catch (Exception e)
- {
- throw new RuntimeException("JBossCacheManager.endTransaction(): Exception for id: " +id, e);
- }
+
+ return updated;
}
+ // ----------------------------------------------- JBossCacheCluster Support
+
/**
- * Gets the classloader of the webapp we are managing.
+ * Sets how often session changes should be replicated to other nodes.
+ *
+ * @param snapshotInterval the number of milliseconds between
+ * session replications.
*/
- protected ClassLoader getWebappClassLoader()
+ public void setSnapshotInterval(int snapshotInterval)
{
- return tcl_;
+ this.snapshotInterval_ = snapshotInterval;
}
/**
- * Goes through all sessions and look if they have expired.
- * Note this overrides the method in JBossManager.
+ * Sets when sessions are replicated to the other nodes. Valid values are:
+ * <ul>
+ * <li>instant</li>
+ * <li>interval</li>
+ * </ul>
*/
+ public void setSnapshotMode(SnapshotMode snapshotMode)
+ {
+ this.snapshotMode_ = snapshotMode;
+ }
+
+ /**
+ * Sets whether the <code>Engine</code> in which we are running
+ * uses <code>mod_jk</code>.
+ */
+ public void setUseJK(boolean useJK)
+ {
+ this.useJK_ = Boolean.valueOf(useJK);
+ }
+
+ /**
+ * Sets the granularity of session data replicated across the cluster.
+ * Valid values are:
+ * <ul>
+ * <li>SESSION</li>
+ * <li>ATTRIBUTE</li>
+ * <li>FIELD</li>
+ * </ul>
+ */
+ public void setReplicationGranularity(ReplicationGranularity granularity)
+ {
+ this.replicationGranularity_ = granularity;
+ }
+
+ /**
+ * Returns the replication granularity.
+ *
+ * @see JBossWebMetaData#REPLICATION_GRANULARITY_ATTRIBUTE
+ * @see JBossWebMetaData#REPLICATION_GRANULARITY_FIELD
+ * @see JBossWebMetaData#REPLICATION_GRANULARITY_SESSION
+ */
+ public String getReplicationGranularityString()
+ {
+ return replicationGranularity_ == null ? null : replicationGranularity_.toString();
+ }
+
+ /**
+ * Sets the granularity of session data replicated across the cluster.
+ * Valid values are:
+ * <ul>
+ * <li>SESSION</li>
+ * <li>ATTRIBUTE</li>
+ * <li>FIELD</li>
+ * </ul>
+ */
+ public void setReplicationGranularityString(String granularity)
+ {
+ setReplicationGranularity(granularity == null ? null :
+ ReplicationGranularity.fromString(granularity.toUpperCase()));
+ }
+
+ /**
+ * Sets the type of operations on a <code>HttpSession</code> that
+ * trigger replication. Valid values are:
+ * <ul>
+ * <li>SET_AND_GET</li>
+ * <li>SET_AND_NON_PRIMITIVE_GET</li>
+ * <li>SET</li>
+ * </ul>
+ */
+ public void setReplicationTrigger(ReplicationTrigger trigger)
+ {
+ this.replicationTrigger_ = trigger;
+ }
+
+ public String getReplicationTriggerString()
+ {
+ return replicationTrigger_ == null ? null : replicationTrigger_.toString();
+ }
+
+ public void setReplicationTriggerString(String trigger)
+ {
+ setReplicationTrigger(trigger == null ? null : ReplicationTrigger.fromString(trigger.toUpperCase()));
+ }
+
+ /**
+ * Sets whether, if replication granularity is set to <code>FIELD</code>,
+ * replication should be done in batch mode. Ignored if field-level
+ * granularity is not used.
+ */
+ public void setReplicationFieldBatchMode(boolean replicationFieldBatchMode)
+ {
+ this.replicationFieldBatchMode_ = Boolean.valueOf(replicationFieldBatchMode);
+ }
+
+ // ------------------------------------------------------------- Properties
+
+ public PojoCache getPojoCache()
+ {
+ return pojoCache_;
+ }
+
+ public Cache getPlainCache()
+ {
+ return plainCache_;
+ }
+
+ /**
+ * Gets the <code>FieldBasedJBossCacheService</code> through which we interact
+ * with the PojoCache.
+ *
+ * @throws IllegalStateException if we are not using field based replication
+ */
+ public FieldBasedJBossCacheService getPojoCacheService()
+ {
+ if (proxy_ != null && !(proxy_ instanceof FieldBasedJBossCacheService))
+ throw new IllegalStateException("PojoCache not being used");
+ return (FieldBasedJBossCacheService) proxy_;
+ }
+
+ /**
+ * Gets the <code>JBossCacheService</code> through which we interact
+ * with the <code>Cache</code>.
+ */
+ public JBossCacheService getCacheService()
+ {
+ return proxy_;
+ }
+
+ // --------------------------------------------------------------- Overrides
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Overrides the superclass version to ensure that the generated id
+ * does not duplicate the id of any other session this manager is aware of.
+ * </p>
+ */
@Override
- protected void processExpires()
+ protected String getNextId()
+ {
+ while (true)
+ {
+ String id = super.getNextId();
+ if (sessions_.containsKey(id) || unloadedSessions_.containsKey(id))
+ {
+ duplicates_.incrementAndGet();
+ }
+ else
+ {
+ return id;
+ }
+ }
+ }
+
+ protected int getTotalActiveSessions()
+ {
+ return localActiveCounter_.get() + unloadedSessions_.size() - passivatedCount_.get();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processExpirationPassivation()
{
boolean expire = maxInactiveInterval_ >= 0;
boolean passivate = isPassivationEnabled();
@@ -1395,15 +1220,14 @@
long passivationMax = passivationMaxIdleTime_ * 1000L;
long passivationMin = passivationMinIdleTime_ * 1000L;
- boolean trace = log_.isTraceEnabled();
- if (trace)
+ if (trace_)
{
- log_.trace("processExpires(): Looking for sessions that have expired ...");
- log_.trace("processExpires(): active sessions = " + calcActiveSessions());
- log_.trace("processExpires(): expired sessions = " + expiredCounter_);
+ log_.trace("processExpirationPassivation(): Looking for sessions that have expired ...");
+ log_.trace("processExpirationPassivation(): active sessions = " + calcActiveSessions());
+ log_.trace("processExpirationPassivation(): expired sessions = " + expiredCounter_);
if (passivate)
{
- log_.trace("processExpires(): passivated count = " + getPassivatedSessionCount());
+ log_.trace("processExpirationPassivation(): passivated count = " + getPassivatedSessionCount());
}
}
@@ -1418,7 +1242,7 @@
ClusteredSession session = (ClusteredSession) sessions[i];
if(session == null)
{
- log_.warn("processExpires(): processing null session at index " +i);
+ log_.warn("processExpirationPassivation(): processing null session at index " +i);
continue;
}
@@ -1443,7 +1267,6 @@
// to deadlocks!!
if (!session.isValid()) continue;
}
- // JBCLUSTER-15
// we now have a valid session; see if we need to passivate it
if (passivate)
@@ -1460,9 +1283,9 @@
// If the session didn't exceed the passivationMaxIdleTime_, see
// if the number of sessions managed by this manager greater than the max allowed
// active sessions, passivate the session if it exceed passivationMinIdleTime_
- else if (maxActive_ > 0
+ else if (maxActiveAllowed_ > 0
&& passivationMin > 0
- && calcActiveSessions() >= maxActive_
+ && calcActiveSessions() >= maxActiveAllowed_
&& timeIdle > passivationMin)
{
processSessionPassivation(session.getRealId());
@@ -1472,31 +1295,34 @@
}
catch (Exception ex)
{
- log_.error("processExpires(): failed expiring " +
+ log_.error("processExpirationPassivation(): failed handling " +
sessions[i].getIdInternal() + " with exception: " +
ex, ex);
}
}
- // Next, handle any unloaded sessions that are stale
+ // Next, handle any unloaded sessions
- long now = System.currentTimeMillis();
- Map unloaded = new HashMap(unloadedSessions_);
- Set entries = unloaded.entrySet();
+
// We may have not gotten replication of a timestamp for requests
// that occurred w/in maxUnreplicatedInterval_ of the previous
// request. So we add a grace period to avoid flushing a session early
// and permanently losing part of its node structure in JBoss Cache.
long maxUnrep = maxUnreplicatedInterval_ < 0 ? 60 : maxUnreplicatedInterval_;
- for (Iterator it = entries.iterator(); it.hasNext(); )
+
+ Map unloaded = new HashMap(unloadedSessions_);
+ for (Iterator<Map.Entry<String, OwnedSessionUpdate>> it = unloaded.entrySet().iterator(); it.hasNext(); )
{
- Map.Entry entry = (Map.Entry) it.next();
- String realId = (String) entry.getKey();
- OwnedSessionUpdate osu = (OwnedSessionUpdate) entry.getValue();
+ Map.Entry<String, OwnedSessionUpdate> entry = it.next();
+ String realId = entry.getKey();
+ OwnedSessionUpdate osu = entry.getValue();
+
// Ignore marker entries for our own passivated sessions
// Also skip if the session isn't configured to expire
if (osu.passivated || osu.maxInactive < 1)
continue;
+
+ long now = System.currentTimeMillis();
long elapsed = (now - osu.updateTime);
try
{
@@ -1514,163 +1340,263 @@
{
processUnloadedSessionPassivation(realId, osu);
}
- // If the session didn't exceed the passivationMaxIdleTime_, See
- // if the number of sessions managed by this manager greater than the max allowed
- // active sessions, passivate the session if it exceed passivationMinIdleTime_
- else if (maxActive_ > 0
+ // If the session didn't exceed the passivationMaxIdleTime_, see
+ // if the number of sessions managed by this manager is greater than the max allowed
+ // active sessions; passivate the session if it exceed passivationMinIdleTime_
+ else if (maxActiveAllowed_ > 0
&& passivationMin >= 0
- && calcActiveSessions() >= maxActive_
- && elapsed >= passivationMin)
+ && elapsed >= passivationMin
+ && calcActiveSessions() >= maxActiveAllowed_)
{
processUnloadedSessionPassivation(realId, osu);
}
}
- }
- // JBClUSTER-15
- // we don't need to worry about session passivation here, since the
- // method processSessionPassivation() takes care of unloadedSessions_ map
- // when it receives a notification of passivation event happened in the
- // distributed store from the CacheListener
+ }
catch (Exception ex)
{
- log_.error("processExpires(): failed removing unloaded session " +
+ log_.error("processExpirationPassivation(): failed handling unloaded session " +
realId, ex);
}
}
}
catch (Exception ex)
{
- log_.error("processExpires: failed with exception: " + ex, ex);
+ log_.error("processExpirationPassivation(): failed with exception: " + ex, ex);
}
- if (trace)
+ if (trace_)
{
- log_.trace("processExpires(): Completed ...");
- log_.trace("processExpires(): active sessions = " + calcActiveSessions());
- log_.trace("processExpires(): expired sessions = " + expiredCounter_);
+ log_.trace("processExpirationPassivation(): Completed ...");
+ log_.trace("processExpirationPassivation(): active sessions = " + calcActiveSessions());
+ log_.trace("processExpirationPassivation(): expired sessions = " + expiredCounter_);
if (passivate)
{
- log_.trace("processExpires(): passivated count = " + getPassivatedSessionCount());
+ log_.trace("processExpirationPassivation(): passivated count = " + getPassivatedSessionCount());
}
}
}
+
+ @Override
+ public void resetStats()
+ {
+ super.resetStats();
+
+ this.maxPassivatedCount_.set(this.passivatedCount_.get());
+ }
+
+
+
+ // --------------------------------------------------------------- Protected
/**
- * Notifies the manager that a session in the distributed cache has
- * been invalidated
+ * Gets the classloader of the webapp we are managing.
*
- * @param realId the session id excluding any jvmRoute
+ * FIXME try and remove the need for this
*/
- public void notifyRemoteInvalidation(String realId)
+ protected ClassLoader getWebappClassLoader()
{
- // Remove the session from our local map
- ClusteredSession session = (ClusteredSession) sessions_.remove(realId);
- if (session == null)
+ return tcl_;
+ }
+
+ // ------------------------------------------------------ Session Management
+
+ private JBossCacheClusteredSession createEmptyClusteredSession()
+ {
+
+ JBossCacheClusteredSession session = null;
+ switch (replicationGranularity_)
{
- // We weren't managing the session anyway. But remove it
- // from the list of cached sessions we haven't loaded
- if (unloadedSessions_.remove(realId) != null)
- {
- if (log_.isTraceEnabled())
- log_.trace("Removed entry for session " + realId + " from unloaded session map");
- }
+ case ATTRIBUTE:
+ session = new AttributeBasedClusteredSession(this);
+ break;
+ case FIELD:
+ session = new FieldBasedClusteredSession(this);
+ break;
+ default:
+ session = new SessionBasedClusteredSession(this);
+ break;
}
- else
+ return session;
+ }
+
+ /**
+ * Loads a session from the distributed store. If an existing session with
+ * the id is already under local management, that session's internal state
+ * will be updated from the distributed store. Otherwise a new session
+ * will be created and added to the collection of those sessions under
+ * local management.
+ *
+ * @param realId id of the session-id with any jvmRoute removed
+ *
+ * @return the session or <code>null</code> if the session cannot be found
+ * in the distributed store
+ */
+ private ClusteredSession loadSession(String realId)
+ {
+ if (realId == null)
{
- // Expire the session
- // DON'T SYNCHRONIZE ON SESSION HERE -- isValid() and
- // expire() are meant to be multi-threaded and synchronize
- // properly internally; synchronizing externally can lead
- // to deadlocks!!
- boolean notify = false; // Don't notify listeners. SRV.10.7
- // allows this, and sending notifications
- // leads to all sorts of issues; e.g.
- // circular calls with ClusteredSSO
- boolean localCall = false; // this call originated from the cache;
- // we have already removed session
- boolean localOnly = true; // Don't pass attr removals to cache
-
- // Ensure the correct TCL is in place
- ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
+ return null;
+ }
+
+ long begin = System.currentTimeMillis();
+ boolean mustAdd = false;
+ JBossCacheClusteredSession session = (JBossCacheClusteredSession) sessions_.get(realId);
+
+ if (session == null)
+ {
+ // This is either the first time we've seen this session on this
+ // server, or we previously expired it and have since gotten
+ // a replication message from another server
+ mustAdd = true;
+ session = createEmptyClusteredSession();
+ }
+
+ synchronized (session)
+ {
+ boolean doTx = false;
try
{
- Thread.currentThread().setContextClassLoader(tcl_);
- session.expire(notify, localCall, localOnly);
+ // We need transaction so any data gravitation replication
+ // is sent in batch.
+ // Don't do anything if there is already transaction context
+ // associated with this thread.
+ if(tm.getTransaction() == null)
+ doTx = true;
+
+ if(doTx)
+ tm.begin();
+
+ // Ignore cache notifications we may generate for this
+ // session if data gravitation occurs.
+ SessionReplicationContext.startCacheActivity();
+
+ session = proxy_.loadSession(realId, session);
+
+ if (session != null)
+ {
+ session.initAfterLoad(this);
+ }
}
+ catch (Exception ex)
+ {
+ try
+ {
+// if(doTx)
+ // Let's set it no matter what.
+ tm.setRollbackOnly();
+ }
+ catch (Exception exn)
+ {
+ log_.error("Caught exception rolling back transaction", exn);
+ }
+ // We will need to alert Tomcat of this exception.
+ if (ex instanceof RuntimeException)
+ throw (RuntimeException) ex;
+
+ throw new RuntimeException("loadSession(): failed to load session " +
+ realId, ex);
+ }
finally
{
- Thread.currentThread().setContextClassLoader(prevTcl);
+ try {
+ if(doTx)
+ endTransaction(realId);
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
}
- // Remove any stats for this session
- stats_.removeStats(realId);
+ if (session != null)
+ {
+ if (mustAdd)
+ add(session, false); // don't replicate
+ long elapsed = System.currentTimeMillis() - begin;
+ stats_.updateLoadStats(realId, elapsed);
+
+ if (trace_)
+ {
+ log_.trace("loadSession(): id= " + realId + ", session=" + session);
+ }
+ }
+ else if (trace_)
+ {
+ log_.trace("loadSession(): session " + realId +
+ " not found in distributed cache");
+ }
}
+
+ return session;
}
-
+
/**
- * Callback from the distributed cache notifying of a local modification
- * to a session's attributes. Meant for use with FIELD granularity,
- * where the session may not be aware of modifications.
- *
- * @param realId the session id excluding any jvmRoute
+ * Places the current session contents in the distributed cache and
+ * replicates them to the cluster
+ *
+ * @param session the session. Cannot be <code>null</code>.
*/
- public void notifyLocalAttributeModification(String realId)
+ private void processSessionRepl(ClusteredSession session)
{
- ClusteredSession session = (ClusteredSession) sessions_.get(realId);
- if (session != null)
+ // If we are using SESSION granularity, we don't want to initiate a TX
+ // for a single put
+ boolean notSession = (replicationGranularity_ != ReplicationGranularity.SESSION);
+ boolean doTx = false;
+ try
{
- session.sessionAttributesDirty();
+ // We need transaction so all the replication are sent in batch.
+ // Don't do anything if there is already transaction context
+ // associated with this thread.
+ if(notSession && tm.getTransaction() == null)
+ doTx = true;
+
+ if(doTx)
+ tm.begin();
+
+ // Tell the proxy to ignore cache notifications we are about
+ // to generate for this session. We have to do this
+ // at this level because we don't want to resume handling
+ // notifications until any compensating changes resulting
+ // from a tx rollback are done.
+ SessionReplicationContext.startCacheActivity();
+
+ session.processSessionRepl();
}
- else
+ catch (Exception ex)
{
- log_.warn("");
+ log_.debug("processSessionRepl(): failed with exception", ex);
+
+ try
+ {
+ //if(doTx)
+ // Let's setRollbackOnly no matter what.
+ // (except if there's no tx due to SESSION (JBAS-3840))
+ if (notSession)
+ tm.setRollbackOnly();
+ }
+ catch (Exception exn)
+ {
+ log_.error("Caught exception rolling back transaction", exn);
+ }
+
+ // We will need to alert Tomcat of this exception.
+ if (ex instanceof RuntimeException)
+ throw (RuntimeException) ex;
+
+ throw new RuntimeException("JBossCacheManager.processSessionRepl(): " +
+ "failed to replicate session.", ex);
}
- }
-
- private void sessionPassivated()
- {
- int pc = passivatedCount_.incrementAndGet();
- if (pc > maxPassivatedCount_)
- maxPassivatedCount_ = pc;
- }
-
- public void sessionActivated()
- {
- int pc = passivatedCount_.decrementAndGet();
- // Correct for drift since we don't know the true passivation
- // count when we started. We can get activations of sessions
- // we didn't know were passivated.
- // FIXME -- is the above statement still correct? Is this needed?
- if (pc < 0)
+ finally
{
- // Just reverse our decrement.
- passivatedCount_.incrementAndGet();
+ try {
+ if(doTx)
+ endTransaction(session.getId());
+ }
+ finally {
+ SessionReplicationContext.finishCacheActivity();
+ }
}
}
- /**
- * Calculates the number of active sessions, and updates
- * the max # of local active sessions and max # of sessions.
- * <p>
- * Call this method when a new session is added or when an
- * accurate count of active sessions is needed.
- * </p>
- *
- * @return the size of the sessions map + the size of the unloaded sessions
- * map - the count of passivated sessions
- */
- private int calcActiveSessions()
- {
- activeCounter_ = sessions_.size();
- if (activeCounter_ > maxLocalActiveCounter_)
- maxLocalActiveCounter_ = activeCounter_;
-
- int count = activeCounter_ + unloadedSessions_.size() - passivatedCount_.get();
- if (count > maxActiveCounter_)
- maxActiveCounter_ = count;
- return count;
- }
-
/**
* Session passivation logic for an actively managed session.
*
@@ -1686,7 +1612,7 @@
{
synchronized (session)
{
- if (log_.isTraceEnabled())
+ if (trace_)
{
log_.trace("Passivating session with id: " + realId);
}
@@ -1707,7 +1633,7 @@
// expose the session to regular invalidation.
Object obj = unloadedSessions_.put(realId,
new OwnedSessionUpdate(null, session.getLastAccessedTime(), session.getMaxInactiveInterval(), true));
- if (log_.isTraceEnabled())
+ if (trace_)
{
if (obj == null)
{
@@ -1719,11 +1645,9 @@
}
}
sessions_.remove(realId);
-// stats_.removeStats(realId);
}
-// activeCounter_--;
}
- else if (log_.isTraceEnabled())
+ else if (trace_)
{
log_.trace("processSessionPassivation(): could not find session " + realId);
}
@@ -1736,7 +1660,7 @@
*/
private void processUnloadedSessionPassivation(String realId, OwnedSessionUpdate osu)
{
- if (log_.isTraceEnabled())
+ if (trace_)
{
log_.trace("Passivating session with id: " + realId);
}
@@ -1754,112 +1678,20 @@
}
}
-
- /**
- * Gets the session id with any jvmRoute removed.
- *
- * @param id a session id with or without an appended jvmRoute.
- * Cannot be <code>null</code>.
- */
- protected String getRealId(String id)
- {
- return (getUseJK() ? Util.getRealId(id) : id);
- }
- /**
- * Callback from the CacheListener to notify us that a session
- * we haven't loaded has been changed.
- *
- * @param realId the session id, without any trailing jvmRoute
- * @param dataOwner the owner of the session. Can be <code>null</code> if
- * the owner is unknown.
- */
- protected void unloadedSessionChanged(String realId,
- String dataOwner,
- SessionTimestamp timestamp,
- SessionMetadata metadata)
+ private void sessionPassivated()
{
- long lastMod = timestamp == null ? System.currentTimeMillis() : timestamp.timestamp;
- int maxLife = metadata == null ? getMaxInactiveInterval() : metadata.maxInactiveInterval;
- Object obj = unloadedSessions_.put(realId,
- new OwnedSessionUpdate(dataOwner, lastMod, maxLife, false));
- if (log_.isTraceEnabled())
+ int pc = passivatedCount_.incrementAndGet();
+ int max = maxPassivatedCount_.get();
+ while (pc > max)
{
- if (obj == null)
+ if (!maxPassivatedCount_ .compareAndSet(max, pc))
{
- log_.trace("New session " + realId + " added to unloaded session map");
- calcActiveSessions();
+ max = maxPassivatedCount_.get();
}
- else
- {
- log_.trace("Updated timestamp for unloaded session " + realId);
- }
}
}
- /**
- * Returns true if the passivation mode is set to true in jboss-web.xml and JBoss Cache passivation
- * has been enabled with proper configured cache loader. Otherwise, it returns false
- *
- * @return
- */
- public boolean isPassivationEnabled()
- {
- return (passivationMode_ && proxy_.isCachePassivationEnabled());
- }
-
- // ------------------------------------------------------ Lifecyle Embedded
-
- /**
- * Start this Manager when running embedded in JBoss AS.
- *
- * @throws org.apache.catalina.LifecycleException
- */
- private void startEmbedded() throws LifecycleException
- {
- super.start();
-
- // Start the JBossCacheService
- // Will need to pass the classloader that is associated with this
- // web app so de-serialization will work correctly.
- tcl_ = super.getContainer().getLoader().getClassLoader();
-
- try
- {
- if (proxy_ == null) // Could happen during a restart
- {
- initCacheProxy();
- }
-
- proxy_.start(tcl_, this);
-
- tm = proxy_.getTransactionManager();
- if(tm == null)
- {
- throw new LifecycleException("JBossCacheManager.start(): Obtain null tm");
- }
-
- initializeUnloadedSessions();
-
- // Setup our SnapshotManager
- initSnapshotManager();
-
- // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
- installValves();
-
- log_.debug("start(): JBossCacheService started");
- }
- catch (LifecycleException le)
- {
- throw le;
- }
- catch (Exception e)
- {
- log_.error("Unable to start manager.", e);
- throw new LifecycleException(e);
- }
- }
-
// ----------------------------------------------- Lifecyle When Unembedded
/**
@@ -1943,88 +1775,58 @@
throw new LifecycleException(e);
}
- try
- {
- registerMBeans();
- }
- catch (Exception e)
- {
- log_.error("Could not register ManagerMBean with MBeanServer", e);
- }
+ registerManagerMBean();
}
/**
- * Register this Manager with JMX.
+ * Accesses the underlying cache and creates the proxy
+ *
+ * @throws ClusteringNotSupportedException
*/
- private void registerMBeans()
+ private void initCacheProxy() throws ClusteringNotSupportedException
{
+ // We are likely going to cause creation and start of a cache here;
+ // we don't want to leak the TCCL to cache/jgroups threads,
+ // so we switch it to our classloader
+ ContextClassLoaderSwitcher switcher = (ContextClassLoaderSwitcher) AccessController.doPrivileged(ContextClassLoaderSwitcher.INSTANTIATOR);
+ ContextClassLoaderSwitcher.SwitchContext switchContext = switcher.getSwitchContext(getClass().getClassLoader());
try
{
- MBeanServer server = getMBeanServer();
-
- String domain;
- if (container_ instanceof ContainerBase)
+ if (ReplicationGranularity.FIELD == replicationGranularity_)
{
- domain = ((ContainerBase) container_).getDomain();
+ PojoCache pojoC = getPojoCache();
+ if (pojoC == null)
+ {
+ pojoC = Util.findPojoCache(cacheConfigName_);
+
+ cacheFromCacheManager_ = true;
+
+ if (pojoC.getCache().getCacheStatus() != CacheStatus.STARTED)
+ pojoC.getCache().start();
+ }
+ proxy_ = new FieldBasedJBossCacheService(pojoC);
}
else
{
- domain = server.getDefaultDomain();
+ Cache pc = getPlainCache();
+ if (pc == null)
+ {
+ pc = Util.findPlainCache(cacheConfigName_);
+
+ cacheFromCacheManager_ = true;
+
+ if (pc.getCacheStatus() != CacheStatus.STARTED)
+ pc.start();
+ }
+ proxy_ = new JBossCacheService(pc);
}
- String hostName = ((Host) container_.getParent()).getName();
- hostName = (hostName == null) ? "localhost" : hostName;
- ObjectName clusterName = new ObjectName(domain
- + ":service=ClusterManager,WebModule=//" + hostName
- + ((Context) container_).getPath());
-
- if (server.isRegistered(clusterName))
- {
- log_.warn("MBean " + clusterName + " already registered");
- return;
- }
-
- objectName_ = clusterName;
- server.registerMBean(this, clusterName);
-
}
- catch (Exception ex)
+ finally
{
- log_.error(ex.getMessage(), ex);
+ // Restore the TCCL
+ switchContext.reset();
}
}
-
- /**
- * Unregister this Manager from the JMX server.
- */
- private void unregisterMBeans()
- {
- if (mserver_ != null)
- {
- try
- {
- mserver_.unregisterMBean(objectName_);
- }
- catch (Exception e)
- {
- log_.error(e);
- }
- }
- }
-
- /**
- * Get the current MBean Server.
- *
- * @return
- * @throws Exception
- */
- private MBeanServer getMBeanServer() throws Exception
- {
- if (mserver_ == null)
- {
- mserver_ = MBeanServerLocator.locateJBoss();
- }
- return (mserver_);
- }
/**
* Gets the ids of all sessions in the distributed cache and adds
@@ -2076,20 +1878,26 @@
if (passivationMax >= 0
&& elapsed > passivationMax)
{
- if (log_.isTraceEnabled())
- log_.trace("Elapsed time of " + elapsed + " for session "+ realId + " exceeds max of " + passivationMax + "; passivating");
+ if (trace_)
+ {
+ log_.trace("Elapsed time of " + elapsed + " for session "+
+ realId + " exceeds max of " + passivationMax + "; passivating");
+ }
processUnloadedSessionPassivation(realId, osu);
}
// If the session didn't exceed the passivationMaxIdleTime_, see
// if the number of sessions managed by this manager greater than the max allowed
// active sessions, passivate the session if it exceed passivationMinIdleTime_
- else if (maxActive_ > 0
+ else if (maxActiveAllowed_ > 0
&& passivationMin >= 0
- && calcActiveSessions() > maxActive_
+ && calcActiveSessions() > maxActiveAllowed_
&& elapsed >= passivationMin)
{
- if (log_.isTraceEnabled())
- log_.trace("Elapsed time of " + elapsed + " for session "+ realId + " exceeds min of " + passivationMin + "; passivating");
+ if (trace_)
+ {
+ log_.trace("Elapsed time of " + elapsed + " for session "+
+ realId + " exceeds min of " + passivationMin + "; passivating");
+ }
processUnloadedSessionPassivation(realId, osu);
}
}
@@ -2159,7 +1967,8 @@
}
else if (ReplicationGranularity.FIELD == replicationGranularity_)
{
- throw new IllegalStateException("Property snapshotMode must be " + SnapshotMode.INTERVAL + " when FIELD granularity is used");
+ throw new IllegalStateException("Property snapshotMode must be " +
+ SnapshotMode.INTERVAL + " when FIELD granularity is used");
}
else if (snapshotInterval_ < 1)
{
@@ -2253,7 +2062,7 @@
}
}
- public void releaseCacheToManager()
+ private void releaseCacheToManager()
{
try
{
@@ -2273,22 +2082,202 @@
log_.error("Problem releasing cache to CacheManager -- config is " + cacheConfigName_, e);
}
}
+
+ /**
+ * Clear the underlying cache store.
+ */
+ private void clearSessions()
+ {
+ boolean passivation = isPassivationEnabled();
+ // First, the sessions we have actively loaded
+ ClusteredSession[] sessions = findLocalSessions();
+ for(int i=0; i < sessions.length; i++)
+ {
+ ClusteredSession ses = sessions[i];
+
+ if (trace_)
+ {
+ log_.trace("clearSessions(): clear session by expiring or passivating: " + ses);
+ }
+ try
+ {
+ // if session passivation is enabled, passivate sessions instead of expiring them which means
+ // they'll be available to the manager for activation after a restart.
+ if(passivation && ses.isValid())
+ {
+ processSessionPassivation(ses.getRealId());
+ }
+ else
+ {
+ boolean notify = true;
+ boolean localCall = true;
+ boolean localOnly = true;
+ ses.expire(notify, localCall, localOnly);
+ }
+ }
+ catch (Throwable t)
+ {
+ log_.warn("clearSessions(): Caught exception expiring or passivating session " +
+ ses.getIdInternal(), t);
+ }
+ finally
+ {
+ // Guard against leaking memory if anything is holding a
+ // ref to the session by clearing its internal state
+ ses.recycle();
+ }
+ }
+
+ String action = passivation ? "evicting" : "removing";
+ Set<Map.Entry<String, OwnedSessionUpdate>> unloaded =
+ unloadedSessions_.entrySet();
+ for (Iterator<Map.Entry<String, OwnedSessionUpdate>> it = unloaded.iterator(); it.hasNext();)
+ {
+ Map.Entry<String, OwnedSessionUpdate> entry = it.next();
+ String realId = entry.getKey();
+ try
+ {
+ if (passivation)
+ {
+ OwnedSessionUpdate osu = entry.getValue();
+ // Ignore the marker entries for our passivated sessions
+ if (!osu.passivated)
+ {
+ proxy_.evictSession(realId, osu.owner);
+ }
+ }
+ else
+ {
+ proxy_.removeSessionLocal(realId, false);
+ }
+ }
+ catch (Exception e)
+ {
+ // Not as big a problem; we don't own the session
+ log_.debug("Problem " + action + " session " + realId + " -- " + e);
+ }
+ it.remove();
+ }
+ }
- public PojoCache getPojoCache()
+ // ------------------------------------------------------ Lifecyle Embedded
+
+ /**
+ * Start this Manager when running embedded in JBoss AS.
+ *
+ * @throws org.apache.catalina.LifecycleException
+ */
+ private void startEmbedded() throws LifecycleException
{
- return pojoCache_;
+ super.start();
+
+ // Start the JBossCacheService
+ // Will need to pass the classloader that is associated with this
+ // web app so de-serialization will work correctly.
+ tcl_ = super.getContainer().getLoader().getClassLoader();
+
+ try
+ {
+ if (proxy_ == null) // Could happen during a restart
+ {
+ initCacheProxy();
+ }
+
+ proxy_.start(tcl_, this);
+
+ tm = proxy_.getTransactionManager();
+ if(tm == null)
+ {
+ throw new LifecycleException("JBossCacheManager.start(): Obtain null tm");
+ }
+
+ initializeUnloadedSessions();
+
+ // Setup our SnapshotManager
+ initSnapshotManager();
+
+ // Add SnapshotValve and, if needed, JvmRouteValve and batch repl valve
+ installValves();
+
+ log_.debug("start(): JBossCacheService started");
+ }
+ catch (LifecycleException le)
+ {
+ throw le;
+ }
+ catch (Exception e)
+ {
+ log_.error("Unable to start manager.", e);
+ throw new LifecycleException(e);
+ }
}
+
+ // -------------------------------------------------------------------- Misc
- public Cache getPlainCache()
+ private void endTransaction(String id)
{
- return plainCache_;
+ if (tm == null)
+ {
+ log_.warn("JBossCacheManager.endTransaction(): tm is null for id: " +id);
+ return;
+ }
+
+ try
+ {
+ if(tm.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
+ {
+ tm.commit();
+ }
+ else
+ {
+ log_.info("JBossCacheManager.endTransaction(): rolling back tx for id: " +id);
+ tm.rollback();
+ }
+ }
+ catch (RollbackException re)
+ {
+ // Do nothing here since cache may rollback automatically.
+ log_.warn("JBossCacheManager.endTransaction(): rolling back transaction with exception: " +re);
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("JBossCacheManager.endTransaction(): Exception for id: " +id, e);
+ }
}
- public String getCacheConfigName()
+ /**
+ * Gets the session id with any jvmRoute removed.
+ *
+ * @param id a session id with or without an appended jvmRoute.
+ * Cannot be <code>null</code>.
+ */
+ private String getRealId(String id)
{
- return (cacheFromCacheManager_ ? cacheConfigName_ : null);
+ return (getUseJK() ? Util.getRealId(id) : id);
}
+ private String reportSessionIds(Set ids)
+ {
+ StringBuffer sb = new StringBuffer();
+ boolean added = false;
+ for (Iterator it = ids.iterator(); it.hasNext(); )
+ {
+ if (added)
+ {
+ sb.append(',');
+ }
+ else
+ {
+ added = true;
+ }
+
+ sb.append(it.next());
+ }
+ return sb.toString();
+ }
+
+ // ------------------------------------------------------------ Inner Classes
+
private class OwnedSessionUpdate
{
String owner;
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java 2008-08-25 15:10:56 UTC (rev 77438)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManagerMBean.java 2008-08-25 15:16:30 UTC (rev 77439)
@@ -34,18 +34,12 @@
*
* @param sessionId the id of the session
* @param key the attribute key
- * @return the value, or <code>null</code> if the session or
- * key does not exist.
+ * @return the value, converted to a String via toString(),
+ * or <code>null</code> if the session or key does not exist.
*/
- Object getSessionAttribute(String sessionId, String key);
+ String getSessionAttribute(String sessionId, String key);
/**
- * Same as <code>getSessionAttribute(sessionId, key).toString()</code>.
- *
- */
- String getSessionAttributeString(String sessionId, String key);
-
- /**
* Expires the given session. If the session is in the distributed store
* but hasn't been loaded on this node, invoking this method will cause it
* to be loaded.
@@ -55,17 +49,28 @@
void expireSession(String sessionId);
/**
- * Gets the last time the given session was accessed on this node.
- * Information about sessions stored in the distributed store but never
- * accessed on this node will not be made available.
+ * Gets the last time the given session was accessed. If the session is in
+ * the distributed store but hasn't been loaded on this node, invoking this
+ * method will cause it to be loaded.
*
* @param sessionId
- * @return the last accessed time, or <code>null</code> if the session
- * has expired or has never been accessed on this node.
+ * @return the last accessed time, or the empty string if the session
+ * doesn't exist.
*/
String getLastAccessedTime(String sessionId);
/**
+ * Gets the creation time of the given session. If the session is in
+ * the distributed store but hasn't been loaded on this node, invoking this
+ * method will cause it to be loaded.
+ *
+ * @param sessionId
+ * @return the creation time, or or the empty string if the session
+ * doesn't exist.
+ */
+ String getCreationTime(String sessionId);
+
+ /**
* Gets the cache config name used to get the underlying cache
* from a cache manager.
*
@@ -174,17 +179,6 @@
String listLocalSessionIds();
/**
- * Gets the count of sessions known to this manager, excluding those
- * in the distributed stored that have not been accessed on this node.
- */
- long getLocalActiveSessionCount();
-
- /**
- * Gets the highest value seen for {@link #getLocalSessionCount()}
- */
- long getMaxLocalActiveSessionCount();
-
- /**
* Gets whether passivation was enabled in jboss-web.xml and in the
* underlying cache.
*
@@ -225,4 +219,9 @@
* @return
*/
long getPassivationMinIdleTime();
+
+ /**
+ * Gets the number of duplicated session ids generated.
+ */
+ int getDuplicates();
}
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java 2008-08-25 15:10:56 UTC (rev 77438)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManager.java 2008-08-25 15:16:30 UTC (rev 77439)
@@ -25,11 +25,14 @@
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import javax.management.MBeanServer;
import javax.management.ObjectName;
@@ -40,19 +43,18 @@
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
-import org.apache.catalina.Session;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.connector.Response;
+import org.apache.catalina.core.ContainerBase;
import org.apache.catalina.util.LifecycleSupport;
+import org.apache.tomcat.util.modeler.Registry;
import org.jboss.logging.Logger;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
-import org.jboss.metadata.web.jboss.ReplicationConfig;
import org.jboss.metadata.web.jboss.PassivationConfig;
-import org.jboss.metadata.web.jboss.ReplicationTrigger;
-import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.web.tomcat.statistics.ReplicationStatistics;
@@ -62,25 +64,17 @@
*
* @author Ben Wang
* @author Hany Mesha
+ * @author Brian Stansberry
+ *
* @version $Revision: 60686 $
*/
public abstract class JBossManager
implements AbstractJBossManager, Lifecycle,
JBossManagerMBean, PropertyChangeListener
-{
- // -- Constants ----------------------------------------
- /**
- * Informational name for this Catalina component
- */
- private static final String info_ = "JBossManager/1.0";
-
- // -- Class attributes ---------------------------------
+{
+ // ------------------------------------------------------------------ Fields
+
protected ReplicationStatistics stats_ = new ReplicationStatistics();
-
- /**
- * Policy to determine if a session is dirty
- */
- protected ReplicationTrigger replicationTrigger_;
/**
* Session passivation flag set in jboss-web.xml by the user.
@@ -92,7 +86,7 @@
/**
* Min time (milliseconds) the session must be idle since lastAccesstime before
* it's eligible for passivation if passivation is enabled and more
- * than maxActive_ sessions are in memory.
+ * than maxActiveAllowed_ sessions are in memory.
* Setting to -1 means it's ignored.
*/
protected int passivationMinIdleTime_ = -1;
@@ -108,27 +102,39 @@
* The lifecycle_ event support for this component.
*/
protected LifecycleSupport lifecycle_ = new LifecycleSupport(this);
+
/**
* Has this component been started_ yet?
*/
protected boolean started_ = false;
+
/**
* Are we allowing backgroundProcess() to execute? We use an object
* so stop() can lock on it to wait for
*/
protected AtomicBoolean backgroundProcessAllowed = new AtomicBoolean();
+
/**
* The objectname this Manager is associated with
*/
protected ObjectName objectName_;
+
/**
- * The Log-object for this class
+ * The Log object for this class
*/
- protected Logger log_ = Logger.getLogger(this.getClass().getName());
+ protected Logger log_ = Logger.getLogger(this.getClass().getName());
+
/**
+ * Whether trace logging is enabled for our logger. Rechecked
+ * every time backgroundProcess() is invoked.
+ */
+ protected boolean trace_ = log_.isTraceEnabled();
+
+ /**
* The Container with which this Manager is associated.
*/
protected Container container_;
+
/**
/**
* The distributable flag for Sessions created by this Manager. If this
@@ -136,80 +142,105 @@
* session controlled by this Manager must be Serializable.
*/
protected boolean distributable_ = true;
+
/**
* The default maximum inactive interval for Sessions created by
* this Manager.
*/
protected int maxInactiveInterval_ = 60;
- /**
- * The session id length of Sessions created by this Manager.
- */
- protected int sessionIdLength_ = 16;
- // Maximum of active sessions allowed. -1 is unlimited.
- protected int maxActive_ = -1;
+ /** Maximum of active sessions allowed. -1 is unlimited. */
+ protected int maxActiveAllowed_ = -1;
- // Number of sessions created by this manager
- protected int createdCounter_ = 0;
+ /** Number of sessions created by this manager */
+ protected AtomicInteger createdCounter_ = new AtomicInteger();
- // number of rejected sessions because the number active sessions exceeds maxActive
- protected volatile int rejectedCounter_ = 0;
+ /** number of sessions rejected because the number active sessions exceeds maxActive */
+ protected AtomicInteger rejectedCounter_ = new AtomicInteger();
- // Number of active sessions
- protected int activeCounter_ = 0;
+ /** Number of active sessions */
+ protected AtomicInteger localActiveCounter_ = new AtomicInteger();
+
+ /** Maximum number of concurrently locally active sessions */
+ protected AtomicInteger maxLocalActiveCounter_ = new AtomicInteger();
- // Maximum number of active sessions seen so far
- protected int maxActiveCounter_ = 0;
+ /** Maximum number of active sessions seen so far */
+ protected AtomicInteger maxActiveCounter_ = new AtomicInteger();
- // number of expired session ids. Not sure what exactly does it mean in our clustered case.
- protected volatile int expiredCounter_ = 0;
+ /** Number of sessions that have been active locally that are now expired. */
+ protected AtomicInteger expiredCounter_ = new AtomicInteger();
+ /** Number of ms since last call to reset() */
protected long timeSinceLastReset_ = 0;
- // Cumulative time spent in backgroundProcess
- protected long processingTime_ = 0;
+ /** Cumulative time spent in backgroundProcess */
+ protected AtomicLong processingTime_ = new AtomicLong();
- /**
- * Map<String,ClusteredSession>. Store the local sessions.
- */
- protected final Map sessions_ = new ConcurrentHashMap();
+ /** Stores the locally active sessions. */
+ protected final Map<String, ClusteredSession> sessions_ = new ConcurrentHashMap<String,ClusteredSession>();
- /**
- * If set to true, it will not replicate the access time stamp unless attributes are dirty.
- *
- * @deprecated unused
- */
- protected boolean useLocalCache_ = true;
-
- /**
- * The property change support for this component.
- */
+ /** The property change support for this component. */
protected PropertyChangeSupport support_ = new PropertyChangeSupport(this);
- protected SessionIDGenerator sessionIDGenerator_;
+ /** Generates ids for new sessions */
+ protected SessionIDGenerator sessionIDGenerator_= SessionIDGenerator.getInstance();;
+ /** Our containing engine's jvmRoute (if it has one) */
protected String jvmRoute_;
+
+ /** Our JMX Server */
+ protected MBeanServer mserver_ = null;
- // TODO Need a string manager to handle exception localization
+ /**
+ * How often calls to backgroundProcess() should trigger
+ * expiration/passivation processing
+ */
+ protected volatile int processExpiresFrequency = 1;
+
+ /**
+ * How many times backgroundProcess() has been called since we last
+ * processed expiration/passivation.
+ */
+ protected int backgroundProcessCount = 0;
+
+ /** Maximum time in ms a now expired session has been alive */
+ protected AtomicInteger maxAliveTime = new AtomicInteger();
+
+ /** Average time in ms a now expired session has been alive */
+ protected AtomicInteger averageAliveTime = new AtomicInteger();
+
+ /**
+ * Number of times our session id generator has generated an id
+ * that matches an existing session.
+ */
+ protected AtomicInteger duplicates_ = new AtomicInteger();
+
+ // TODO Need a string manager to handle localization
- public JBossManager()
+ // ------------------------------------------------------------ Constructors
+
+ /**
+ * Creates a new JBossManager
+ */
+ protected JBossManager()
{
- sessionIDGenerator_ = SessionIDGenerator.getInstance();
-
}
+ // -------------------------------------------------------------- Properties
+
+ // ---------------------------------------------------- AbstractJBossManager
+
+ /**
+ * {@inheritDoc}
+ */
public void init(String name, JBossWebMetaData webMetaData)
throws ClusteringNotSupportedException
- {
- ReplicationConfig rpc = webMetaData.getReplicationConfig();
- if (rpc != null)
- {
- replicationTrigger_ = rpc.getReplicationTrigger();
- }
+ {
if (webMetaData.getMaxActiveSessions() != null)
{
- maxActive_ = webMetaData.getMaxActiveSessions().intValue();
+ maxActiveAllowed_ = webMetaData.getMaxActiveSessions().intValue();
}
+
PassivationConfig pConfig = webMetaData.getPassivationConfig();
if (pConfig != null)
{
@@ -228,213 +259,481 @@
}
}
- log_.debug("init(): replicationTrigger is " + replicationTrigger_ +
- " and maxActiveSessions allowed is " + maxActive_ +
+ log_.debug("init(): maxActiveSessions allowed is " + maxActiveAllowed_ +
" and passivationMode is " + passivationMode_);
+ }
- try
+ /**
+ * {@inheritDoc}
+ */
+ public String getJvmRoute()
+ {
+ if (jvmRoute_ == null)
{
- // Give this manager a name
- objectName_ = new ObjectName("jboss.web:service=ClusterManager,WebModule=" + name);
+ Engine e = getEngine();
+ jvmRoute_= (e == null ? null : e.getJvmRoute());
}
- catch (Throwable e)
+ return jvmRoute_;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setNewSessionCookie(String sessionId, HttpServletResponse response)
+ {
+ if (response != null)
{
- log_.error("Could not create ObjectName", e);
- throw new ClusteringNotSupportedException(e.toString());
+ Context context = (Context) container_;
+ Connector connector = ((Response)response).getConnector();
+ if (context.getCookies())
+ {
+ // set a new session cookie
+ Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME, sessionId);
+ if (trace_)
+ {
+ log_.trace("Setting cookie with session id:" + sessionId + " & name:" + Globals.SESSION_COOKIE_NAME);
+ }
+ newCookie.setPath("/");
+ newCookie.setMaxAge(-1);
+ if (connector.getSecure()) {
+ newCookie.setSecure(true);
+ }
+
+ response.addCookie(newCookie);
+ }
}
}
+
+ // ----------------------------------------------------------------- Manager
- public ReplicationTrigger getReplicationTrigger()
+ /**
+ * {@inheritDoc}
+ */
+ public void addPropertyChangeListener(PropertyChangeListener listener)
{
- return this.replicationTrigger_;
+ support_.addPropertyChangeListener(listener);
}
/**
- * Sets the type of operations on a <code>HttpSession</code> that
- * trigger replication. Valid values are:
- * <ul>
- * <li>SET_AND_GET</li>
- * <li>SET_AND_NON_PRIMITIVE_GET</li>
- * <li>SET</li>
- * </ul>
+ * {@inheritDoc}
*/
- public void setReplicationTrigger(ReplicationTrigger trigger)
+ public void removePropertyChangeListener(PropertyChangeListener listener)
{
- this.replicationTrigger_ = trigger;
+ support_.removePropertyChangeListener(listener);
}
/**
- * Retrieve the enclosing Engine for this Manager.
- *
- * @return an Engine object (or null).
+ * {@inheritDoc}
*/
- public Engine getEngine()
+ public void propertyChange(PropertyChangeEvent evt)
{
- Engine e = null;
- for (Container c = getContainer(); e == null && c != null; c = c.getParent())
+ support_.firePropertyChange(evt);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void backgroundProcess()
+ {
+ // Always reset trace_
+ trace_ = log_.isTraceEnabled();
+
+ // For other work, only execute every processExpiresFrequency
+ backgroundProcessCount = (backgroundProcessCount + 1) % processExpiresFrequency;
+ if (backgroundProcessCount != 0)
+ return;
+
+ synchronized (backgroundProcessAllowed)
{
- if (c != null && c instanceof Engine)
+ if (backgroundProcessAllowed.get())
{
- e = (Engine) c;
+ long start = System.currentTimeMillis();
+
+ processExpirationPassivation();
+
+ long elapsed = System.currentTimeMillis() - start;
+
+ processingTime_.addAndGet(elapsed);
}
}
- return e;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getActiveSessions()
+ {
+ return calcActiveSessions();
+ }
/**
- * Retrieve the JvmRoute for the enclosing Engine.
- *
- * @return the JvmRoute or null.
+ * {@inheritDoc}
*/
- public String getJvmRoute()
+ public Container getContainer()
{
- if (jvmRoute_ == null)
+ return container_;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setContainer(Container container)
+ {
+ // De-register from the old Container (if any)
+ if ((this.container_ != null) && (this.container_ instanceof Context))
+ this.container_.removePropertyChangeListener(this);
+
+ // Default processing provided by our superclass
+ this.container_ = container;
+
+ // Register with the new Container (if any)
+ if ((this.container_ != null) && (this.container_ instanceof Context))
{
- Engine e = getEngine();
- jvmRoute_= (e == null ? null : e.getJvmRoute());
+ setMaxInactiveInterval
+ (((Context) this.container_).getSessionTimeout() * 60);
+ this.container_.addPropertyChangeListener(this);
}
- return jvmRoute_;
}
/**
- * Get a new session-id from the distributed store
- *
- * @return new session-id
+ * {@inheritDoc}
*/
- protected String getNextId()
+ public boolean getDistributable()
{
- return sessionIDGenerator_.getSessionId();
+ return distributable_;
}
/**
- * Gets the JMX <code>ObjectName</code> under
- * which our <code>TreeCache</code> is registered.
+ * {@inheritDoc}
*/
- public ObjectName getObjectName()
+ public void setDistributable(boolean distributable)
{
- return objectName_;
+ this.distributable_ = distributable;
}
- public boolean isUseLocalCache()
+ /**
+ * {@inheritDoc}
+ */
+ public int getExpiredSessions()
{
- return useLocalCache_;
+ return expiredCounter_.get();
}
+ /** No-op */
+ public void setExpiredSessions(int expiredSessions)
+ {
+ // ignored
+ }
+
/**
- * Sets a new cookie for the given session id and response
- *
- * @param sessionId The session id
+ * {@inheritDoc}
*/
- public void setSessionCookie(String sessionId)
+ public int getMaxActive()
{
- HttpServletResponse response = SessionReplicationContext.getOriginalResponse();
- setNewSessionCookie(sessionId, response);
+ return maxActiveAllowed_;
}
- public void setNewSessionCookie(String sessionId, HttpServletResponse response)
+ /**
+ * {@inheritDoc}
+ */
+ public void setMaxActive(int maxActive)
{
- if (response != null)
- {
- Context context = (Context) container_;
- Connector connector = ((Response)response).getConnector();
- if (context.getCookies())
- {
- // set a new session cookie
- Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME, sessionId);
- if (log_.isTraceEnabled())
- {
- log_.trace("Setting cookie with session id:" + sessionId + " & name:" + Globals.SESSION_COOKIE_NAME);
- }
- newCookie.setPath("/");
- newCookie.setMaxAge(-1);
- if (connector.getSecure()) {
- newCookie.setSecure(true);
- }
+ this.maxActiveAllowed_ = maxActive;
+ }
- response.addCookie(newCookie);
- }
- }
+ /**
+ * {@inheritDoc}
+ */
+ public int getMaxInactiveInterval()
+ {
+ return maxInactiveInterval_;
}
- // JBossManagerMBean-methods -------------------------------------
-
- // A better property name for the MBean API
- public int getMaxActiveAllowed()
+ /**
+ * {@inheritDoc}
+ */
+ public void setMaxInactiveInterval(int interval)
{
- return getMaxActive();
+ this.maxInactiveInterval_ = interval;
}
- // A better property name for the MBean API
- public void setMaxActiveAllowed(int maxActive)
+ /**
+ * {@inheritDoc}
+ */
+ public long getProcessingTime()
{
- setMaxActive(maxActive);
+ return this.processingTime_.get();
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getRejectedSessions()
+ {
+ return rejectedCounter_.get();
+ }
+
+ /** No-op */
+ public void setRejectedSessions(int rejectedSessions)
+ {
+ // ignored
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getSessionAverageAliveTime()
+ {
+ return averageAliveTime.get();
+ }
+
+ /** No-op */
+ public void setSessionAverageAliveTime(int sessionAverageAliveTime)
+ {
+ // ignored
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getSessionCounter()
+ {
+ return createdCounter_.get();
+ }
+
+ /** No-op */
+ public void setSessionCounter(int sessionCounter)
+ {
+ // ignored
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getSessionIdLength()
+ {
+ return SessionIDGenerator.SESSION_ID_BYTES;
+ }
+
+ /** No-op */
+ public void setSessionIdLength(int idLength)
+ {
+ // ignored
+ }
- public long getMaxActiveSessionCount()
+ /**
+ * {@inheritDoc}
+ */
+ public int getSessionMaxAliveTime()
{
- return this.maxActiveCounter_;
+ return maxAliveTime.get();
}
- public ReplicationStatistics getReplicationStatistics()
+ /** No-op */
+ public void setSessionMaxAliveTime(int sessionAliveTime)
{
- return stats_;
+ // ignored
}
- public void resetStats()
+ /** Throws UnsupportedOperationException */
+ public void load() throws ClassNotFoundException, IOException
{
- stats_.resetStats();
- activeCounter_ = 0;
- maxActiveCounter_ = 0;
- rejectedCounter_ = 0;
- createdCounter_ = 0;
- expiredCounter_ = 0;
- processingTime_ = 0;
- timeSinceLastReset_ = System.currentTimeMillis();
+ throw new UnsupportedOperationException("load() not supported");
}
- public long timeInSecondsSinceLastReset()
+ /** Throws UnsupportedOperationException */
+ public void unload() throws IOException
{
- return (System.currentTimeMillis() - timeSinceLastReset_) / (1000L);
+ throw new UnsupportedOperationException("unload() not supported");
}
+ // -------------------------------------------------------------- Lifecycle
+
+ public void addLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle_.addLifecycleListener(listener);
+ }
+
+ public LifecycleListener[] findLifecycleListeners()
+ {
+ return lifecycle_.findLifecycleListeners();
+ }
+
+ public void removeLifecycleListener(LifecycleListener listener)
+ {
+ lifecycle_.removeLifecycleListener(listener);
+ }
+
+ /**
+ * Start this Manager
+ *
+ * @throws org.apache.catalina.LifecycleException
+ *
+ */
+ public void start() throws LifecycleException
+ {
+ startManager();
+ }
+
+ /**
+ * Stop this Manager
+ *
+ * @throws org.apache.catalina.LifecycleException
+ *
+ */
+ public void stop() throws LifecycleException
+ {
+ // Block for any ongoing backgroundProcess, then disable
+ synchronized (backgroundProcessAllowed)
+ {
+ backgroundProcessAllowed.set(false);
+ }
+
+ resetStats();
+ stopManager();
+ }
+
+ // ------------------------------------------------------- JBossManagerMBean
+
+ /**
+ * {@inheritDoc}
+ */
public long getActiveSessionCount()
{
- return getActiveSessions();
+ return calcActiveSessions();
}
+ /**
+ * {@inheritDoc}
+ */
+ public String getAlgorithm()
+ {
+ return SessionIDGenerator.SESSION_ID_HASH_ALGORITHM;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getClassName()
+ {
+ return getClass().getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public long getCreatedSessionCount()
{
- return createdCounter_;
+ return createdCounter_.get();
}
+ /**
+ * {@inheritDoc}
+ */
public long getExpiredSessionCount()
{
- return expiredCounter_;
+ return expiredCounter_.get();
}
- public long getRejectedSessionCount()
+ /**
+ * {@inheritDoc}
+ */
+ public long getLocalActiveSessionCount()
{
- return rejectedCounter_;
+ return localActiveCounter_.get();
}
- public int getSessionMaxAliveTime()
+ /**
+ * {@inheritDoc}
+ */
+ public int getMaxActiveAllowed()
{
- return 0;
+ return getMaxActive();
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setMaxActiveAllowed(int maxActive)
+ {
+ setMaxActive(maxActive);
+ }
- public void setSessionMaxAliveTime(int sessionMaxAliveTime)
+ /**
+ * {@inheritDoc}
+ */
+ public int getMaxActiveSessions()
{
+ return getMaxActiveAllowed();
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getMaxActiveSessionCount()
+ {
+ return this.maxActiveCounter_.get();
+ }
- public int getSessionAverageAliveTime()
+ /**
+ * {@inheritDoc}
+ */
+ public long getMaxLocalActiveSessionCount()
{
- return 0;
+ return maxLocalActiveCounter_.get();
}
- public void setSessionAverageAliveTime(int sessionAverageAliveTime)
+ /**
+ * {@inheritDoc}
+ */
+ public String getName()
{
+ return getClass().getSimpleName();
}
+ /**
+ * {@inheritDoc}
+ */
+ public int getProcessExpiresFrequency()
+ {
+ return this.processExpiresFrequency;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setProcessExpiresFrequency(int frequency)
+ {
+ this.processExpiresFrequency = frequency;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getRejectedSessionCount()
+ {
+ return rejectedCounter_.get();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ReplicationStatistics getReplicationStatistics()
+ {
+ return stats_;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getTimeSinceLastReset()
+ {
+ return (System.currentTimeMillis() - timeSinceLastReset_) / (1000L);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public String reportReplicationStatistics()
{
StringBuffer tmp = new StringBuffer();
@@ -492,6 +791,9 @@
}
+ /**
+ * {@inheritDoc}
+ */
public String reportReplicationStatisticsCSV()
{
StringBuffer tmp = createCSVHeader();
@@ -534,6 +836,9 @@
}
+ /**
+ * {@inheritDoc}
+ */
public String reportReplicationStatisticsCSV(String sessionId)
{
StringBuffer tmp = createCSVHeader();
@@ -569,73 +874,226 @@
}
return tmp.toString();
}
-
- private StringBuffer createCSVHeader()
+
+ /**
+ * {@inheritDoc}
+ */
+ public void resetStats()
{
- StringBuffer tmp = new StringBuffer();
- tmp.append("sessionID,");
- tmp.append("replicationCount,");
- tmp.append("minPassivationTime,");
- tmp.append("maxPassivationTime,");
- tmp.append("totalPassivationTime,");
- tmp.append("minReplicationTime,");
- tmp.append("maxReplicationTime,");
- tmp.append("totalReplicationlTime,");
- tmp.append("loadCount,");
- tmp.append("minLoadTime,");
- tmp.append("maxLoadTime,");
- tmp.append("totalLoadTime");
-
- return tmp;
+ stats_.resetStats();
+ maxActiveCounter_.set(localActiveCounter_.get());
+ rejectedCounter_.set(0);
+ createdCounter_.set(0);
+ expiredCounter_.set(0);
+ processingTime_.set(0);
+ maxAliveTime.set(0);
+ averageAliveTime.set(0);
+ duplicates_.set(0);
+ timeSinceLastReset_ = System.currentTimeMillis();
}
- // Lifecycle-methods -------------------------------------
+ // ------------------------------------------------------------------ Public
- public void addLifecycleListener(LifecycleListener listener)
+ /**
+ * Gets the JMX <code>ObjectName</code> under
+ * which our <code>TreeCache</code> is registered.
+ */
+ public ObjectName getObjectName()
{
- lifecycle_.addLifecycleListener(listener);
+ return objectName_;
}
- public LifecycleListener[] findLifecycleListeners()
- {
- return lifecycle_.findLifecycleListeners();
- }
+ // ------------------------------------------------------------------ Protected
- public void removeLifecycleListener(LifecycleListener listener)
+ /**
+ * Go through all sessions and look if they have expired or need to be passivated.
+ */
+ protected abstract void processExpirationPassivation();
+
+ /** Get the total number of active sessions */
+ protected abstract int getTotalActiveSessions();
+
+ /**
+ * Calculates the number of active sessions, and updates
+ * the max # of local active sessions and max # of sessions.
+ * <p>
+ * Call this method when a new session is added or when an
+ * accurate count of active sessions is needed.
+ * </p>
+ *
+ * @return the size of the sessions map + the size of the unloaded sessions
+ * map - the count of passivated sessions
+ */
+ protected int calcActiveSessions()
{
- lifecycle_.removeLifecycleListener(listener);
+ localActiveCounter_.set(sessions_.size());
+ int active = localActiveCounter_.get();
+ int maxLocal = maxLocalActiveCounter_.get();
+ while (active > maxLocal)
+ {
+ if (!maxLocalActiveCounter_.compareAndSet(maxLocal, active))
+ {
+ maxLocal = maxLocalActiveCounter_.get();
+ }
+ }
+
+ int count = getTotalActiveSessions();
+ int max = maxActiveCounter_.get();
+ while (count > max)
+ {
+ if (!maxActiveCounter_.compareAndSet(max, count))
+ {
+ max = maxActiveCounter_.get();
+ // Something changed, so reset our count
+ count = getTotalActiveSessions();
+ }
+ }
+ return count;
}
/**
- * Start this Manager
+ * Returns the given session if it is being actively managed by this manager.
+ * An actively managed session is on that was either created on this server,
+ * brought into local management by a call to
+ * {@link #findLocalSession(String)} or brought into local management by a
+ * call to {@link #findSessions()}.
*
- * @throws org.apache.catalina.LifecycleException
+ * @param realId the session id, with any trailing jvmRoute removed.
*
+ * @see #getRealId(String)
*/
- public void start() throws LifecycleException
+ protected ClusteredSession findLocalSession(String realId)
{
- startManager();
+ return sessions_.get(realId);
}
/**
- * Stop this Manager
+ * Returns all the sessions that are being actively managed by this manager.
+ * This includes those that were created on this server, those that were
+ * brought into local management by a call to
+ * {@link #findLocalSession(String)} as well as all sessions brought into
+ * local management by a call to {@link #findSessions()}.
+ */
+ protected ClusteredSession[] findLocalSessions()
+ {
+ Collection<ClusteredSession> coll = sessions_.values();
+ ClusteredSession[] sess = new ClusteredSession[coll.size()];
+ return coll.toArray(sess);
+ }
+
+ /**
+ * Get a new session-id from the distributed store
*
- * @throws org.apache.catalina.LifecycleException
- *
+ * @return new session-id
*/
- public void stop() throws LifecycleException
+ protected String getNextId()
{
- // Block for any ongoing backgroundProcess, then disable
- synchronized (backgroundProcessAllowed)
+ return sessionIDGenerator_.getSessionId();
+ }
+
+ /**
+ * Updates statistics to reflect that a session with a given "alive time"
+ * has been expired.
+ *
+ * @param sessionAliveTime number of ms from when the session was created
+ * to when it was expired.
+ */
+ protected void sessionExpired(int sessionAliveTime)
+ {
+ int current = maxAliveTime.get();
+ while (sessionAliveTime > current)
{
- backgroundProcessAllowed.set(false);
+ if (maxAliveTime.compareAndSet(current, sessionAliveTime))
+ break;
+ else
+ current = maxAliveTime.get();
}
- resetStats();
- stopManager();
+ expiredCounter_.incrementAndGet();
+ int newAverage;
+ do
+ {
+ int expCount = expiredCounter_.get();
+ current = averageAliveTime.get();
+ newAverage = ((current * (expCount - 1)) + sessionAliveTime)/expCount;
+ }
+ while (averageAliveTime.compareAndSet(current, newAverage) == false);
}
+
+ /**
+ * Register this Manager with JMX.
+ */
+ protected void registerManagerMBean()
+ {
+ try
+ {
+ MBeanServer server = getMBeanServer();
+ String domain;
+ if (container_ instanceof ContainerBase)
+ {
+ domain = ((ContainerBase) container_).getDomain();
+ }
+ else
+ {
+ domain = server.getDefaultDomain();
+ }
+ String hostName = ((Host) container_.getParent()).getName();
+ hostName = (hostName == null) ? "localhost" : hostName;
+ ObjectName clusterName = new ObjectName(domain
+ + ":type=Manager,host=" + hostName + ",path="
+ + ((Context) container_).getPath());
+
+ if (server.isRegistered(clusterName))
+ {
+ log_.warn("MBean " + clusterName + " already registered");
+ return;
+ }
+
+ objectName_ = clusterName;
+ server.registerMBean(this, clusterName);
+
+ }
+ catch (Exception ex)
+ {
+ log_.error("Could not register " + getClass().getSimpleName() + " to MBeanServer", ex);
+ }
+ }
+
/**
+ * Unregister this Manager from the JMX server.
+ */
+ protected void unregisterManagerMBean()
+ {
+ if (mserver_ != null)
+ {
+ try
+ {
+ mserver_.unregisterMBean(objectName_);
+ }
+ catch (Exception e)
+ {
+ log_.error("Could not unregister " + getClass().getSimpleName() + " from MBeanServer", e);
+ }
+ }
+ }
+
+ /**
+ * Get the current MBean Server.
+ *
+ * @return
+ * @throws Exception
+ */
+ protected MBeanServer getMBeanServer() throws Exception
+ {
+ if (mserver_ == null)
+ {
+ mserver_ = Registry.getRegistry(null, null).getMBeanServer();
+ }
+ return mserver_;
+ }
+
+ /**
* Prepare for the beginning of active use of the public methods of this
* component. This method should be called after <code>configure()</code>,
* and before any of the public methods of the component are utilized.
@@ -660,15 +1118,7 @@
started_ = true;
// register ClusterManagerMBean to the MBeanServer
- try
- {
- MBeanServer server = MBeanServerLocator.locateJBoss();
- server.registerMBean(this, objectName_);
- }
- catch (Exception e)
- {
- log_.error("Could not register ClusterManagerMBean to MBeanServer", e);
- }
+ registerManagerMBean();
}
/**
@@ -692,236 +1142,47 @@
lifecycle_.fireLifecycleEvent(STOP_EVENT, null);
started_ = false;
- // unregister ClusterManagerMBean from the MBeanServer
- try
- {
- MBeanServer server = MBeanServerLocator.locateJBoss();
- server.unregisterMBean(objectName_);
- }
- catch (Exception e)
- {
- log_.error("Could not unregister ClusterManagerMBean from MBeanServer", e);
- }
+ // unregister from the MBeanServer
+ unregisterManagerMBean();
}
- // Manager-methods -------------------------------------
- public Container getContainer()
- {
- return container_;
- }
-
- public void setContainer(Container container)
- {
- // De-register from the old Container (if any)
- if ((this.container_ != null) && (this.container_ instanceof Context))
- this.container_.removePropertyChangeListener(this);
-
- // Default processing provided by our superclass
- this.container_ = container;
-
- // Register with the new Container (if any)
- if ((this.container_ != null) && (this.container_ instanceof Context))
- {
- setMaxInactiveInterval
- (((Context) this.container_).getSessionTimeout() * 60);
- this.container_.addPropertyChangeListener(this);
- }
- }
-
- public boolean getDistributable()
- {
- return distributable_;
- }
-
- public void setDistributable(boolean distributable)
- {
- this.distributable_ = distributable;
- }
-
- public String getInfo()
- {
- return info_;
- }
-
- public int getMaxInactiveInterval()
- {
- return maxInactiveInterval_;
- }
-
- public void setMaxInactiveInterval(int interval)
- {
- this.maxInactiveInterval_ = interval;
- }
-
- public int getSessionIdLength()
- {
- return sessionIdLength_;
- }
-
- public void setSessionIdLength(int idLength)
- {
- this.sessionIdLength_ = idLength;
- }
-
- public int getSessionCounter()
- {
- return createdCounter_;
- }
-
- public void setSessionCounter(int sessionCounter)
- {
- this.createdCounter_ = sessionCounter;
- }
-
- public int getMaxActive()
- {
- return maxActive_;
- }
-
- public void setMaxActive(int maxActive)
- {
- this.maxActive_ = maxActive;
- }
-
- public int getExpiredSessions()
- {
- return expiredCounter_;
- }
-
- public void setExpiredSessions(int expiredSessions)
- {
- this.expiredCounter_ = expiredSessions;
- }
-
- public int getRejectedSessions()
- {
- return rejectedCounter_;
- }
-
- public void setRejectedSessions(int rejectedSessions)
- {
- this.rejectedCounter_ = rejectedSessions;
- }
+ // ----------------------------------------------------------------- Private
- public long getProcessingTime()
+ private StringBuffer createCSVHeader()
{
- return this.processingTime_;
+ StringBuffer tmp = new StringBuffer();
+ tmp.append("sessionID,");
+ tmp.append("replicationCount,");
+ tmp.append("minPassivationTime,");
+ tmp.append("maxPassivationTime,");
+ tmp.append("totalPassivationTime,");
+ tmp.append("minReplicationTime,");
+ tmp.append("maxReplicationTime,");
+ tmp.append("totalReplicationlTime,");
+ tmp.append("loadCount,");
+ tmp.append("minLoadTime,");
+ tmp.append("maxLoadTime,");
+ tmp.append("totalLoadTime");
+
+ return tmp;
}
- public void addPropertyChangeListener(PropertyChangeListener listener)
- {
- support_.addPropertyChangeListener(listener);
- }
-
/**
- * Remove the active session locally from the manager without replicating to the cluster. This can be
- * useful when the session is exipred, for example, where there is not need to propagate the expiration.
+ * Retrieve the enclosing Engine for this Manager.
*
- * @param session
+ * @return an Engine object (or null).
*/
- public abstract void removeLocal(Session session);
-
- /**
- * Store the modified session.
- *
- * @param session
- */
- public abstract boolean storeSession(Session session);
-
- public int getActiveSessions()
+ private Engine getEngine()
{
- return activeCounter_;
- }
-
-/*
- public void add(Session session)
- {
- //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public Session createEmptySession()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public Session createSession()
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public Session findSession(String id) throws IOException
- {
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public Session[] findSessions()
- {
- return new Session[0]; //To change body of implemented methods use File | Settings | File Templates.
- }
-
- public void remove(Session session)
- {
- //To change body of implemented methods use File | Settings | File Templates.
- }
-*/
-
- public void load() throws ClassNotFoundException, IOException
- {
- // TODO. Implement persistence layer.
- throw new RuntimeException("JBossManager.load(): Method not implemented.");
- }
-
- public void removePropertyChangeListener(PropertyChangeListener listener)
- {
- support_.removePropertyChangeListener(listener);
- }
-
- public void unload() throws IOException
- {
- // TODO. Implement persistence layer.
- throw new RuntimeException("JBossManager.load(): Method not implemented.");
- }
-
- public void backgroundProcess()
- {
- // Called from Catalina StandardEngine for every 60 seconds.
-
- synchronized (backgroundProcessAllowed)
+ Engine e = null;
+ for (Container c = getContainer(); e == null && c != null; c = c.getParent())
{
- if (backgroundProcessAllowed.get())
+ if (c != null && c instanceof Engine)
{
- long start = System.currentTimeMillis();
-
- processExpires();
-
- long elapsed = System.currentTimeMillis() - start;
-
- processingTime_ += elapsed;
+ e = (Engine) c;
}
}
+ return e;
}
- /**
- * Go through all sessions and look if they have expired
- */
- protected abstract void processExpires();
-
- public void propertyChange(PropertyChangeEvent evt)
- {
- // TODO Need to handle it here.
- }
-
- /**
- * Find in-memory sessions, if any.
- * @return local session found. Sessions of size 0, if not found.
- */
- abstract public ClusteredSession[] findLocalSessions();
-
- /**
- * Find in-memory sessions, if any.
- * @param realId the Session id without JvmRoute tag.
- * @return local session found. Null if not found.
- */
- abstract public ClusteredSession findLocalSession(String realId);
-
}
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java 2008-08-25 15:10:56 UTC (rev 77438)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossManagerMBean.java 2008-08-25 15:16:30 UTC (rev 77439)
@@ -47,16 +47,23 @@
void resetStats();
/**
- * Gets the elapsed time since this manager was instantiated or the
- * last call to resetStats()
+ * Gets the elapsed time (in seconds) since this manager was instantiated
+ * or the last call to resetStats()
*/
- long timeInSecondsSinceLastReset();
+ long getTimeSinceLastReset();
/**
* Gets the number of sessions active on this node. This includes
* replicated sessions that have not been accessed on this node.
*/
long getActiveSessionCount();
+
+ /**
+ * Gets the count of sessions known to this manager, excluding those
+ * in the distributed stored that have not been accessed on this node.
+ */
+ long getLocalActiveSessionCount();
+
/**
* Gets the number of times session creation has failed because the
@@ -85,6 +92,11 @@
long getMaxActiveSessionCount();
/**
+ * Gets the highest value seen for {@link #getLocalSessionCount()}
+ */
+ long getMaxLocalActiveSessionCount();
+
+ /**
* Gets the maximum number of {@link #getActiveSessionCount() active sessions}
* that will concurrently be allowed on this node. This includes replicated
* sessions that have not been accessed on this node.
@@ -154,4 +166,79 @@
* any jvmRoute.
*/
int getSessionIdLength();
+
+ // StandardManager Attributes
+
+ /**
+ * Gets the message digest algorithm to be used when generating
+ * session identifiers.
+ */
+ String getAlgorithm();
+
+ /**
+ * Gets the fully qualified class name of the managed object
+ */
+ String getClassName();
+
+ /**
+ * Gets the maximum number of active Sessions allowed, or -1
+ for no limit.
+ */
+ int getMaxActiveSessions();
+
+ /**
+ * Gets the frequency of the manager checks (expiration and passivation)
+ */
+ int getProcessExpiresFrequency();
+
+ /**
+ * Sets the frequency of the manager checks (expiration and passivation)
+ */
+ void setProcessExpiresFrequency(int frequency);
+
+ /**
+ * Gets the name of this Manager implementation.
+ */
+ String getName();
+
+ /**
+ * Number of active sessions at this moment.
+ * Same as {@link #getActiveSessionCount()}.
+ */
+ int getActiveSessions();
+
+ /**
+ * Total number of sessions created by this manager.
+ * Same as {@link #getCreatedSessionCount()}
+ */
+ int getSessionCounter();
+
+ /**
+ * Gets the maximum number of active sessions so far.
+ * Same as {@link #getMaxActiveSessionCount()}
+ */
+ int getMaxActive();
+
+ /**
+ * Gets the longest time an expired session had been alive
+ */
+ int getSessionMaxAliveTime();
+
+ /**
+ * Gets the average time an expired session had been alive
+ */
+ int getSessionAverageAliveTime();
+
+ /**
+ * Gets the number of sessions that expired.
+ * Same as {@link #getExpiredSessionCount()}
+ */
+ int getExpiredSessions();
+
+ /**
+ * Gets the number of sessions we rejected due to maxActive being reached.
+ * Same as {@link #getRejectedSessionCount()}
+ */
+ int getRejectedSessions();
+
}
More information about the jboss-cvs-commits
mailing list