[jboss-cvs] JBossAS SVN: r80003 - 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
Thu Oct 23 14:55:57 EDT 2008
Author: bstansberry at jboss.com
Date: 2008-10-23 14:55:56 -0400 (Thu, 23 Oct 2008)
New Revision: 80003
Added:
trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockDistributedCacheManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredManager.java
Removed:
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java
Modified:
trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockJBossManager.java
trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
Log:
[JBAS-6109] ClusteredSession no longer subclasses StandardSession
Added: trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockDistributedCacheManager.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockDistributedCacheManager.java (rev 0)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockDistributedCacheManager.java 2008-10-23 18:55:56 UTC (rev 80003)
@@ -0,0 +1,178 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.test.cluster.web.jvmroute;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import org.jboss.web.tomcat.service.session.distributedcache.spi.BatchingManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSession;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.FieldBasedDistributedCacheManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.LocalDistributableSessionManager;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class MockDistributedCacheManager implements FieldBasedDistributedCacheManager
+{
+ public static final MockDistributedCacheManager INSTANCE = new MockDistributedCacheManager();
+
+
+ public void evictSession(String realId)
+ {
+ // no-op
+ }
+
+ public void evictSession(String realId, String dataOwner)
+ {
+ // no-op
+ }
+
+ public Object getAttribute(String realId, String key)
+ {
+ return null;
+ }
+
+ public Set getAttributeKeys(String realId)
+ {
+ return Collections.EMPTY_SET;
+ }
+
+ public Map getAttributes(String realId)
+ {
+ return Collections.EMPTY_MAP;
+ }
+
+ public BatchingManager getBatchingManager()
+ {
+ return null;
+ }
+
+ public Map getSessionData(String realId, String dataOwner)
+ {
+ return Collections.EMPTY_MAP;
+ }
+
+ public Map<String, String> getSessionIds()
+ {
+ return Collections.EMPTY_MAP;
+ }
+
+ public boolean isPassivationEnabled()
+ {
+ return false;
+ }
+
+ public <T extends DistributableSession> T loadSession(String realId, T toLoad)
+ {
+ return null;
+ }
+
+ public void putAttribute(String realId, Map map)
+ {
+ // no-op
+ }
+
+ public void putAttribute(String realId, String key, Object value)
+ {
+ // no-op
+ }
+
+ public void putSession(String realId, DistributableSession session)
+ {
+ // no-op
+ }
+
+ public Object removeAttribute(String realId, String key)
+ {
+ return null;
+ }
+
+ public void removeAttributes(String realId)
+ {
+ // no-op
+ }
+
+ public void removeAttributesLocal(String realId)
+ {
+ // no-op
+ }
+
+ public void removeSession(String realId, boolean removeRegion)
+ {
+ // no-op
+ }
+
+ public void removeSessionLocal(String realId, boolean removeRegion)
+ {
+ // no-op
+ }
+
+ public void removeSessionLocal(String realId, String dataOwner)
+ {
+ // no-op
+ }
+
+ public void start(ClassLoader tcl, LocalDistributableSessionManager manager)
+ {
+ // no-op
+ }
+
+ public void stop()
+ {
+ // no-op
+ }
+
+ public Object getPojo(String realId, String key)
+ {
+ return null;
+ }
+
+ public Set getPojoKeys(String realId)
+ {
+ return Collections.EMPTY_SET;
+ }
+
+ public Object removePojo(String realId, String key)
+ {
+ return null;
+ }
+
+ public void removePojoLocal(String realId, String key)
+ {
+ // no-op
+ }
+
+ public void removePojosLocal(String realId)
+ {
+ // no-op
+ }
+
+ public Object setPojo(String realId, String key, Object pojo, DistributableSession session)
+ {
+ return null;
+ }
+
+}
Modified: trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockJBossManager.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockJBossManager.java 2008-10-23 18:15:21 UTC (rev 80002)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockJBossManager.java 2008-10-23 18:55:56 UTC (rev 80003)
@@ -31,14 +31,18 @@
import org.apache.catalina.Session;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.metadata.web.jboss.ReplicationTrigger;
-import org.jboss.web.tomcat.service.session.AbstractJBossManager;
+import org.jboss.web.tomcat.service.session.ClusteredManager;
import org.jboss.web.tomcat.service.session.distributedcache.spi.ClusteringNotSupportedException;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.FieldBasedDistributedCacheManager;
+import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationPolicy;
+import org.jboss.web.tomcat.service.session.notification.LegacyClusteredSessionNotificationPolicy;
/**
* @author Brian Stansberry
*
*/
-public class MockJBossManager implements AbstractJBossManager
+public class MockJBossManager implements ClusteredManager
{
private String jvmRoute = null;
private String newCookieIdSession = null;
@@ -248,4 +252,32 @@
{
}
+ public DistributedCacheManager getDistributedCacheManager()
+ {
+ return MockDistributedCacheManager.INSTANCE;
+ }
+
+ public FieldBasedDistributedCacheManager getFieldBasedDistributedCacheManager()
+ {
+ return MockDistributedCacheManager.INSTANCE;
+ }
+
+ public int getMaxUnreplicatedInterval()
+ {
+ return -1;
+ }
+
+ public ClusteredSessionNotificationPolicy getNotificationPolicy()
+ {
+ // TODO Auto-generated method stub
+ return new LegacyClusteredSessionNotificationPolicy();
+ }
+
+ public boolean getUseJK()
+ {
+ return true;
+ }
+
+
+
}
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-10-23 18:15:21 UTC (rev 80002)
+++ trunk/testsuite/src/main/org/jboss/test/cluster/web/jvmroute/MockSession.java 2008-10-23 18:55:56 UTC (rev 80003)
@@ -22,13 +22,10 @@
package org.jboss.test.cluster.web.jvmroute;
-import java.util.Map;
-
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.ClusteredManager;
import org.jboss.web.tomcat.service.session.ClusteredSession;
import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationCause;
@@ -48,11 +45,11 @@
*/
public MockSession(MockJBossManager manager)
{
- super(manager, ReplicationTrigger.SET_AND_NON_PRIMITIVE_GET, true);
+ super(manager);
}
@Override
- public void tellNew()
+ public void tellNew(ClusteredSessionNotificationCause cause)
{
// no-op
}
@@ -69,29 +66,29 @@
{
return new StandardSessionFacade(this);
}
-
- // Inherited abstract methods
@Override
- protected Object getJBossInternalAttribute(String name)
+ public String getInfo()
{
- return null;
+ return info;
}
+ // Inherited abstract methods
+
@Override
- protected Map getJBossInternalAttributes()
+ protected Object getAttributeInternal(String name)
{
return null;
}
@Override
- public void initAfterLoad(AbstractJBossManager manager, ClusteredSessionNotificationCause cause)
+ public void initAfterLoad(ClusteredManager manager, ClusteredSessionNotificationCause cause)
{
}
@Override
- public void processSessionRepl()
+ public void processSessionReplication()
{
}
@@ -103,19 +100,24 @@
@Override
public void removeMyselfLocal()
{
+ }
+
+ @Override
+ protected void populateAttributes()
+ {
}
@Override
- protected Object setJBossInternalAttribute(String name, Object value)
+ protected Object setAttributeInternal(String name, Object value)
{
return null;
}
@Override
- protected Object removeJBossInternalAttribute(String name, boolean localCall, boolean localOnly)
+ protected Object removeAttributeInternal(String name, boolean localCall, boolean localOnly)
{
return null;
- }
+ }
}
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java 2008-10-23 18:15:21 UTC (rev 80002)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/AttributeBasedClusteredSession.java 2008-10-23 18:55:56 UTC (rev 80003)
@@ -21,25 +21,15 @@
*/
package org.jboss.web.tomcat.service.session;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
-import java.util.Set;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
+
/**
- * Implementation of a clustered session for the JBossCacheManager. The replication granularity
+ * Implementation of a clustered session where the replication granularity
* level is attribute based; that is, we replicate only the dirty attributes.
- * We use JBossCache for our internal, deplicated data store.
- * The internal structure is like in JBossCache:
- * <pre>
- * /JSESSION
- * /hostname
- * /web_app_path (path + session id is unique)
- * /id Map(id, session)
- * (VERSION_KEY, version) // Used for version tracking. version is an Integer.
- * /ATTRIBUTE Map(attr_key, value)
- * </pre>
* <p/>
* Note that the isolation level of the cache dictates the
* concurrency behavior. Also note that session and its associated attribtues are stored in different nodes.
@@ -52,7 +42,7 @@
* @version $Revision: 56542 $
*/
class AttributeBasedClusteredSession
- extends JBossCacheClusteredSession
+ extends ClusteredSession<DistributedCacheManager>
{
static final long serialVersionUID = -5625209785550936713L;
/**
@@ -61,22 +51,27 @@
protected static final String info = "AttributeBasedClusteredSession/1.0";
// Transient map to store attr changes for replication.
- private transient Map<Object, Object> attrModifiedMap_ = new HashMap<Object, Object>();
+ private transient Map<String, Object> attrModifiedMap_ = new HashMap<String, Object>();
// Note that the removed attr is intentionally stored in a map
// instead of a Set so it is faster to lookup and remove.
- private transient Map attrRemovedMap_ = new HashMap();
- private static final int REMOVE = 0; // Used to track attribute changes
- private static final int MODIFY = 1;
- // TODO why isn't the superclass field sufficient?
- private transient Map attributes_ = Collections.synchronizedMap(new HashMap());
+ private transient Map<String, Object> attrRemovedMap_ = new HashMap<String, Object>();
- public AttributeBasedClusteredSession(JBossCacheManager manager)
+ // ------------------------------------------------------------ Constructors
+
+
+ public AttributeBasedClusteredSession(ClusteredManager manager)
{
super(manager);
}
+
// ----------------------------------------------- Overridden Public Methods
+ @Override
+ public String getInfo()
+ {
+ return (info);
+ }
/**
* Override the superclass to additionally reset this class' fields.
@@ -86,72 +81,22 @@
* thorough.
* </p>
*/
+ @Override
public void recycle()
{
super.recycle();
- attributes_.clear();
clearAttrChangedMaps();
}
- /**
- * Return a string representation of this object.
- */
- public String toString()
- {
-
- StringBuffer sb = new StringBuffer();
- sb.append("AttributeBasedClusteredSession[");
- sb.append(super.toString());
- sb.append("]");
- return (sb.toString());
-
- }
-
- /**
- * Overrides the superclass version to read in the attributes.
- */
- protected synchronized void replicateAttributes()
- {
- // Go thru the attribute change list
-
- if (isSessionAttributeMapDirty())
- {
- // Go thru the modified attr list first
- int modCount = attrModifiedMap_.size();
- if (modCount == 1)
- {
- for (Map.Entry entry : attrModifiedMap_.entrySet())
- {
- proxy_.putAttribute(realId, (String) entry.getKey(), entry.getValue());
- }
- }
- else if (modCount > 0)
- {
- // It's more efficient to write a map than 2 method calls,
- // plus it reduces the number of CacheListener notifications
- proxy_.putAttribute(realId, attrModifiedMap_);
- }
-
- // Go thru the remove attr list
- if (attrRemovedMap_.size() > 0)
- {
- for (Iterator it = attrRemovedMap_.keySet().iterator(); it.hasNext(); )
- {
- proxy_.removeAttribute(realId, (String) it.next());
- }
- }
-
- clearAttrChangedMaps();
- }
- }
-
+ @Override
public void removeMyself()
{
// This is a shortcut to remove session and it's child attributes.
- proxy_.removeSession(realId, false);
+ getDistributedCacheManager().removeSession(getRealId(), false);
}
+ @Override
public void removeMyselfLocal()
{
// Need to evict attribute first before session to clean up everything.
@@ -159,101 +104,132 @@
// removeAttributesLocal call here in order to evict the ATTRIBUTE node.
// Otherwise empty nodes for the session root and child ATTRIBUTE will
// remain in the tree and screw up our list of session names.
- proxy_.removeAttributesLocal(realId);
- proxy_.removeSessionLocal(realId, false);
+ getDistributedCacheManager().removeAttributesLocal(getRealId());
+ getDistributedCacheManager().removeSessionLocal(getRealId(), false);
}
- // ------------------------------------------------ JBoss internal abstract method
+ // -------------------------------------------- Overridden Protected Methods
/**
* Populate the attributes stored in the distributed store to local transient ones.
*/
+ @Override
protected void populateAttributes()
{
- Map map = proxy_.getAttributes(realId);
+ Map map = getDistributedCacheManager().getAttributes(getRealId());
+ Map<String, Object> attributes = getAttributesInternal();
// Preserve any local attributes that were excluded from replication
- Map excluded = removeExcludedAttributes(attributes_);
+ Map excluded = removeExcludedAttributes(attributes);
if (excluded != null)
map.putAll(excluded);
- attributes_ = Collections.synchronizedMap(map);
+ attributes.clear();
+ attributes.putAll(map);
attrModifiedMap_.clear();
attrRemovedMap_.clear();
}
- protected Object getJBossInternalAttribute(String name)
+ @Override
+ protected Object getAttributeInternal(String name)
{
- Object result = attributes_.get(name);
+ Object result = getAttributesInternal().get(name);
// Do dirty check even if result is null, as w/ SET_AND_GET null
// still makes us dirty (ensures timely replication w/o using ACCESS)
if (isGetDirty(result) && !replicationExcludes.contains(name))
{
- attributeChanged(name, result, MODIFY);
+ attributeChanged(name, result, false);
}
return result;
}
- protected Object removeJBossInternalAttribute(String name,
+ @Override
+ protected Object removeAttributeInternal(String name,
boolean localCall,
boolean localOnly)
{
- Object result = attributes_.remove(name);
+ Object result = getAttributesInternal().remove(name);
if (localCall && !replicationExcludes.contains(name))
- attributeChanged(name, result, REMOVE);
+ attributeChanged(name, result, true);
return result;
}
- protected Map getJBossInternalAttributes()
+ @Override
+ protected Object setAttributeInternal(String key, Object value)
{
- return attributes_;
+ Object old = getAttributesInternal().put(key, value);
+ if (!replicationExcludes.contains(key))
+ attributeChanged(key, value, false);
+ return old;
}
- protected Set getJBossInternalKeys()
- {
- return attributes_.keySet();
- }
-
/**
- * Method inherited from Tomcat. Return zero-length based string if not found.
+ * Overrides the superclass version to read in the attributes.
*/
- protected String[] keys()
+ @Override
+ protected synchronized void replicateAttributes()
{
- return ((String[]) getJBossInternalKeys().toArray(EMPTY_ARRAY));
+ // Go thru the attribute change list
+ String myRealId = getRealId();
+ if (isSessionAttributeMapDirty())
+ {
+ DistributedCacheManager distributedCacheManager = getDistributedCacheManager();
+
+ // Go thru the modified attr list first
+ int modCount = attrModifiedMap_.size();
+ if (modCount == 1)
+ {
+ for (Map.Entry entry : attrModifiedMap_.entrySet())
+ {
+ distributedCacheManager.putAttribute(myRealId, (String) entry.getKey(), entry.getValue());
+ }
+ }
+ else if (modCount > 0)
+ {
+ // It's more efficient to write a map than 2 method calls,
+ // plus it reduces the number of CacheListener notifications
+ distributedCacheManager.putAttribute(myRealId, attrModifiedMap_);
+ }
+
+ // Go thru the remove attr list
+ if (attrRemovedMap_.size() > 0)
+ {
+ for (Iterator it = attrRemovedMap_.keySet().iterator(); it.hasNext(); )
+ {
+ distributedCacheManager.removeAttribute(myRealId, (String) it.next());
+ }
+ }
+
+ clearAttrChangedMaps();
+ }
}
- protected Object setJBossInternalAttribute(String key, Object value)
- {
- Object old = attributes_.put(key, value);
- if (!replicationExcludes.contains(key))
- attributeChanged(key, value, MODIFY);
- return old;
- }
+ // ------------------------------------------------------- Private Methods
- protected synchronized void attributeChanged(Object key, Object value, int op)
+ private synchronized void attributeChanged(String key, Object value, boolean removal)
{
- if (op == MODIFY)
+ if (removal)
{
- if (attrRemovedMap_.containsKey(key))
+ if (attrModifiedMap_.containsKey(key))
{
- attrRemovedMap_.remove(key);
+ attrModifiedMap_.remove(key);
}
- attrModifiedMap_.put(key, value);
+ attrRemovedMap_.put(key, value);
}
- else if (op == REMOVE)
+ else
{
- if (attrModifiedMap_.containsKey(key))
+ if (attrRemovedMap_.containsKey(key))
{
- attrModifiedMap_.remove(key);
+ attrRemovedMap_.remove(key);
}
- attrRemovedMap_.put(key, value);
+ attrModifiedMap_.put(key, value);
}
sessionAttributesDirty();
}
- protected synchronized void clearAttrChangedMaps()
+ private synchronized void clearAttrChangedMaps()
{
attrRemovedMap_.clear();
attrModifiedMap_.clear();
Added: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredManager.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredManager.java (rev 0)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredManager.java 2008-10-23 18:55:56 UTC (rev 80003)
@@ -0,0 +1,89 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.web.tomcat.service.session;
+
+import org.jboss.metadata.web.jboss.ReplicationTrigger;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
+import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationPolicy;
+
+/**
+ * View of a Manager from a ClusteredSession.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public interface ClusteredManager extends AbstractJBossManager
+{
+
+ /**
+ * Get the maximum interval between requests, in seconds, after which a
+ * request will trigger replication of the session's metadata regardless
+ * of whether the request has otherwise made the session dirty. Such
+ * replication ensures that other nodes in the cluster are aware of a
+ * relatively recent value for the session's timestamp and won't incorrectly
+ * expire an unreplicated session upon failover.
+ * <p/>
+ * Default value is <code>-1</code>.
+ * <p/>
+ * The cost of the metadata replication depends on the configured
+ * {@link #setReplicationGranularityString(String) replication granularity}.
+ * With <code>SESSION</code>, the sesssion's attribute map is replicated
+ * along with the metadata, so it can be fairly costly. With other
+ * granularities, the metadata object is replicated separately from the
+ * attributes and only contains a String, and a few longs, ints and booleans.
+ *
+ * @return the maximum interval since last replication after which a request
+ * will trigger session metadata replication. A value of
+ * <code>0</code> means replicate metadata on every request; a value
+ * of <code>-1</code> means never replicate metadata unless the
+ * session is otherwise dirty.
+ */
+ int getMaxUnreplicatedInterval();
+
+ /**
+ * Gets the policy for determining whether the servlet spec notifications related
+ * to session events are allowed to be emitted on the local cluster node.
+ */
+ ClusteredSessionNotificationPolicy getNotificationPolicy();
+
+ /**
+ * Gets the policy controlling whether session attribute reads and writes
+ * mark the session/attribute as needing replication.
+ *
+ * @return SET, SET_AND_GET, SET_AND_NON_PRIMITIVE_GET or <code>null</code>
+ * if this has not yet been configured.
+ */
+ ReplicationTrigger getReplicationTrigger();
+
+ /**
+ * Gets whether JK is being used and special handling of a jvmRoute
+ * portion of session ids is needed.
+ */
+ boolean getUseJK();
+
+ /**
+ * Gets the <code>DistributedCacheManager</code> through which we interact
+ * with the distributed cache.
+ */
+ DistributedCacheManager getDistributedCacheManager();
+}
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-10-23 18:15:21 UTC (rev 80002)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/ClusteredSession.java 2008-10-23 18:55:56 UTC (rev 80003)
@@ -19,18 +19,45 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package org.jboss.web.tomcat.service.session;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.security.AccessController;
import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
@@ -40,15 +67,21 @@
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
+import org.apache.catalina.Manager;
import org.apache.catalina.Session;
+import org.apache.catalina.SessionEvent;
+import org.apache.catalina.SessionListener;
+import org.apache.catalina.security.SecurityUtil;
import org.apache.catalina.session.StandardSession;
+import org.apache.catalina.session.StandardSessionFacade;
import org.apache.catalina.util.Enumerator;
import org.apache.catalina.util.StringManager;
import org.jboss.logging.Logger;
import org.jboss.metadata.web.jboss.ReplicationTrigger;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSession;
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionMetadata;
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionTimestamp;
-
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
import org.jboss.web.tomcat.service.session.notification.ClusteredSessionManagementStatus;
import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationCause;
import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationPolicy;
@@ -56,20 +89,24 @@
/**
* Abstract base class for session clustering based on StandardSession. Different session
- * replication strategy can be implemented such as session- or attribute-based ones.
+ * replication strategies can be implemented by subclasses.
*
* @author Ben Wang
* @author Brian Stansberry
*
* @version $Revision: 58585 $
*/
-public abstract class ClusteredSession
- extends StandardSession
+public abstract class ClusteredSession<D extends DistributedCacheManager>
+ implements HttpSession, Session, DistributableSession
{
+ // --------------------------------------------------------------- Constants
+
private static final long serialVersionUID = -758573655613558722L;
- protected static Logger log = Logger.getLogger(ClusteredSession.class);
- // ----------------------------------------------------- Instance Variables
+ protected static final boolean ACTIVITY_CHECK =
+ Globals.STRICT_SERVLET_COMPLIANCE
+ || Boolean.valueOf(System.getProperty("org.apache.catalina.session.StandardSession.ACTIVITY_CHECK", "false")).booleanValue();
+
/**
* Descriptive information describing this Session implementation.
*/
@@ -78,9 +115,7 @@
/**
* Set of attribute names which are not allowed to be replicated/persisted.
*/
- protected static final String[] excludedAttributes = {
- Globals.SUBJECT_ATTR
- };
+ protected static final String[] excludedAttributes = { Globals.SUBJECT_ATTR };
/**
* Set containing all members of {@link #excludedAttributes}.
@@ -95,57 +130,227 @@
}
replicationExcludes = Collections.unmodifiableSet(set);
}
+
+
+ /**
+ * The method signature for the <code>fireContainerEvent</code> method.
+ */
+ protected static final Class containerEventTypes[] = { String.class, Object.class };
- protected ReplicationTrigger invalidationPolicy;
+ protected static final Logger log = Logger.getLogger(ClusteredSession.class);
/**
+ * The dummy HTTP session context used for servlet spec compliance.
+ */
+ @SuppressWarnings("deprecation")
+ protected static javax.servlet.http.HttpSessionContext sessionContext = new javax.servlet.http.HttpSessionContext()
+ {
+ private final Map empty = Collections.EMPTY_MAP;
+
+ public Enumeration getIds()
+ {
+ return new Enumerator(empty);
+ }
+
+ public HttpSession getSession(String sessionId)
+ {
+ return null;
+ }
+ };
+
+ /**
+ * The string manager for this package.
+ */
+ protected static final StringManager sm =
+ StringManager.getManager(ClusteredSession.class.getPackage().getName());
+
+ // ----------------------------------------------------- Instance Variables
+
+ /**
+ * The collection of user data attributes associated with this Session.
+ */
+ private Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
+
+
+ /**
+ * The authentication type used to authenticate our cached Principal,
+ * if any. NOTE: This value is not included in the serialized
+ * version of this object.
+ */
+ private transient String authType = null;
+
+
+ /**
+ * The <code>java.lang.Method</code> for the
+ * <code>fireContainerEvent()</code> method of the
+ * <code>org.apache.catalina.core.StandardContext</code> method,
+ * if our Context implementation is of this class. This value is
+ * computed dynamically the first time it is needed, or after
+ * a session reload (since it is declared transient).
+ */
+ private transient Method containerEventMethod = null;
+
+
+ /**
+ * The time this session was created, in milliseconds since midnight,
+ * January 1, 1970 GMT.
+ */
+ private long creationTime = 0L;
+
+
+ /**
+ * We are currently processing a session expiration, so bypass
+ * certain IllegalStateException tests. NOTE: This value is not
+ * included in the serialized version of this object.
+ */
+ private transient boolean expiring = false;
+
+
+ /**
+ * The facade associated with this session. NOTE: This value is not
+ * included in the serialized version of this object.
+ */
+ private transient StandardSessionFacade facade = null;
+
+
+ /**
+ * The session identifier of this Session.
+ */
+ private String id = null;
+
+
+ /**
+ * The last accessed time for this Session.
+ */
+ private long lastAccessedTime = creationTime;
+
+
+ /**
+ * The session event listeners for this Session.
+ */
+ private transient ArrayList listeners = new ArrayList();
+
+
+ /**
+ * The Manager with which this Session is associated.
+ */
+ private transient ClusteredManager manager = null;
+
+ /**
+ * Our proxy to the distributed cache.
+ */
+ private transient D distributedCacheManager;
+
+ /**
+ * The maximum time interval, in seconds, between client requests before
+ * the servlet container may invalidate this session. A negative time
+ * indicates that the session should never time out.
+ */
+ private int maxInactiveInterval = -1;
+
+
+ /**
+ * Flag indicating whether this session is new or not.
+ */
+ private boolean isNew = false;
+
+
+ /**
+ * Flag indicating whether this session is valid or not.
+ */
+ private boolean isValid = false;
+
+
+ /**
+ * Internal notes associated with this session by Catalina components
+ * and event listeners. <b>IMPLEMENTATION NOTE:</b> This object is
+ * <em>not</em> saved and restored across session serializations!
+ */
+ private transient Map notes = new Hashtable();
+
+
+ /**
+ * The authenticated Principal associated with this session, if any.
+ * <b>IMPLEMENTATION NOTE:</b> This object is <i>not</i> saved and
+ * restored across session serializations!
+ */
+ private transient Principal principal = null;
+
+
+ /**
+ * The property change support for this component. NOTE: This value
+ * is not included in the serialized version of this object.
+ */
+ private transient PropertyChangeSupport support =
+ new PropertyChangeSupport(this);
+
+
+ /**
+ * The current accessed time for this session.
+ */
+ private long thisAccessedTime = creationTime;
+
+
+ /**
+ * The access count for this session.
+ */
+ private transient AtomicInteger accessCount = null;
+
+ /**
+ * Policy controlling whether reading/writing attributes requires
+ * replication.
+ */
+ private ReplicationTrigger invalidationPolicy;
+
+
+ /**
* If true, means the local in-memory session data contains metadata
* changes that have not been published to the distributed cache.
*/
- protected transient boolean sessionMetadataDirty;
+ private transient boolean sessionMetadataDirty;
/**
* If true, means the local in-memory session data contains attribute
* changes that have not been published to the distributed cache.
*/
- protected transient boolean sessionAttributesDirty;
+ private transient boolean sessionAttributesDirty;
/**
* Object wrapping thisAccessedTime. Create once and mutate so
* we can store it in JBoss Cache w/o concern that a transaction
* rollback will revert the cached ref to an older object.
*/
- protected transient DistributableSessionTimestamp timestamp = new DistributableSessionTimestamp();
+ private transient DistributableSessionTimestamp timestamp = new DistributableSessionTimestamp();
/**
* Object wrapping other metadata for this session. Create once and mutate so
* we can store it in JBoss Cache w/o concern that a transaction
* rollback will revert the cached ref to an older object.
*/
- protected transient DistributableSessionMetadata metadata = new DistributableSessionMetadata();
+ private transient DistributableSessionMetadata metadata = new DistributableSessionMetadata();
/**
* 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;
+ private transient int outdatedVersion;
/**
* The last time {@link #setIsOutdated setIsOutdated(true)} was called or
* <code>0</code> if <code>setIsOutdated(false)</code> was subsequently called.
*/
- protected transient long outdatedTime;
+ private transient long outdatedTime;
/**
* Version number to track cache invalidation. If any new version number is
* greater than this one, it means the data it holds is newer than this one.
*/
- protected int version;
+ private final AtomicInteger version = new AtomicInteger(0);
/**
* The session's id with any jvmRoute removed.
*/
- protected transient String realId;
+ private transient String realId;
/**
* Whether JK is being used, in which case our realId will
@@ -156,318 +361,456 @@
/**
* Timestamp when we were last replicated.
*/
- protected transient long lastReplicated;
+ private transient long lastReplicated;
/**
* Maximum number of milliseconds this session
* should be allowed to go unreplicated if access to the
* session doesn't mark it as dirty.
*/
- protected transient long maxUnreplicatedInterval;
+ private transient long maxUnreplicatedInterval;
/** True if maxUnreplicatedInterval is 0 or less than maxInactiveInterval */
- protected transient boolean alwaysReplicateTimestamp = true;
+ private transient boolean alwaysReplicateTimestamp = true;
/**
* Whether any of this session's attributes implement
* HttpSessionActivationListener.
*/
- protected transient Boolean hasActivationListener;
+ private transient Boolean hasActivationListener;
/**
* Has this session only been accessed once?
*/
- protected transient boolean firstAccess;
+ private transient boolean firstAccess;
/**
* Policy that drives whether we issue servlet spec notifications.
*/
- protected transient ClusteredSessionNotificationPolicy notificationPolicy;
+ private transient ClusteredSessionNotificationPolicy notificationPolicy;
- protected transient ClusteredSessionManagementStatus clusterStatus;
+ private transient ClusteredSessionManagementStatus clusterStatus;
/** True if a call to activate() is needed to offset a preceding passivate() call */
- protected transient boolean needsPostReplicateActivation;
+ private transient boolean needsPostReplicateActivation;
+
+
+ // ------------------------------------------------------------ Constructors
/**
- * The string manager for this package.
+ * Creates a new ClusteredSession.
+ *
+ * @param manager the manager for this session
*/
- protected static StringManager sm =
- StringManager.getManager(ClusteredSession.class.getPackage().getName());
+ protected ClusteredSession(ClusteredManager manager)
+ {
+ super();
+ this.manager = manager;
- protected ClusteredSession(AbstractJBossManager manager, ReplicationTrigger replicationTrigger, boolean useJK)
- {
- super(manager);
- invalidationPolicy = replicationTrigger;
- this.useJK = useJK;
+ // Initialize access count
+ if (ACTIVITY_CHECK) {
+ accessCount = new AtomicInteger();
+ }
+
+ invalidationPolicy = manager.getReplicationTrigger();
+ this.useJK = manager.getUseJK();
this.firstAccess = true;
- this.notificationPolicy = new LegacyClusteredSessionNotificationPolicy();
- checkAlwaysReplicateTimestamp();
+
+ int maxUnrep = manager.getMaxUnreplicatedInterval() * 1000;
+ setMaxUnreplicatedInterval(maxUnrep);
+ this.notificationPolicy = manager.getNotificationPolicy();
+ establishDistributedCacheManager();
}
+
+ // ---------------------------------------------------------------- Session
- /**
- * Check to see if the session data is still valid. Outdated here means
- * that the in-memory data is not in sync with one in the data store.
- *
- * @return
- */
- public boolean isOutdated()
+ public abstract String getInfo();
+
+ public String getAuthType()
{
- return thisAccessedTime < outdatedTime;
+ return (this.authType);
}
-
- public void clearOutdated()
+
+ public void setAuthType(String authType)
{
- // Only overwrite the access time if access() hasn't been called
- // since setOutdatedVersion() was called
- if (outdatedTime > thisAccessedTime)
- {
- lastAccessedTime = thisAccessedTime;
- thisAccessedTime = outdatedTime;
- }
- outdatedTime = 0;
-
- // Only overwrite the version if the outdated version is greater
- // Otherwise when we first unmarshal a session that has been
- // replicated many times, we will reset the version to 0
- if (outdatedVersion > version)
- version = outdatedVersion;
-
- outdatedVersion = 0;
+ String oldAuthType = this.authType;
+ this.authType = authType;
+ support.firePropertyChange("authType", oldAuthType, this.authType);
}
-
- public void updateAccessTimeFromOutdatedTime()
+
+ public long getCreationTime()
{
- if (outdatedTime > thisAccessedTime)
- {
- lastAccessedTime = thisAccessedTime;
- thisAccessedTime = outdatedTime;
- }
- outdatedTime = 0;
+ if (!isValidInternal())
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.getCreationTime.ise"));
+
+ return (this.creationTime);
}
-
+
/**
- * Gets the session id with any appended jvmRoute info removed.
+ * Set the creation time for this session. This method is called by the
+ * Manager when an existing Session instance is reused.
*
- * @see #getUseJK()
+ * @param time The new creation time
*/
- public String getRealId()
+ public void setCreationTime(long time)
{
- return realId;
+ this.creationTime = time;
+ this.lastAccessedTime = time;
+ this.thisAccessedTime = time;
+ sessionMetadataDirty();
}
- private void parseRealId(String sessionId)
+ public String getId()
{
- String newId = null;
- if (useJK)
- newId = Util.getRealId(sessionId);
- else
- newId = sessionId;
-
- // realId is used in a lot of map lookups, so only replace it
- // if the new id is actually different -- preserve object identity
- if (!newId.equals(realId))
- realId = newId;
+ return (this.id);
}
+ public String getIdInternal()
+ {
+ return (this.id);
+ }
+
/**
- * This is called specifically for failover case using mod_jk where the new
- * session has this node name in there. As a result, it is safe to just
- * replace the id since the backend store is using the "real" id
- * without the node name.
- *
- * @param id
+ * Overrides the superclass method to also set the
+ * {@link #getRealId() realId} property.
*/
- public void resetIdWithRouteInfo(String id)
+ public void setId(String id)
{
+ // Parse the real id first, as super.setId() calls add(),
+ // which depends on having the real id
+ parseRealId(id);
+
+ if ((this.id != null) && (manager != null))
+ manager.remove(this);
+
this.id = id;
- parseRealId(id);
+
+ this.clusterStatus = new ClusteredSessionManagementStatus(this.realId, true, null, null);
+
+ if (manager != null)
+ manager.add(this);
}
- public boolean getUseJK()
+ public long getLastAccessedTime()
{
- return useJK;
+ if (!isValidInternal()) {
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.getLastAccessedTime.ise"));
+ }
+
+ return (this.lastAccessedTime);
}
- public int getVersion()
+ public long getLastAccessedTimeInternal()
{
- return version;
+ return (this.lastAccessedTime);
}
- /**
- * Increment our version due to local changes.
- *
- * @return the new version
- */
- public int incrementVersionFromLocalActivity()
+ public Manager getManager()
{
- 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)
+ return (this.manager);
+ }
+
+ public void setManager(Manager manager)
{
- boolean outdated = this.version < version;
- if (outdated)
- {
- this.outdatedVersion = version;
- outdatedTime = System.currentTimeMillis();
- }
- return outdated;
+ if ((manager instanceof ClusteredManager) == false)
+ throw new IllegalArgumentException("manager must implement ClusteredManager");
+
+ this.manager = (ClusteredManager) manager;
}
+ public int getMaxInactiveInterval()
+ {
+ return (this.maxInactiveInterval);
+ }
+
/**
* Overrides the superclass to calculate
* {@link #getMaxUnreplicatedInterval() maxUnreplicatedInterval}.
*/
public void setMaxInactiveInterval(int interval)
{
- super.setMaxInactiveInterval(interval);
+ this.maxInactiveInterval = interval;
+ if (isValid && interval == 0)
+ {
+ expire();
+ }
checkAlwaysReplicateTimestamp();
sessionMetadataDirty();
}
- /**
- * Gets the time {@link #updateLastReplicated()} was last called, or
- * <code>0</code> if it has never been called.
- */
- public long getLastReplicated()
+ public Principal getPrincipal()
{
- return lastReplicated;
- }
-
+ return (this.principal);
+ }
+
/**
- * Sets the {@link #getLastReplicated() lastReplicated} field to
- * the current time.
+ * Set the authenticated Principal that is associated with this Session.
+ * This provides an <code>Authenticator</code> with a means to cache a
+ * previously authenticated Principal, and avoid potentially expensive
+ * <code>Realm.authenticate()</code> calls on every request.
+ *
+ * @param principal The new Principal, or <code>null</code> if none
*/
- public void updateLastReplicated()
+ public void setPrincipal(Principal principal)
{
- lastReplicated = System.currentTimeMillis();
+ Principal oldPrincipal = this.principal;
+ this.principal = principal;
+ support.firePropertyChange("principal", oldPrincipal, this.principal);
+
+ if ((oldPrincipal != null && !oldPrincipal.equals(principal)) ||
+ (oldPrincipal == null && principal != null))
+ sessionMetadataDirty();
}
- public long getMaxUnreplicatedInterval()
+ public void access()
{
- return maxUnreplicatedInterval;
+ this.lastAccessedTime = this.thisAccessedTime;
+ this.thisAccessedTime = System.currentTimeMillis();
+
+ if (ACTIVITY_CHECK) {
+ accessCount.incrementAndGet();
+ }
+
+ // JBAS-3528. If it's not the first access, make sure
+ // the 'new' flag is correct
+ if (!firstAccess && isNew)
+ {
+ setNew(false);
+ }
}
- public void setMaxUnreplicatedInterval(long interval)
+ public void endAccess()
{
- this.maxUnreplicatedInterval = Math.max(interval, -1);
- checkAlwaysReplicateTimestamp();
- }
-
- public boolean getMustReplicateTimestamp()
- {
- // If the access times are the same, access() was never called
- // on the session
- boolean touched = this.thisAccessedTime != this.lastAccessedTime;
- boolean exceeds = alwaysReplicateTimestamp && touched;
+ isNew = false;
+
+ if (ACTIVITY_CHECK) {
+ accessCount.decrementAndGet();
+ }
- if (!exceeds && touched && maxUnreplicatedInterval > 0) // -1 means ignore
+ this.lastAccessedTime = this.thisAccessedTime;
+
+ if (firstAccess)
{
- long unrepl = System.currentTimeMillis() - lastReplicated;
- exceeds = (unrepl >= maxUnreplicatedInterval);
- }
-
- return exceeds;
+ firstAccess = false;
+ // Tomcat marks the session as non new, but that's not really
+ // accurate per SRV.7.2, as the second request hasn't come in yet
+ // So, we fix that
+ isNew = true;
+ }
}
-
- public DistributableSessionTimestamp getSessionTimestamp()
+
+ public boolean isNew()
{
- this.timestamp.setTimestamp(this.thisAccessedTime);
- return this.timestamp;
+ if (!isValidInternal())
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.isNew.ise"));
+
+ return (this.isNew);
}
- public DistributableSessionMetadata getSessionMetadata()
+ public void setNew(boolean isNew)
{
- this.metadata.setId(id);
- this.metadata.setCreationTime(creationTime);
- this.metadata.setMaxInactiveInterval(maxInactiveInterval);
-// this.metadata.setInvalidationPolicy(invalidationPolicy);
- this.metadata.setNew(isNew);
- this.metadata.setValid(isValid);
+ this.isNew = isNew;
- return this.metadata;
+ // Don't replicate metadata just 'cause its the second request
+ // The only effect of this is if someone besides a request
+ // deserializes metadata from the distributed cache, this
+ // field may be out of date.
+ // If a request accesses the session, the access() call will
+ // set isNew=false, so the request will see the correct value
+ // sessionMetadataDirty();
}
-
- public Map<String, Object> getSessionAttributeMap()
+
+
+ /**
+ * Overrides the {@link StandardSession#isValid() superclass method}
+ * to call {@ #isValid(boolean) isValid(true)}.
+ */
+ public boolean isValid()
{
- return null;
+ return isValid(true);
}
- /**
- * Gets the sessions creation time, skipping any validity check.
- *
- * @return the creation time
- */
- public long getCreationTimeInternal()
+ public void setValid(boolean isValid)
{
- return creationTime;
+ this.isValid = isValid;
+ sessionMetadataDirty();
}
/**
- * This is called after loading a session to initialize the transient values.
+ * Invalidates this session and unbinds any objects bound to it.
+ * Overridden here to remove across the cluster instead of just expiring.
*
- * @param manager
- * @param cause the cause of the load
+ * @exception IllegalStateException if this method is called on
+ * an invalidated session
*/
- public abstract void initAfterLoad(AbstractJBossManager manager, ClusteredSessionNotificationCause cause);
+ public void invalidate()
+ {
+ if (!isValid())
+ throw new IllegalStateException(sm.getString("clusteredSession.invalidate.ise"));
- /**
- * Propogate session to the internal store.
- */
- public abstract void processSessionRepl();
+ // Cause this session to expire globally
+ boolean notify = true;
+ boolean localCall = true;
+ boolean localOnly = false;
+ expire(notify, localCall, localOnly, ClusteredSessionNotificationCause.INVALIDATE);
+ }
- /**
- * Remove myself from the internal store.
- */
- public abstract void removeMyself();
+ public void expire()
+ {
+ expire(true);
+ }
/**
- * Remove myself from the <t>local</t> internal store.
+ * Expires the session, but in such a way that other cluster nodes
+ * are unaware of the expiration.
+ *
+ * @param notify
*/
- public abstract void removeMyselfLocal();
+ public void expire(boolean notify)
+ {
+ boolean localCall = true;
+ boolean localOnly = true;
+ expire(notify, localCall, localOnly, ClusteredSessionNotificationCause.TIMEOUT);
+ }
+ public void recycle()
+ {
+ // Reset the instance variables associated with this Session
+ attributes.clear();
+ setAuthType(null);
+ creationTime = 0L;
+ expiring = false;
+ id = null;
+ lastAccessedTime = 0L;
+ maxInactiveInterval = -1;
+ notes.clear();
+ setPrincipal(null);
+ isNew = false;
+ isValid = false;
+ manager = null;
+
+ listeners.clear();
+ support = new PropertyChangeSupport(this);
+
+ invalidationPolicy = ReplicationTrigger.ACCESS;
+ outdatedTime = 0;
+ outdatedVersion = 0;
+ sessionAttributesDirty = false;
+ sessionMetadataDirty = false;
+ realId = null;
+ useJK = false;
+ version.set(0);
+ hasActivationListener = null;
+ lastReplicated = 0;
+ maxUnreplicatedInterval = 0;
+ alwaysReplicateTimestamp = true;
+ this.notificationPolicy = null;
+ this.clusterStatus = null;
+
+ distributedCacheManager = null;
+ }
+
+ public void addSessionListener(SessionListener listener)
+ {
+ listeners.add(listener);
+ }
- // ----------------------------------------------- Overridden Public Methods
+ public void removeSessionListener(SessionListener listener)
+ {
+ listeners.remove(listener);
+ }
- public void access()
+ public Object getNote(String name)
{
- super.access();
-
- // JBAS-3528. If it's not the first access, make sure
- // the 'new' flag is correct
- if (!firstAccess && isNew)
- {
- setNew(false);
- }
+ return (notes.get(name));
}
+
+ public Iterator getNoteNames()
+ {
+ return (notes.keySet().iterator());
+ }
+
+ public void setNote(String name, Object value)
+ {
+ notes.put(name, value);
+ }
+
+ public void removeNote(String name)
+ {
+ notes.remove(name);
+ }
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// public void removeNote(String name)
+// {
+// // FormAuthenticator removes the username and password because
+// // it assumes they are not needed if the Principal is cached,
+// // but they are needed if the session fails over, so ignore
+// // the removal request.
+// // TODO discuss this on Tomcat dev list to see if a better
+// // way of handling this can be found
+// if (Constants.SESS_USERNAME_NOTE.equals(name)
+// || Constants.SESS_PASSWORD_NOTE.equals(name))
+// {
+// if (log.isDebugEnabled())
+// {
+// log.debug("removeNote(): ignoring removal of note " + name);
+// }
+// }
+// else
+// {
+// super.removeNote(name);
+// }
+//
+// }
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// public void setNote(String name, Object value)
+// {
+// super.setNote(name, value);
+//
+// if (Constants.SESS_USERNAME_NOTE.equals(name)
+// || Constants.SESS_PASSWORD_NOTE.equals(name))
+// {
+// sessionIsDirty();
+// }
+// }
+
+ public HttpSession getSession()
+ {
+ if (facade == null){
+ if (SecurityUtil.isPackageProtectionEnabled()){
+ final HttpSession fsession = this;
+ facade = (StandardSessionFacade)AccessController.doPrivileged(new PrivilegedAction(){
+ public Object run(){
+ return new StandardSessionFacade(fsession);
+ }
+ });
+ } else {
+ facade = new StandardSessionFacade(this);
+ }
+ }
+ return (facade);
+ }
-
- public void endAccess()
+ // ------------------------------------------------------------ HttpSession
+
+ public ServletContext getServletContext()
{
- super.endAccess();
-
- this.lastAccessedTime = this.thisAccessedTime;
-
- if (firstAccess)
- {
- firstAccess = false;
- // Tomcat marks the session as non new, but that's not really
- // accurate per SRV.7.2, as the second request hasn't come in yet
- // So, we fix that
- isNew = true;
- }
+ if (manager == null)
+ return (null);
+ Context context = (Context) manager.getContainer();
+ if (context == null)
+ return (null);
+ else
+ return (context.getServletContext());
}
public Object getAttribute(String name)
{
-
if (!isValid())
throw new IllegalStateException
(sm.getString("clusteredSession.getAttribute.ise"));
@@ -499,7 +842,7 @@
}
// Validate our current state
- if (!isValid())
+ if (!isValidInternal())
{
throw new IllegalStateException
(sm.getString("clusteredSession.setAttribute.ise"));
@@ -511,14 +854,12 @@
(sm.getString("clusteredSession.setAttribute.iae"));
}
- ClusteredSessionNotificationPolicy policy = getNotificationPolicy();
-
// Construct an event with the new value
HttpSessionBindingEvent event = null;
// Call the valueBound() method if necessary
if (value instanceof HttpSessionBindingListener
- && policy.isHttpSessionBindingListenerInvocationAllowed(this.clusterStatus, ClusteredSessionNotificationCause.MODIFY, name, true))
+ && notificationPolicy.isHttpSessionBindingListenerInvocationAllowed(this.clusterStatus, ClusteredSessionNotificationCause.MODIFY, name, true))
{
event = new HttpSessionBindingEvent(getSession(), name, value);
try
@@ -527,17 +868,20 @@
}
catch (Throwable t)
{
- manager.getContainer().getLogger().error(sm.getString("standardSession.bindingEvent"), t);
+ manager.getContainer().getLogger().error(sm.getString("clusteredSession.bindingEvent"), t);
}
}
+
+ if (value instanceof HttpSessionActivationListener)
+ hasActivationListener = Boolean.TRUE;
// Replace or add this attribute
- Object unbound = setInternalAttribute(name, value);
+ Object unbound = setAttributeInternal(name, value);
// Call the valueUnbound() method if necessary
if ((unbound != null) && (unbound != value) &&
(unbound instanceof HttpSessionBindingListener) &&
- policy.isHttpSessionBindingListenerInvocationAllowed(this.clusterStatus, ClusteredSessionNotificationCause.MODIFY, name, true))
+ notificationPolicy.isHttpSessionBindingListenerInvocationAllowed(this.clusterStatus, ClusteredSessionNotificationCause.MODIFY, name, true))
{
try
{
@@ -546,12 +890,12 @@
}
catch (Throwable t)
{
- manager.getContainer().getLogger().error(sm.getString("standardSession.bindingEvent"), t);
+ manager.getContainer().getLogger().error(sm.getString("clusteredSession.bindingEvent"), t);
}
}
// Notify interested application event listeners
- if (policy.isHttpSessionAttributeListenerInvocationAllowed(this.clusterStatus, ClusteredSessionNotificationCause.MODIFY, name, true))
+ if (notificationPolicy.isHttpSessionAttributeListenerInvocationAllowed(this.clusterStatus, ClusteredSessionNotificationCause.MODIFY, name, true))
{
Context context = (Context) manager.getContainer();
Object lifecycleListeners[] = context.getApplicationEventListeners();
@@ -617,56 +961,426 @@
{
;
}
- manager.getContainer().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
+ manager.getContainer().getLogger().error(sm.getString("clusteredSession.attributeEvent"), t);
}
}
}
}
+ public void removeAttribute(String name)
+ {
+ // Validate our current state
+ if (!isValidInternal())
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.removeAttribute.ise"));
+
+ final boolean localCall = true;
+ final boolean localOnly = false;
+ final boolean notify = true;
+ removeAttributeInternal(name, localCall, localOnly, notify, ClusteredSessionNotificationCause.MODIFY);
+ }
+ @SuppressWarnings("deprecation")
+ public javax.servlet.http.HttpSessionContext getSessionContext()
+ {
+ return (sessionContext);
+ }
+
+ public Object getValue(String name)
+ {
+ return (getAttribute(name));
+ }
+
+ public String[] getValueNames()
+ {
+ if (!isValidInternal())
+ throw new IllegalStateException
+ (sm.getString("clusteredSession.getValueNames.ise"));
+
+ return (keys());
+ }
+
+ public void putValue(String name, Object value)
+ {
+ setAttribute(name, value);
+ }
+
+ public void removeValue(String name)
+ {
+ removeAttribute(name);
+ }
+
+ // ---------------------------------------------------- DistributableSession
+
+ public boolean getCreateRegionForSession()
+ {
+ // By default, regions are not needed. Subclasses that need them
+ // can override.
+ return false;
+ }
+
+ public boolean needRegionForSession()
+ {
+ // By default, regions are not needed. Subclasses that need them
+ // can override.
+ return false;
+ }
+
+ public boolean hasRegionForSession()
+ {
+ // By default, regions are not needed. Subclasses that need them
+ // can override.
+ return false;
+ }
+
+ public void createdRegionForSession()
+ {
+ // By default, regions are not needed. Subclasses that need them
+ // can override.
+ }
+
+ public int getVersion()
+ {
+ return version.get();
+ }
+
+ public boolean getMustReplicateTimestamp()
+ {
+ // If the access times are the same, access() was never called
+ // on the session
+ boolean touched = this.thisAccessedTime != this.lastAccessedTime;
+ boolean exceeds = alwaysReplicateTimestamp && touched;
+
+ if (!exceeds && touched && maxUnreplicatedInterval > 0) // -1 means ignore
+ {
+ long unrepl = System.currentTimeMillis() - lastReplicated;
+ exceeds = (unrepl >= maxUnreplicatedInterval);
+ }
+
+ return exceeds;
+ }
+
+ public DistributableSessionTimestamp getSessionTimestamp()
+ {
+ this.timestamp.setTimestamp(this.thisAccessedTime);
+ return this.timestamp;
+ }
+
+ public boolean isSessionMetadataDirty()
+ {
+ return sessionMetadataDirty;
+ }
+
+ public DistributableSessionMetadata getSessionMetadata()
+ {
+ this.metadata.setId(id);
+ this.metadata.setCreationTime(creationTime);
+ this.metadata.setMaxInactiveInterval(maxInactiveInterval);
+ this.metadata.setNew(isNew);
+ this.metadata.setValid(isValid);
+
+ return this.metadata;
+ }
+
+ public boolean isSessionAttributeMapDirty()
+ {
+ return sessionAttributesDirty;
+ }
+
/**
- * Returns whether the attribute's type is one that can be replicated.
+ * {@inheritDoc}
*
- * @param attribute the attribute
- * @return <code>true</code> if <code>attribute</code> is <code>null</code>,
- * <code>Serializable</code> or an array of primitives.
+ * @return this default impl always returns <code>null</code>
*/
- protected boolean canAttributeBeReplicated(Object attribute)
+ public Map<String, Object> getSessionAttributeMap()
{
- if (attribute instanceof Serializable || attribute == null)
- return true;
- Class clazz = attribute.getClass().getComponentType();
- return (clazz != null && clazz.isPrimitive());
+ return null;
}
+
+ public void update(Integer version, DistributableSessionTimestamp timestamp,
+ DistributableSessionMetadata metadata, Map attributes)
+ {
+ assert version != null : "version is null";
+ assert timestamp != null : "timestamp is null";
+ assert metadata != null : "metadata is null";
+
+ this.version.set(version.intValue());
+
+ this.lastAccessedTime = this.thisAccessedTime = timestamp.getTimestamp();
+ this.timestamp = timestamp;
+
+ this.id = metadata.getId();
+ this.creationTime = metadata.getCreationTime();
+ this.maxInactiveInterval = metadata.getMaxInactiveInterval();
+ this.isNew = metadata.isNew();
+ this.isValid = metadata.isValid();
+ this.metadata = metadata;
+
+ // Get our id without any jvmRoute appended
+ parseRealId(id);
+
+ // We no longer know if we have an activationListener
+ hasActivationListener = null;
+
+ // If the session has been replicated, any subsequent
+ // access cannot be the first.
+ this.firstAccess = false;
+
+ // We don't know when we last replicated our timestamp. We may be
+ // getting called due to activation, not deserialization after
+ // replication, so this.timestamp may be after the last replication.
+ // So use the creation time as a conservative guesstimate. Only downside
+ // is we may replicate a timestamp earlier than we need to, which is not
+ // a heavy cost.
+ this.lastReplicated = this.creationTime;
+
+ this.notificationPolicy = new LegacyClusteredSessionNotificationPolicy();
+
+ this.clusterStatus = new ClusteredSessionManagementStatus(this.realId, true, null, null);
+
+ checkAlwaysReplicateTimestamp();
+
+ // TODO uncomment when work on JBAS-1900 is completed
+// // Session notes -- for FORM auth apps, allow replicated session
+// // to be used without requiring a new login
+// // We use the superclass set/removeNote calls here to bypass
+// // the custom logic we've added
+// String username = (String) in.readObject();
+// if (username != null)
+// {
+// super.setNote(Constants.SESS_USERNAME_NOTE, username);
+// }
+// else
+// {
+// super.removeNote(Constants.SESS_USERNAME_NOTE);
+// }
+// String password = (String) in.readObject();
+// if (password != null)
+// {
+// super.setNote(Constants.SESS_PASSWORD_NOTE, password);
+// }
+// else
+// {
+// super.removeNote(Constants.SESS_PASSWORD_NOTE);
+// }
+
+ // we ignore the attributes
+ }
+ // ------------------------------------------------------------------ Public
+
/**
- * Invalidates this session and unbinds any objects bound to it.
- * Overridden here to remove across the cluster instead of just expiring.
+ * This is called after loading a session to initialize the transient values.
*
- * @exception IllegalStateException if this method is called on
- * an invalidated session
+ * @param manager
+ * @param cause the cause of the load
*/
- public void invalidate()
+ public void initAfterLoad(ClusteredManager manager, ClusteredSessionNotificationCause cause)
{
- if (!isValid())
- throw new IllegalStateException(sm.getString("clusteredSession.invalidate.ise"));
+ // Our manager, notification policy and proxy may have been lost
+ // if we were replicated, so reestablish them
+ this.manager = manager;
+ this.notificationPolicy = manager.getNotificationPolicy();
+ establishDistributedCacheManager();
- // Cause this session to expire globally
- boolean notify = true;
- boolean localCall = true;
- boolean localOnly = false;
- expire(notify, localCall, localOnly, ClusteredSessionNotificationCause.INVALIDATE);
+ // Since attribute map may be transient, we may need to populate it
+ // from the underlying store.
+ populateAttributes();
+
+ if (cause == ClusteredSessionNotificationCause.ACTIVATION)
+ {
+ this.needsPostReplicateActivation = true;
+ }
+
+ // Notify all attributes of type HttpSessionActivationListener (SRV 7.7.2)
+ this.notifyDidActivate(cause);
+
+ // We are no longer outdated vis a vis distributed cache
+ clearOutdated();
}
-
-
+
/**
- * Overrides the {@link StandardSession#isValid() superclass method}
- * to call {@ #isValid(boolean) isValid(true)}.
+ * Increment our version and propagate ourself to the distributed cache.
*/
- public boolean isValid()
+ public synchronized void processSessionReplication()
{
- return isValid(true);
+ // Replicate the session.
+ if (log.isTraceEnabled())
+ {
+ log.trace("processSessionRepl(): session is dirty. Will increment " +
+ "version from: " + getVersion() + " and replicate.");
+ }
+ version.incrementAndGet();
+ distributedCacheManager.putSession(getRealId(), this);
+
+ // Allow subclasses to replicate attributes if needed
+ replicateAttributes();
+
+ sessionAttributesDirty = false;
+ sessionMetadataDirty = false;
+
+ lastReplicated = System.currentTimeMillis();
}
+
+ /**
+ * Remove myself from the distributed cache.
+ */
+ public abstract void removeMyself();
+
+ /**
+ * Remove myself from the <t>local</t> instance of the distributed cache.
+ */
+ public abstract void removeMyselfLocal();
+
+ /**
+ * Gets the session id with any appended jvmRoute info removed.
+ *
+ * @see #getUseJK()
+ */
+ public String getRealId()
+ {
+ return realId;
+ }
+
+ /**
+ * Gets the sessions creation time, skipping any validity check.
+ *
+ * @return the creation time
+ */
+ public long getCreationTimeInternal()
+ {
+ return creationTime;
+ }
+
+ /**
+ * Gets the time {@link #processSessionReplication()} was last called, or
+ * <code>0</code> if it has never been called.
+ */
+ public long getLastReplicated()
+ {
+ return lastReplicated;
+ }
+
+ /**
+ * Gets the maximum period in ms after which a request accessing this
+ * session will trigger replication of its timestamp, even if the
+ * request doesn't otherwise modify the session. A value of -1 means
+ * no limit.
+ */
+ public long getMaxUnreplicatedInterval()
+ {
+ return maxUnreplicatedInterval;
+ }
+
+ /**
+ * Sets the maximum period in ms after which a request accessing this
+ * session will trigger replication of its timestamp, even if the
+ * request doesn't otherwise modify the session. A value of -1 means
+ * no limit.
+ */
+ public void setMaxUnreplicatedInterval(long interval)
+ {
+ this.maxUnreplicatedInterval = Math.max(interval, -1);
+ checkAlwaysReplicateTimestamp();
+ }
+
+ /**
+ * This is called specifically for failover case using mod_jk where the new
+ * session has this node name in there. As a result, it is safe to just
+ * replace the id since the backend store is using the "real" id
+ * without the node name.
+ *
+ * @param id
+ */
+ public void resetIdWithRouteInfo(String id)
+ {
+ this.id = id;
+ parseRealId(id);
+ }
+
+ /**
+ * Gets whether the session expects to be accessible via mod_jk, mod_proxy,
+ * mod_cluster or other such AJP-based load balancers.
+ */
+ public boolean getUseJK()
+ {
+ return useJK;
+ }
+
+ /**
+ * 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 = getVersion() < version;
+ if (outdated)
+ {
+ this.outdatedVersion = version;
+ outdatedTime = System.currentTimeMillis();
+ }
+ return outdated;
+ }
+
+ /**
+ * Check to see if the session data is still valid. Outdated here means
+ * that the in-memory data is not in sync with one in the data store.
+ *
+ * @return
+ */
+ public boolean isOutdated()
+ {
+ return thisAccessedTime < outdatedTime;
+ }
+
+ public boolean isSessionDirty()
+ {
+ return sessionAttributesDirty || sessionMetadataDirty;
+ }
+
+ /** Inform any HttpSessionListener of the creation of this session */
+ public void tellNew(ClusteredSessionNotificationCause cause)
+ {
+ // Notify interested session event listeners
+ fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
+
+ // Notify interested application event listeners
+ if (notificationPolicy.isHttpSessionListenerInvocationAllowed(this.clusterStatus, cause, true))
+ {
+ Context context = (Context) manager.getContainer();
+ Object lifecycleListeners[] = context.getApplicationLifecycleListeners();
+ if (lifecycleListeners != null)
+ {
+ HttpSessionEvent event = new HttpSessionEvent(getSession());
+ for (int i = 0; i < lifecycleListeners.length; i++)
+ {
+ if (!(lifecycleListeners[i] instanceof HttpSessionListener))
+ continue;
+ HttpSessionListener listener = (HttpSessionListener) lifecycleListeners[i];
+ try
+ {
+ fireContainerEvent(context, "beforeSessionCreated", listener);
+ listener.sessionCreated(event);
+ fireContainerEvent(context, "afterSessionCreated", listener);
+ }
+ catch (Throwable t)
+ {
+ try
+ {
+ fireContainerEvent(context, "afterSessionCreated", listener);
+ }
+ catch (Exception e)
+ {
+ ;
+ }
+ manager.getContainer().getLogger().error(sm.getString("clusteredSession.sessionEvent"), t);
+ }
+ }
+ }
+ }
+ }
/**
* Returns whether the current session is still valid, but
@@ -711,19 +1425,6 @@
}
/**
- * Expires the session, but in such a way that other cluster nodes
- * are unaware of the expiration.
- *
- * @param notify
- */
- public void expire(boolean notify)
- {
- boolean localCall = true;
- boolean localOnly = true;
- expire(notify, localCall, localOnly, ClusteredSessionNotificationCause.TIMEOUT);
- }
-
- /**
* Expires the session, notifying listeners and possibly the manager.
* <p>
* <strong>NOTE:</strong> The manager will only be notified of the expiration
@@ -747,7 +1448,8 @@
* <code>true</code>.
* @param cause the cause of the expiration
*/
- public void expire(boolean notify, boolean localCall, boolean localOnly, ClusteredSessionNotificationCause cause)
+ public void expire(boolean notify, boolean localCall, boolean localOnly,
+ ClusteredSessionNotificationCause cause)
{
if (log.isTraceEnabled())
{
@@ -777,7 +1479,7 @@
Object lifecycleListeners[] = context.getApplicationLifecycleListeners();
if (notify
&& (lifecycleListeners != null)
- && getNotificationPolicy().isHttpSessionListenerInvocationAllowed(this.clusterStatus, cause, localCall))
+ && notificationPolicy.isHttpSessionListenerInvocationAllowed(this.clusterStatus, cause, localCall))
{
HttpSessionEvent event =
new HttpSessionEvent(getSession());
@@ -810,7 +1512,7 @@
{
;
}
- manager.getContainer().getLogger().error(sm.getString("standardSession.sessionEvent"), t);
+ manager.getContainer().getLogger().error(sm.getString("clusteredSession.sessionEvent"), t);
}
}
}
@@ -828,10 +1530,17 @@
// JBAS-1360 -- Unbind any objects associated with this session
String keys[] = keys();
for (int i = 0; i < keys.length; i++)
+ {
removeAttributeInternal(keys[i], localCall, localOnly, notify, cause);
+ }
// Remove this session from our manager's active sessions
- removeFromManager(localCall, localOnly);
+ // If !localCall, this expire call came from the manager,
+ // so don't recurse
+ if (localCall)
+ {
+ removeFromManager(localOnly);
+ }
// We have completed expire of this session
setValid(false);
@@ -839,36 +1548,15 @@
}
}
-
+
/**
- * Advise our manager to remove this expired session.
+ * Inform any HttpSessionActivationListener that the session will passivate.
*
- * @param localCall whether this call originated from local activity
- * or from a remote invalidation. In this default
- * implementation, this parameter is ignored.
- * @param localOnly whether the rest of the cluster should be made aware
- * of the removal
+ * @param cause cause of the notification (e.g. {@link ClusteredSessionNotificationCause#REPLICATION}
+ * or {@link ClusteredSessionNotificationCause#PASSIVATION}
*/
- protected void removeFromManager(boolean localCall, boolean localOnly)
+ public void notifyWillPassivate(ClusteredSessionNotificationCause cause)
{
- if(localOnly)
- {
- ((AbstractJBossManager) manager).removeLocal(this);
- }
- else
- {
- manager.remove(this);
- }
- }
-
- @Override
- public void passivate()
- {
- passivate(ClusteredSessionNotificationCause.PASSIVATION);
- }
-
- public void passivate(ClusteredSessionNotificationCause cause)
- {
// Notify interested session event listeners
fireSessionEvent(Session.SESSION_PASSIVATED_EVENT, null);
@@ -876,8 +1564,6 @@
{
boolean hasListener = false;
- ClusteredSessionNotificationPolicy policy = getNotificationPolicy();
-
// Notify ActivationListeners
HttpSessionEvent event = null;
String keys[] = keys();
@@ -889,7 +1575,7 @@
{
hasListener = true;
- if (policy.isHttpSessionActivationListenerInvocationAllowed(this.clusterStatus, cause, keys[i]))
+ if (notificationPolicy.isHttpSessionActivationListenerInvocationAllowed(this.clusterStatus, cause, keys[i]))
{
if (event == null)
event = new HttpSessionEvent(getSession());
@@ -916,13 +1602,14 @@
}
}
- public void activate()
+ /**
+ * Inform any HttpSessionActivationListener that the session has been activated.
+ *
+ * @param cause cause of the notification (e.g. {@link ClusteredSessionNotificationCause#REPLICATION}
+ * or {@link ClusteredSessionNotificationCause#PASSIVATION}
+ */
+ public void notifyDidActivate(ClusteredSessionNotificationCause cause)
{
- activate(ClusteredSessionNotificationCause.ACTIVATION);
- }
-
- public void activate(ClusteredSessionNotificationCause cause)
- {
// Notify interested session event listeners
fireSessionEvent(Session.SESSION_ACTIVATED_EVENT, null);
@@ -932,8 +1619,6 @@
boolean hasListener = false;
- ClusteredSessionNotificationPolicy policy = getNotificationPolicy();
-
HttpSessionEvent event = null;
String keys[] = keys();
Map attrs = getAttributesInternal();
@@ -944,7 +1629,7 @@
{
hasListener = true;
- if (policy.isHttpSessionActivationListenerInvocationAllowed(this.clusterStatus, cause, keys[i]))
+ if (notificationPolicy.isHttpSessionActivationListenerInvocationAllowed(this.clusterStatus, cause, keys[i]))
{
if (event == null)
event = new HttpSessionEvent(getSession());
@@ -970,214 +1655,74 @@
}
}
+ /**
+ * Gets whether the session needs to notify HttpSessionActivationListeners
+ * that it has been activated following replication.
+ */
public boolean getNeedsPostReplicateActivation()
{
return needsPostReplicateActivation;
}
- // TODO uncomment when work on JBAS-1900 is completed
-// public void removeNote(String name)
-// {
-// // FormAuthenticator removes the username and password because
-// // it assumes they are not needed if the Principal is cached,
-// // but they are needed if the session fails over, so ignore
-// // the removal request.
-// // TODO discuss this on Tomcat dev list to see if a better
-// // way of handling this can be found
-// if (Constants.SESS_USERNAME_NOTE.equals(name)
-// || Constants.SESS_PASSWORD_NOTE.equals(name))
-// {
-// if (log.isDebugEnabled())
-// {
-// log.debug("removeNote(): ignoring removal of note " + name);
-// }
-// }
-// else
-// {
-// super.removeNote(name);
-// }
-//
-// }
+ @Override
+ public String toString()
+ {
+ return new StringBuilder(getClass().getSimpleName())
+ .append('[').append("id: ").append(id)
+ .append(" lastAccessedTime: ").append(lastAccessedTime)
+ .append(" version: ").append(version)
+ .append(" lastOutdated: ").append(outdatedTime).append(']')
+ .toString();
+ }
- // TODO uncomment when work on JBAS-1900 is completed
-// public void setNote(String name, Object value)
-// {
-// super.setNote(name, value);
-//
-// if (Constants.SESS_USERNAME_NOTE.equals(name)
-// || Constants.SESS_PASSWORD_NOTE.equals(name))
-// {
-// sessionIsDirty();
-// }
-// }
+ // ----------------------------------------------------- Protected Methods
- /**
- * Override the superclass to additionally reset this class' fields.
- * <p>
- * <strong>NOTE:</strong> It is not anticipated that this method will be
- * called on a ClusteredSession, but we are overriding the method to be
- * thorough.
- * </p>
- */
- public void recycle()
- {
- super.recycle();
-
- // Fields that the superclass isn't clearing
- listeners.clear();
- support = new PropertyChangeSupport(this);
-
- invalidationPolicy = ReplicationTrigger.ACCESS;
- outdatedTime = 0;
- outdatedVersion = 0;
- sessionAttributesDirty = false;
- sessionMetadataDirty = false;
- realId = null;
- useJK = false;
- version = 0;
- hasActivationListener = null;
- lastReplicated = 0;
- maxUnreplicatedInterval = 0;
- alwaysReplicateTimestamp = true;
- this.notificationPolicy = null;
- this.clusterStatus = null;
- }
+ protected abstract Object getAttributeInternal(String name);
+
+ protected abstract Object setAttributeInternal(String name, Object value);
+
+ protected abstract Object removeAttributeInternal(String name, boolean localCall, boolean localOnly);
/**
- * Set the creation time for this session. This method is called by the
- * Manager when an existing Session instance is reused.
- *
- * @param time The new creation time
+ * Extension point for subclasses to load the attribute map from the
+ * distributed cache.
*/
- public void setCreationTime(long time)
- {
- super.setCreationTime(time);
- sessionMetadataDirty();
- }
+ protected abstract void populateAttributes();
- /**
- * Overrides the superclass method to also set the
- * {@link #getRealId() realId} property.
- */
- public void setId(String id)
+ protected final Map<String, Object> getAttributesInternal()
{
- // Parse the real id first, as super.setId() calls add(),
- // which depends on having the real id
- parseRealId(id);
-
- // TODO -- should we bypass this if realId hasn't changed? We're removing
- // and readding every time we fail over, when all we want is a
- // jvmRoute change to the session id
-
- if ((this.id != null) && (manager != null))
- manager.remove(this);
-
- this.id = id;
-
- this.clusterStatus = new ClusteredSessionManagementStatus(this.realId, true, null, null);
-
- if (manager != null)
- manager.add(this);
+ return attributes;
}
-
-
- /**
- * Set the authenticated Principal that is associated with this Session.
- * This provides an <code>Authenticator</code> with a means to cache a
- * previously authenticated Principal, and avoid potentially expensive
- * <code>Realm.authenticate()</code> calls on every request.
- *
- * @param principal The new Principal, or <code>null</code> if none
- */
- public void setPrincipal(Principal principal)
+ protected final ClusteredManager getManagerInternal()
{
-
- Principal oldPrincipal = this.principal;
- this.principal = principal;
- support.firePropertyChange("principal", oldPrincipal, this.principal);
-
- if ((oldPrincipal != null && !oldPrincipal.equals(principal)) ||
- (oldPrincipal == null && principal != null))
- sessionMetadataDirty();
-
+ return manager;
}
- public void setNew(boolean isNew)
+ protected final D getDistributedCacheManager()
{
- super.setNew(isNew);
- // Don't replicate metadata just 'cause its the second request
- // The only effect of this is if someone besides a request
- // deserializes metadata from the distributed cache, this
- // field may be out of date.
- // If a request accesses the session, the access() call will
- // set isNew=false, so the request will see the correct value
- // sessionMetadataDirty();
+ return distributedCacheManager;
}
- public void setValid(boolean isValid)
+ protected final void setDistributedCacheManager(D distributedCacheManager)
{
- super.setValid(isValid);
- sessionMetadataDirty();
- }
+ this.distributedCacheManager = distributedCacheManager;
+ }
- @Override
- public void tellNew()
+ /**
+ * Returns whether the attribute's type is one that can be replicated.
+ *
+ * @param attribute the attribute
+ * @return <code>true</code> if <code>attribute</code> is <code>null</code>,
+ * <code>Serializable</code> or an array of primitives.
+ */
+ protected boolean canAttributeBeReplicated(Object attribute)
{
- tellNew(ClusteredSessionNotificationCause.CREATE);
+ if (attribute instanceof Serializable || attribute == null)
+ return true;
+ Class clazz = attribute.getClass().getComponentType();
+ return (clazz != null && clazz.isPrimitive());
}
-
- public void tellNew(ClusteredSessionNotificationCause cause)
- {
- // Notify interested session event listeners
- fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
-
- // Notify interested application event listeners
- if (getNotificationPolicy().isHttpSessionListenerInvocationAllowed(this.clusterStatus, cause, true))
- {
- Context context = (Context) manager.getContainer();
- Object lifecycleListeners[] = context.getApplicationLifecycleListeners();
- if (lifecycleListeners != null)
- {
- HttpSessionEvent event = new HttpSessionEvent(getSession());
- for (int i = 0; i < lifecycleListeners.length; i++)
- {
- if (!(lifecycleListeners[i] instanceof HttpSessionListener))
- continue;
- HttpSessionListener listener = (HttpSessionListener) lifecycleListeners[i];
- try
- {
- fireContainerEvent(context, "beforeSessionCreated", listener);
- listener.sessionCreated(event);
- fireContainerEvent(context, "afterSessionCreated", listener);
- }
- catch (Throwable t)
- {
- try
- {
- fireContainerEvent(context, "afterSessionCreated", listener);
- }
- catch (Exception e)
- {
- ;
- }
- manager.getContainer().getLogger().error(sm.getString("standardSession.sessionEvent"), t);
- }
- }
- }
- }
- }
-
- public String toString()
- {
- StringBuffer buf = new StringBuffer();
- buf.append("id: " +id).append(" lastAccessedTime: " +lastAccessedTime).append(
- " version: " +version).append(" lastOutdated: " + outdatedTime);
-
- return buf.toString();
- }
-
- // ----------------------------------------------------- Protected Methods
/**
* Removes any attribute whose name is found in {@link #excludedAttributes}
@@ -1190,7 +1735,7 @@
* <code>attributes</code>, or <code>null</code> if no attributes
* were removed.
*/
- protected static Map removeExcludedAttributes(Map attributes)
+ protected final Map removeExcludedAttributes(Map attributes)
{
Map excluded = null;
for (int i = 0; i < excludedAttributes.length; i++) {
@@ -1213,111 +1758,95 @@
return excluded;
}
- protected ClusteredSessionNotificationPolicy getNotificationPolicy()
+ protected final boolean isGetDirty(Object attribute)
{
- return notificationPolicy;
+ boolean result = false;
+ switch (invalidationPolicy)
+ {
+ case SET_AND_GET:
+ result = true;
+ break;
+ case SET_AND_NON_PRIMITIVE_GET:
+ result = isMutable(attribute);
+ break;
+ default:
+ // result is false
+ }
+ return result;
}
-
- protected void setNotificationPolicy(ClusteredSessionNotificationPolicy notificationPolicy)
- {
- this.notificationPolicy = notificationPolicy;
- }
- public void update(Integer version, DistributableSessionTimestamp timestamp,
- DistributableSessionMetadata metadata, Map attributes)
+ protected boolean isMutable(Object attribute)
{
- assert version != null : "version is null";
- assert timestamp != null : "timestamp is null";
- assert metadata != null : "metadata is null";
-
- this.version = version.intValue();
-
- this.lastAccessedTime = this.thisAccessedTime = timestamp.getTimestamp();
- this.timestamp = timestamp;
-
- this.id = metadata.getId();
- this.creationTime = metadata.getCreationTime();
- this.maxInactiveInterval = metadata.getMaxInactiveInterval();
- this.isNew = metadata.isNew();
- this.isValid = metadata.isValid();
-// this.invalidationPolicy = metadata.getInvalidationPolicy();
- this.metadata = metadata;
-
- // Get our id without any jvmRoute appended
- parseRealId(id);
-
- // We no longer know if we have an activationListener
- hasActivationListener = null;
-
- // If the session has been replicated, any subsequent
- // access cannot be the first.
- this.firstAccess = false;
-
- // We don't know when we last replicated our timestamp. We may be
- // getting called due to activation, not deserialization after
- // replication, so this.timestamp may be after the last replication.
- // So use the creation time as a conservative guesstimate. Only downside
- // is we may replicate a timestamp earlier than we need to, which is not
- // a heavy cost.
- this.lastReplicated = this.creationTime;
-
- this.notificationPolicy = new LegacyClusteredSessionNotificationPolicy();
-
- this.clusterStatus = new ClusteredSessionManagementStatus(this.realId, true, null, null);
-
- checkAlwaysReplicateTimestamp();
-
- // TODO uncomment when work on JBAS-1900 is completed
-// // Session notes -- for FORM auth apps, allow replicated session
-// // to be used without requiring a new login
-// // We use the superclass set/removeNote calls here to bypass
-// // the custom logic we've added
-// String username = (String) in.readObject();
-// if (username != null)
-// {
-// super.setNote(Constants.SESS_USERNAME_NOTE, username);
-// }
-// else
-// {
-// super.removeNote(Constants.SESS_USERNAME_NOTE);
-// }
-// String password = (String) in.readObject();
-// if (password != null)
-// {
-// super.setNote(Constants.SESS_PASSWORD_NOTE, password);
-// }
-// else
-// {
-// super.removeNote(Constants.SESS_PASSWORD_NOTE);
-// }
-
- // we ignore the attributes
- }
+ return attribute != null &&
+ !(attribute instanceof String ||
+ attribute instanceof Number ||
+ attribute instanceof Character ||
+ attribute instanceof Boolean);
+ }
- // -------------------------------------- Internal protected method override
-
/**
- * Method inherited from Tomcat. Return zero-length based string if not found.
+ * Gets a reference to the JBossCacheService.
*/
- protected String[] keys()
+ protected void establishDistributedCacheManager()
{
- return ((String[]) getAttributesInternal().keySet().toArray(EMPTY_ARRAY));
+ if (distributedCacheManager == null)
+ {
+ distributedCacheManager = (D) getManagerInternal().getDistributedCacheManager();
+
+ // still null???
+ if (distributedCacheManager == null)
+ {
+ throw new RuntimeException("DistributedCacheManager is null.");
+ }
+ }
}
-
+
/**
- * Called by super.removeAttribute().
- *
- * @param name the attribute name
- * @param notify <code>true</code> if listeners should be notified
+ * Extension point for subclasses to handle replication of attributes.
+ * This default implementation does nothing.
*/
- @Override
- protected void removeAttributeInternal(String name, boolean notify)
+ protected void replicateAttributes()
{
- boolean localCall = true;
- boolean localOnly = false;
- removeAttributeInternal(name, localCall, localOnly, notify, ClusteredSessionNotificationCause.MODIFY);
+ // no-op
}
+
+ protected final void sessionAttributesDirty()
+ {
+ if (!sessionAttributesDirty && log.isTraceEnabled())
+ log.trace("Marking session attributes dirty " + id);
+
+ sessionAttributesDirty = true;
+ }
+ protected final void setHasActivationListener(boolean hasListener)
+ {
+ this.hasActivationListener = Boolean.valueOf(hasListener);
+ }
+
+ // ----------------------------------------------------------------- Private
+
+ private void checkAlwaysReplicateTimestamp()
+ {
+ this.alwaysReplicateTimestamp =
+ (maxUnreplicatedInterval == 0
+ || (maxUnreplicatedInterval > 0 && maxInactiveInterval >= 0
+ && maxUnreplicatedInterval > (maxInactiveInterval * 1000)));
+ }
+
+ private void parseRealId(String sessionId)
+ {
+ String newId = null;
+ if (useJK)
+ newId = Util.getRealId(sessionId);
+ else
+ newId = sessionId;
+
+ // realId is used in a lot of map lookups, so only replace it
+ // if the new id is actually different -- preserve object identity
+ if (!newId.equals(realId))
+ realId = newId;
+ }
+
/**
* Remove the attribute from the local cache and possibly the distributed
* cache, plus notify any listeners
@@ -1333,14 +1862,14 @@
* @param notify <code>true</code> if listeners should be notified
* @param cause the cause of the removal
*/
- protected void removeAttributeInternal(String name,
+ private void removeAttributeInternal(String name,
boolean localCall,
boolean localOnly,
boolean notify,
ClusteredSessionNotificationCause cause)
{
// Remove this attribute from our collection
- Object value = removeJBossInternalAttribute(name, localCall, localOnly);
+ Object value = removeAttributeInternal(name, localCall, localOnly);
// Do we need to do valueUnbound() and attributeRemoved() notification?
if (!notify || (value == null))
@@ -1348,19 +1877,17 @@
return;
}
- ClusteredSessionNotificationPolicy policy = getNotificationPolicy();
-
// Call the valueUnbound() method if necessary
HttpSessionBindingEvent event = null;
if (value instanceof HttpSessionBindingListener
- && policy.isHttpSessionBindingListenerInvocationAllowed(this.clusterStatus, cause, name, localCall))
+ && notificationPolicy.isHttpSessionBindingListenerInvocationAllowed(this.clusterStatus, cause, name, localCall))
{
event = new HttpSessionBindingEvent(getSession(), name, value);
((HttpSessionBindingListener) value).valueUnbound(event);
}
// Notify interested application event listeners
- if (policy.isHttpSessionAttributeListenerInvocationAllowed(this.clusterStatus, cause, name, localCall))
+ if (notificationPolicy.isHttpSessionAttributeListenerInvocationAllowed(this.clusterStatus, cause, name, localCall))
{
Context context = (Context) manager.getContainer();
Object lifecycleListeners[] = context.getApplicationEventListeners();
@@ -1399,110 +1926,129 @@
{
;
}
- manager.getContainer().getLogger().error(sm.getString("standardSession.attributeEvent"), t);
+ manager.getContainer().getLogger().error(sm.getString("clusteredSession.attributeEvent"), t);
}
}
}
-
}
-
- protected Object getAttributeInternal(String name)
+
+ private String[] keys()
{
- return getJBossInternalAttribute(name);
+ Set keySet = getAttributesInternal().keySet();
+ return ((String[]) keySet.toArray(new String[keySet.size()]));
}
- protected Map getAttributesInternal()
+ /**
+ * Return the <code>isValid</code> flag for this session without any expiration
+ * check.
+ */
+ private boolean isValidInternal()
{
- return getJBossInternalAttributes();
+ return (this.isValid || this.expiring);
}
-
- protected Object setInternalAttribute(String name, Object value)
+
+ /**
+ * Fire container events if the Context implementation is the
+ * <code>org.apache.catalina.core.StandardContext</code>.
+ *
+ * @param context Context for which to fire events
+ * @param type Event type
+ * @param data Event data
+ *
+ * @exception Exception occurred during event firing
+ */
+ private void fireContainerEvent(Context context,
+ String type, Object data)
+ throws Exception
{
- if (value instanceof HttpSessionActivationListener)
- hasActivationListener = Boolean.TRUE;
-
- return setJBossInternalAttribute(name, value);
- }
- // ------------------------------------------ JBoss internal abstract method
+ if (!"org.apache.catalina.core.StandardContext".equals
+ (context.getClass().getName()))
+ {
+ return; // Container events are not supported
+ }
+ // NOTE: Race condition is harmless, so do not synchronize
+ if (containerEventMethod == null)
+ {
+ containerEventMethod =
+ context.getClass().getMethod("fireContainerEvent",
+ containerEventTypes);
+ }
+ Object containerEventParams[] = new Object[2];
+ containerEventParams[0] = type;
+ containerEventParams[1] = data;
+ containerEventMethod.invoke(context, containerEventParams);
- protected abstract Object getJBossInternalAttribute(String name);
+ }
- protected abstract Object removeJBossInternalAttribute(String name, boolean localCall, boolean localOnly);
+ /**
+ * Notify all session event listeners that a particular event has
+ * occurred for this Session. The default implementation performs
+ * this notification synchronously using the calling thread.
+ *
+ * @param type Event type
+ * @param data Event data
+ */
+ private void fireSessionEvent(String type, Object data)
+ {
+ if (listeners.size() < 1)
+ return;
+ SessionEvent event = new SessionEvent(this, type, data);
+ SessionListener list[] = new SessionListener[0];
+ synchronized (listeners)
+ {
+ list = (SessionListener[]) listeners.toArray(list);
+ }
- protected abstract Map getJBossInternalAttributes();
+ for (int i = 0; i < list.length; i++)
+ {
+ ((SessionListener) list[i]).sessionEvent(event);
+ }
- protected abstract Object setJBossInternalAttribute(String name, Object value);
-
- // ------------------------------------------------ Session Package Methods
-
- protected void sessionAttributesDirty()
- {
- if (!sessionAttributesDirty && log.isTraceEnabled())
- log.trace("Marking session attributes dirty " + id);
-
- sessionAttributesDirty = true;
}
- protected void sessionMetadataDirty()
+ private void sessionMetadataDirty()
{
if (!sessionMetadataDirty && !isNew && log.isTraceEnabled())
log.trace("Marking session metadata dirty " + id);
sessionMetadataDirty = true;
}
- public boolean isSessionMetadataDirty()
+ /**
+ * Advise our manager to remove this expired session.
+ * @param localOnly whether the rest of the cluster should be made aware
+ * of the removal
+ */
+ private void removeFromManager(boolean localOnly)
{
- return sessionMetadataDirty;
+ if(localOnly)
+ {
+ manager.removeLocal(this);
+ }
+ else
+ {
+ manager.remove(this);
+ }
}
- public boolean isSessionDirty()
+ private final void clearOutdated()
{
- return sessionAttributesDirty || sessionMetadataDirty;
- }
-
- public boolean isSessionAttributeMapDirty()
- {
- return sessionAttributesDirty;
- }
-
- public boolean getCreateRegionForSession()
- {
- return false;
- }
-
- protected boolean isGetDirty(Object attribute)
- {
- boolean result = false;
- switch (invalidationPolicy)
+ // Only overwrite the access time if access() hasn't been called
+ // since setOutdatedVersion() was called
+ if (outdatedTime > thisAccessedTime)
{
- case SET_AND_GET:
- result = true;
- break;
- case SET_AND_NON_PRIMITIVE_GET:
- result = isMutable(attribute);
- break;
- default:
- // result is false
+ lastAccessedTime = thisAccessedTime;
+ thisAccessedTime = outdatedTime;
}
- return result;
+ outdatedTime = 0;
+
+ // Only overwrite the version if the outdated version is greater
+ // Otherwise when we first unmarshall a session that has been
+ // replicated many times, we will reset the version to 0
+ if (outdatedVersion > version.get())
+ version.set(outdatedVersion);
+
+ outdatedVersion = 0;
}
-
- protected boolean isMutable(Object attribute)
- {
- return attribute != null &&
- !(attribute instanceof String ||
- attribute instanceof Number ||
- attribute instanceof Character ||
- attribute instanceof Boolean);
- }
-
- private void checkAlwaysReplicateTimestamp()
- {
- this.alwaysReplicateTimestamp =
- (maxUnreplicatedInterval == 0
- || (maxUnreplicatedInterval > 0 && maxInactiveInterval >= 0
- && maxUnreplicatedInterval > (maxInactiveInterval * 1000)));
- }
-}
+}
\ No newline at end of file
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java 2008-10-23 18:15:21 UTC (rev 80002)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java 2008-10-23 18:55:56 UTC (rev 80003)
@@ -22,8 +22,6 @@
package org.jboss.web.tomcat.service.session;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
@@ -49,18 +47,7 @@
* required to be aspectized. That is, you can't simply declare them as Serializable. This is restricted
* because of the marshalling/unmarshalling issue.</p>
*
- * <p>We use JBossCache for our internal, replicated data store.
- * The internal structure is like in JBossCache:
- * <pre>
- * /JSESSION
- * /hostname
- * /web_app_path (path + session id is unique)
- * /id Map(id, session)
- * (VERSION, version)
- * /ATTRIBUTE Map(can be empty)
- * /pojo Map(field name, field value) (pojo naming is by field.getName())
- *
- * </pre>
+ * <p>We use PojoCache for our internal, replicated data store.
* <p/>
*
* @author Ben Wang
@@ -69,7 +56,7 @@
* @version $Revision: 58586 $
*/
class FieldBasedClusteredSession
- extends JBossCacheClusteredSession
+ extends ClusteredSession<FieldBasedDistributedCacheManager>
{
/** The serialVersionUID */
private static final long serialVersionUID = 8347544395334247623L;
@@ -79,18 +66,27 @@
*/
protected static final String info = "FieldBasedClusteredSession/1.0";
- protected transient Map attributes_ = Collections.synchronizedMap(new HashMap());
- protected transient FieldBasedDistributedCacheManager fieldProxy_;
protected transient boolean needRegion_ = true;
+
- public FieldBasedClusteredSession(JBossCacheManager manager)
+ // ------------------------------------------------------------ Constructors
+
+
+ public FieldBasedClusteredSession(ClusteredManager manager)
{
super(manager);
}
+
// ----------------------------------------------- Overridden Public Methods
+ @Override
+ public String getInfo()
+ {
+ return (info);
+ }
+
/**
* Override the superclass to additionally reset this class' fields.
* <p>
@@ -99,11 +95,11 @@
* thorough.
* </p>
*/
+ @Override
public void recycle()
{
super.recycle();
- attributes_.clear();
needRegion_ = true;
}
@@ -125,20 +121,6 @@
needRegion_ = false;
}
- /**
- * Return a string representation of this object.
- */
- public String toString()
- {
-
- StringBuffer sb = new StringBuffer();
- sb.append("FieldBasedClusteredSession[");
- sb.append(super.toString());
- sb.append("]");
- return (sb.toString());
-
- }
-
// The superclass version of processSessionRepl is fine; it will remove
// the session metadata, and any attribute changes have been picked up
// for replication as they were made; no need to do anything here
@@ -147,14 +129,16 @@
// super.processSessionRepl();
// }
+ @Override
public void removeMyself()
{
// This is a shortcut to remove session and it's child attributes.
// Note that there is no need to remove attribute first since caller
// will do that already.
- proxy_.removeSession(realId, true);
+ getDistributedCacheManager().removeSession(getRealId(), true);
}
+ @Override
public void removeMyselfLocal()
{
// Need to evict attribute first before session to clean up everything.
@@ -164,31 +148,12 @@
// removePojosLocal call here in order to evict the ATTRIBUTE node.
// Otherwise empty nodes for the session root and child ATTRIBUTE will
// remain in the tree and screw up our list of session names.
- fieldProxy_.removePojosLocal(realId);
- fieldProxy_.removeSessionLocal(realId, true);
+ String myRealId = getRealId();
+ getDistributedCacheManager().removePojosLocal(myRealId);
+ getDistributedCacheManager().removeSessionLocal(myRealId, true);
}
- // ------------------------------------------------ JBoss internal abstract method
-
-
-
- @Override
- protected void establishProxy()
- {
- if (fieldProxy_ == null)
- {
- fieldProxy_ = ((JBossCacheManager) manager).getFieldBasedDistributedCacheManager();
-
- // still null???
- if (fieldProxy_ == null)
- {
- throw new IllegalStateException("Cache service is null");
- }
-
- // Set the superclass ref as well
- this.proxy_ = fieldProxy_;
- }
- }
+ // --------------------------------------------- Overridden Protected Methods
/**
* Populate the attributes stored in the distributed store to the local
@@ -196,13 +161,18 @@
* remove ourself as an Observer to existing attributes that are no longer
* in the distributed store.
*/
+ @Override
protected void populateAttributes()
{
+ Map<String, Object> attributes = getAttributesInternal();
+ String myRealId = getRealId();
+
// Preserve any local attributes that were excluded from replication
- Map excluded = removeExcludedAttributes(attributes_);
+ Map excluded = removeExcludedAttributes(attributes);
- Set keys = fieldProxy_.getPojoKeys(realId);
- Set oldKeys = new HashSet(attributes_.keySet());
+ FieldBasedDistributedCacheManager dcm = getDistributedCacheManager();
+ Set keys = dcm.getPojoKeys(myRealId);
+ Set oldKeys = new HashSet(attributes.keySet());
// Since we are going to touch each attribute, might as well
// check if we have any HttpSessionActivationListener
@@ -216,10 +186,10 @@
{
String name = (String) it.next();
- Object newAttrib = fieldProxy_.getPojo(realId, name);
+ Object newAttrib = dcm.getPojo(myRealId, name);
if (newAttrib != null)
{
- attributes_.put(name, newAttrib);
+ attributes.put(name, newAttrib);
// Check if we have a listener
if (newAttrib instanceof HttpSessionActivationListener)
@@ -228,17 +198,17 @@
else
{
// This shouldn't happen -- if we had a key, newAttrib s/b not null
- attributes_.remove(name);
+ attributes.remove(name);
}
}
}
- hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
+ setHasActivationListener(hasListener);
// Cycle through remaining old keys and remove them
for (Iterator it = oldKeys.iterator(); it.hasNext(); )
{
- attributes_.remove(it.next());
+ attributes.remove(it.next());
}
// Restore any excluded attributes
@@ -246,10 +216,11 @@
attributes.putAll(excluded);
}
- protected Object getJBossInternalAttribute(String name)
+ @Override
+ protected Object getAttributeInternal(String name)
{
// Check the local map first.
- Object result = attributes_.get(name);
+ Object result = getAttributesInternal().get(name);
// NOTE -- we no longer check with the store. Attributes are only
// loaded from store during populateAttributes() call at beginning
@@ -270,6 +241,7 @@
* as "immutable", since as an Observer we will detect any changes
* to those types.
*/
+ @Override
protected boolean isMutable(Object attribute)
{
boolean pojo = (attribute instanceof Advised);
@@ -277,50 +249,27 @@
return mutable;
}
- protected Object removeJBossInternalAttribute(String name, boolean localCall, boolean localOnly)
+ @Override
+ protected Object removeAttributeInternal(String name, boolean localCall, boolean localOnly)
{
// Remove it from the underlying store
if (localCall && !replicationExcludes.contains(name))
{
if (localOnly)
- fieldProxy_.removePojoLocal(realId, name);
+ getDistributedCacheManager().removePojoLocal(getRealId(), name);
else
- fieldProxy_.removePojo(realId, name);
+ getDistributedCacheManager().removePojo(getRealId(), name);
sessionAttributesDirty();
}
- Object result = attributes_.remove(name);
- if(result == null)
- {
- log.warn("removeJBossInternalAttribute(): null value to remove with key: "+ name);
- return null;
- }
-
- return result;
+ return getAttributesInternal().remove(name);
}
-
- protected Map getJBossInternalAttributes()
- {
- return attributes_;
- }
-
- protected Set getJBossInternalKeys()
- {
- return attributes_.keySet();
- }
-
+
/**
- * Method inherited from Tomcat. Return zero-length based string if not found.
- */
- protected String[] keys()
- {
- return ((String[]) getJBossInternalKeys().toArray(EMPTY_ARRAY));
- }
-
- /**
* Overrides the superclass to allow instrumented classes and
* non-serializable Collections and Maps.
*/
+ @Override
protected boolean canAttributeBeReplicated(Object attribute)
{
return (attribute == null || Util.checkPojoType(attribute));
@@ -334,12 +283,14 @@
* @param value
* @return Object
*/
- protected Object setJBossInternalAttribute(String key, Object value)
+ @Override
+ protected Object setAttributeInternal(String key, Object value)
{
Object oldVal = null;
if (!replicationExcludes.contains(key))
{
- oldVal = fieldProxy_.setPojo(realId, key, value, this);
+ String myRealId = getRealId();
+ oldVal = getDistributedCacheManager().setPojo(myRealId, key, value, this);
if(value != null)
{
@@ -347,7 +298,7 @@
if( value instanceof Map || value instanceof Collection)
{
// We need to obtain the proxy first.
- value = fieldProxy_.getPojo(realId, key);
+ value = getDistributedCacheManager().getPojo(myRealId, key);
}
}
@@ -356,7 +307,7 @@
}
// Still need to put it in the map to track locally.
- oldVal = attributes_.put(key, value);
+ oldVal = getAttributesInternal().put(key, value);
return oldVal;
}
Deleted: 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-10-23 18:15:21 UTC (rev 80002)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheClusteredSession.java 2008-10-23 18:55:56 UTC (rev 80003)
@@ -1,196 +0,0 @@
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2006, JBoss Inc., and individual contributors as indicated
-* by the @authors tag. See the copyright.txt in the distribution for a
-* full listing of individual contributors.
-*
-* This is free software; you can redistribute it and/or modify it
-* under the terms of the GNU Lesser General Public License as
-* published by the Free Software Foundation; either version 2.1 of
-* the License, or (at your option) any later version.
-*
-* This software is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this software; if not, write to the Free
-* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-*/
-
-package org.jboss.web.tomcat.service.session;
-
-import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSession;
-import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
-import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationCause;
-
-/**
- * Common superclass of ClusteredSession types that use JBossCache
- * as their distributed cache.
- *
- * @author Brian Stansberry
- *
- * @version $Revision: 56542 $
- */
-public abstract class JBossCacheClusteredSession
- extends ClusteredSession implements DistributableSession
-{
- /**
- * Our proxy to the cache.
- */
- protected transient DistributedCacheManager proxy_;
-
- /**
- * Create a new JBossCacheClusteredSession.
- *
- * @param manager
- * @param useJK
- */
- public JBossCacheClusteredSession(JBossCacheManager manager)
- {
- super(manager, manager.getReplicationTrigger(), manager.getUseJK());
- int maxUnrep = manager.getMaxUnreplicatedInterval() * 1000;
- setMaxUnreplicatedInterval(maxUnrep);
- establishNotificationPolicy();
- establishProxy();
- }
-
- /**
- * Initialize fields marked as transient after loading this session
- * from the distributed store
- *
- * @param manager the manager for this session
- */
- @Override
- public void initAfterLoad(AbstractJBossManager manager, ClusteredSessionNotificationCause cause)
- {
- // Our manager, notification policy and proxy may have been lost
- // if we were replicated, so reestablish them
- setManager(manager);
- establishNotificationPolicy();
- establishProxy();
-
- // Since attribute map may be transient, we may need to populate it
- // from the underlying store.
- populateAttributes();
-
- if (cause == ClusteredSessionNotificationCause.ACTIVATION)
- {
- this.needsPostReplicateActivation = true;
- }
-
- // Notify all attributes of type HttpSessionActivationListener (SRV 7.7.2)
- this.activate(cause);
-
- // We are no longer outdated vis a vis distributed cache
- clearOutdated();
- }
-
- public boolean needRegionForSession()
- {
- return false;
- }
-
- public boolean hasRegionForSession()
- {
- return false;
- }
-
- public void createdRegionForSession()
- {
- // ignored
- }
-
- /**
- * Gets a reference to the JBossCacheService.
- */
- protected void establishProxy()
- {
- if (proxy_ == null)
- {
- proxy_ = ((JBossCacheManager) manager).getDistributedCacheManager();
-
- // still null???
- if (proxy_ == null)
- {
- throw new RuntimeException("JBossCacheClusteredSession: Cache service is null.");
- }
- }
- }
-
- protected abstract void populateAttributes();
-
- /**
- * Override the superclass to additionally reset this class' fields.
- * <p>
- * <strong>NOTE:</strong> It is not anticipated that this method will be
- * called on a ClusteredSession, but we are overriding the method to be
- * thorough.
- * </p>
- */
- public void recycle()
- {
- super.recycle();
-
- proxy_ = null;
- }
-
- /**
- * Increment our version and place ourself in the cache.
- */
- public synchronized void processSessionRepl()
- {
- // Replicate the session.
- if (log.isTraceEnabled())
- {
- log.trace("processSessionRepl(): session is dirty. Will increment " +
- "version from: " + getVersion() + " and replicate.");
- }
- this.incrementVersionFromLocalActivity();
- proxy_.putSession(realId, this);
-
- // Allow subclasses to replicate attributes if needed
- replicateAttributes();
-
- sessionAttributesDirty = false;
- sessionMetadataDirty = false;
-
- updateLastReplicated();
- }
-
- /**
- * Extension point for subclasses to handle replication of attributes.
- * This default implementation does nothing.
- */
- protected void replicateAttributes()
- {
- // no-op
- }
-
- /**
- * Overrides the superclass impl by doing nothing if <code>localCall</code>
- * is <code>false</code>. The JBossCacheManager will already be aware of
- * a remote invalidation and will handle removal itself.
- */
- protected void removeFromManager(boolean localCall, boolean localOnly)
- {
- if (localCall)
- {
- super.removeFromManager(localCall, localOnly);
- }
- }
-
-
- protected Object removeAttributeInternal(String name, boolean localCall, boolean localOnly)
- {
- return removeJBossInternalAttribute(name, localCall, localOnly);
- }
-
- protected void establishNotificationPolicy()
- {
- setNotificationPolicy(((JBossCacheManager)manager).getNotificationPolicy());
- }
-
-}
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-10-23 18:15:21 UTC (rev 80002)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java 2008-10-23 18:55:56 UTC (rev 80003)
@@ -53,9 +53,7 @@
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManagerFactory;
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManagerFactoryFactory;
-import org.jboss.web.tomcat.service.session.distributedcache.spi.FieldBasedDistributedCacheManager;
import org.jboss.web.tomcat.service.session.distributedcache.spi.LocalDistributableSessionManager;
-
import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationCapability;
import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationCause;
import org.jboss.web.tomcat.service.session.notification.ClusteredSessionNotificationPolicy;
@@ -72,7 +70,7 @@
*/
public class JBossCacheManager
extends JBossManager
- implements JBossCacheManagerMBean, LocalDistributableSessionManager
+ implements JBossCacheManagerMBean, LocalDistributableSessionManager, ClusteredManager
{
// --------------------------------------------------------------- Constants
@@ -288,7 +286,7 @@
// Notify all session attributes that they get serialized (SRV 7.7.2)
long begin = System.currentTimeMillis();
- session.passivate(ClusteredSessionNotificationCause.REPLICATION);
+ session.notifyWillPassivate(ClusteredSessionNotificationCause.REPLICATION);
long elapsed = System.currentTimeMillis() - begin;
stats_.updatePassivationStats(realId, elapsed);
@@ -331,50 +329,6 @@
add((ClusteredSession) session, false); // wait to replicate until req end
}
- /**
- * Adds the given session to the collection of those being managed by this
- * Manager.
- *
- * @param session the session. Cannot be <code>null</code>.
- * @param replicate whether the session should be replicated
- *
- * @throws NullPointerException if <code>session</code> is <code>null</code>.
- */
- private void add(ClusteredSession session, boolean replicate)
- {
- // TODO -- why are we doing this check? The request checks session
- // validity and will expire the session; this seems redundant
- if (!session.isValid())
- {
- // Not an error; this can happen if a failover request pulls in an
- // outdated session from the distributed cache (see TODO above)
- log_.debug("Cannot add session with id=" + session.getIdInternal() +
- " because it is invalid");
- return;
- }
-
- String realId = session.getRealId();
- Object existing = sessions_.put(realId, session);
- unloadedSessions_.remove(realId);
-
- if (!session.equals(existing))
- {
- if (replicate)
- {
- storeSession(session);
- }
-
- // Update counters
- calcActiveSessions();
-
- if (trace_)
- {
- log_.trace("Session with id=" + session.getIdInternal() + " added. " +
- "Current active sessions " + localActiveCounter_.get());
- }
- }
- }
-
// Satisfy the Manager interface. Internally we use
// createEmptyClusteredSession to avoid a cast
public Session createEmptySession()
@@ -532,7 +486,7 @@
// replication, we need to make an offsetting activate() call
if (session.getNeedsPostReplicateActivation())
{
- session.activate(ClusteredSessionNotificationCause.REPLICATION);
+ session.notifyDidActivate(ClusteredSessionNotificationCause.REPLICATION);
}
}
@@ -625,6 +579,38 @@
}
}
+ // ------------------------------------------------------- ClusteredManager
+
+ /**
+ * Gets the <code>JBossCacheService</code> through which we interact
+ * with the <code>Cache</code>.
+ */
+ public DistributedCacheManager getDistributedCacheManager()
+ {
+ return proxy_;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getMaxUnreplicatedInterval()
+ {
+ return maxUnreplicatedInterval_;
+ }
+
+ public ClusteredSessionNotificationPolicy getNotificationPolicy()
+ {
+ return notificationPolicy_;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ReplicationTrigger getReplicationTrigger()
+ {
+ return this.replicationTrigger_;
+ }
+
// --------------------------------------------------------------- Lifecycle
/**
@@ -779,14 +765,6 @@
/**
* {@inheritDoc}
*/
- public int getMaxUnreplicatedInterval()
- {
- return maxUnreplicatedInterval_;
- }
-
- /**
- * {@inheritDoc}
- */
public void setMaxUnreplicatedInterval(int maxUnreplicatedInterval)
{
this.maxUnreplicatedInterval_ = maxUnreplicatedInterval;
@@ -822,14 +800,6 @@
public ReplicationGranularity getReplicationGranularity()
{
return replicationGranularity_;
- }
-
- /**
- * {@inheritDoc}
- */
- public ReplicationTrigger getReplicationTrigger()
- {
- return this.replicationTrigger_;
}
/**
@@ -938,15 +908,19 @@
boolean localOnly = true; // Don't pass attr removals to cache
// Ensure the correct TCL is in place
- ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
+ ContextClassLoaderSwitcher.SwitchContext switcher = null;
try
{
- Thread.currentThread().setContextClassLoader(tcl_);
+ switcher = getContextClassLoaderSwitcher().getSwitchContext();
+ switcher.setClassLoader(tcl_);
session.expire(notify, localCall, localOnly, ClusteredSessionNotificationCause.INVALIDATE);
}
finally
{
- Thread.currentThread().setContextClassLoader(prevTcl);
+ if (switcher != null)
+ {
+ switcher.reset();
+ }
}
// Remove any stats for this session
@@ -1151,40 +1125,6 @@
this.replicationFieldBatchMode_ = Boolean.valueOf(replicationFieldBatchMode);
}
- // ------------------------------------------------------------- Properties
-
- /**
- * Gets the <code>FieldBasedJBossCacheService</code> through which we interact
- * with the PojoCache.
- *
- * @throws IllegalStateException if we are not using field based replication
- */
- public FieldBasedDistributedCacheManager getFieldBasedDistributedCacheManager()
- {
- if (proxy_ != null && !(proxy_ instanceof FieldBasedDistributedCacheManager))
- throw new IllegalStateException("PojoCache not being used");
- return (FieldBasedDistributedCacheManager) proxy_;
- }
-
- /**
- * Gets the <code>JBossCacheService</code> through which we interact
- * with the <code>Cache</code>.
- */
- public DistributedCacheManager getDistributedCacheManager()
- {
- return proxy_;
- }
-
- public ClusteredSessionNotificationPolicy getNotificationPolicy()
- {
- return notificationPolicy_;
- }
-
- public void setNotificationPolicy_(ClusteredSessionNotificationPolicy notificationPolicy_)
- {
- this.notificationPolicy_ = notificationPolicy_;
- }
-
// --------------------------------------------------------------- Overrides
/**
@@ -1421,10 +1361,10 @@
// ------------------------------------------------------ Session Management
- private JBossCacheClusteredSession createEmptyClusteredSession()
+ private ClusteredSession createEmptyClusteredSession()
{
- JBossCacheClusteredSession session = null;
+ ClusteredSession session = null;
switch (replicationGranularity_)
{
case ATTRIBUTE:
@@ -1439,6 +1379,50 @@
}
return session;
}
+
+ /**
+ * Adds the given session to the collection of those being managed by this
+ * Manager.
+ *
+ * @param session the session. Cannot be <code>null</code>.
+ * @param replicate whether the session should be replicated
+ *
+ * @throws NullPointerException if <code>session</code> is <code>null</code>.
+ */
+ private void add(ClusteredSession session, boolean replicate)
+ {
+ // TODO -- why are we doing this check? The request checks session
+ // validity and will expire the session; this seems redundant
+ if (!session.isValid())
+ {
+ // Not an error; this can happen if a failover request pulls in an
+ // outdated session from the distributed cache (see TODO above)
+ log_.debug("Cannot add session with id=" + session.getIdInternal() +
+ " because it is invalid");
+ return;
+ }
+
+ String realId = session.getRealId();
+ Object existing = sessions_.put(realId, session);
+ unloadedSessions_.remove(realId);
+
+ if (!session.equals(existing))
+ {
+ if (replicate)
+ {
+ storeSession(session);
+ }
+
+ // Update counters
+ calcActiveSessions();
+
+ if (trace_)
+ {
+ log_.trace("Session with id=" + session.getIdInternal() + " added. " +
+ "Current active sessions " + localActiveCounter_.get());
+ }
+ }
+ }
/**
* Loads a session from the distributed store. If an existing session with
@@ -1463,7 +1447,7 @@
boolean mustAdd = false;
boolean passivated = false;
- JBossCacheClusteredSession session = (JBossCacheClusteredSession) sessions_.get(realId);
+ ClusteredSession session = sessions_.get(realId);
if (session == null)
{
@@ -1605,7 +1589,7 @@
// from a tx rollback are done.
SessionReplicationContext.startCacheActivity();
- session.processSessionRepl();
+ session.processSessionReplication();
}
catch (Exception ex)
{
@@ -1669,7 +1653,7 @@
// Tell the proxy to ignore cache notifications we are about
// to generate for this session.
SessionReplicationContext.startCacheActivity();
- session.passivate(ClusteredSessionNotificationCause.PASSIVATION);
+ session.notifyWillPassivate(ClusteredSessionNotificationCause.PASSIVATION);
proxy_.evictSession(realId);
sessionPassivated();
}
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties 2008-10-23 18:15:21 UTC (rev 80002)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings.properties 2008-10-23 18:55:56 UTC (rev 80003)
@@ -1,9 +1,11 @@
clusteredSession.attributeEvent=Session attribute event listener threw exception
+clusteredSession.bindingEvent=Session binding event listener threw exception
clusteredSession.invalidate.ise=invalidate: Session already invalidated
clusteredSession.isNew.ise=isNew: Session already invalidated
clusteredSession.getAttribute.ise=getAttribute: Session already invalidated
clusteredSession.getAttributeNames.ise=getAttributeNames: Session already invalidated
clusteredSession.getCreationTime.ise=getCreationTime: Session already invalidated
+clusteredSession.getLastAccessedTime.ise=getLastAccessedTime: Session already invalidated
clusteredSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: Session already invalidated
clusteredSession.getValueNames.ise=getAttributeNames: Session already invalidated
clusteredSession.notSerializable=Cannot serialize session attribute {0} for session {1}
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties 2008-10-23 18:15:21 UTC (rev 80002)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/LocalStrings_ja.properties 2008-10-23 18:55:56 UTC (rev 80003)
@@ -1,9 +1,11 @@
clusteredSession.attributeEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u5c5e\u6027\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
+clusteredSession.bindingEvent=\u30bb\u30c3\u30b7\u30e7\u30f3\u30d0\u30a4\u30f3\u30c7\u30a3\u30f3\u30b0\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u304c\u4f8b\u5916\u3092\u6295\u3052\u307e\u3057\u305f
clusteredSession.invalidate.ise=invalidate: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
clusteredSession.isNew.ise=isNew: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
clusteredSession.getAttribute.ise=getAttribute: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
clusteredSession.getAttributeNames.ise=getAttributeNames: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
clusteredSession.getCreationTime.ise=getCreationTime: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+clusteredSession.getLastAccessedTime.ise=getLastAccessedTime: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u65e2\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
clusteredSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
clusteredSession.getValueNames.ise=getAttributeNames: \u30bb\u30c3\u30b7\u30e7\u30f3\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059
clusteredSession.notSerializable=\u30bb\u30c3\u30b7\u30e7\u30f3 {1} \u306e\u305f\u3081\u306b\u30bb\u30c3\u30b7\u30e7\u30f3\u5c5e\u6027 {0} \u3092\u30b7\u30ea\u30a2\u30e9\u30a4\u30ba\u3067\u304d\u307e\u305b\u3093
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java 2008-10-23 18:15:21 UTC (rev 80002)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/SessionBasedClusteredSession.java 2008-10-23 18:55:56 UTC (rev 80003)
@@ -26,20 +26,13 @@
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionMetadata;
import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributableSessionTimestamp;
+import org.jboss.web.tomcat.service.session.distributedcache.spi.DistributedCacheManager;
/**
- * Implementation of a clustered session for the JBossCacheManager. The replication granularity
- * level is session based; that is, we replicate per whole session object.
- * We use JBossCache for our internal replicated data store.
- * The internal structure in JBossCache is as follows:
- * <pre>
- * /JSESSION
- * /hostname
- * /web_app_path (path + session id is unique)
- * /id Map(id, session)
- * (VERSION_KEY, version) // Used for version tracking. version is an Integer.
- * </pre>
+ * Implementation of a ClusteredSession where the replication granularity level
+ * is session based; that is, we replicate the entire attribute map whenever a
+ * request makes any attribute dirty.
* <p/>
* Note that the isolation level of the cache dictates the
* concurrency behavior.</p>
@@ -50,7 +43,7 @@
* @version $Revision: 56542 $
*/
class SessionBasedClusteredSession
- extends JBossCacheClusteredSession
+ extends ClusteredSession<DistributedCacheManager>
{
static final long serialVersionUID = 3200976125245487256L;
@@ -59,49 +52,79 @@
*/
protected static final String info = "SessionBasedClusteredSession/1.0";
- public SessionBasedClusteredSession(JBossCacheManager manager)
+ // ----------------------------------------------------------- Constructors
+
+
+ public SessionBasedClusteredSession(ClusteredManager manager)
{
super(manager);
}
+
// ---------------------------------------------- Overridden Public Methods
- /**
- * Return a string representation of this object.
- */
- public String toString()
+ @Override
+ public String getInfo()
{
- StringBuffer sb = new StringBuffer();
- sb.append("SessionBasedClusteredSession[");
- sb.append(super.toString());
- sb.append("]");
- return (sb.toString());
-
+ return (info);
}
+ @Override
public void removeMyself()
{
- proxy_.removeSession(realId, false);
+ getDistributedCacheManager().removeSession(getRealId(), false);
}
+ @Override
public void removeMyselfLocal()
{
- proxy_.removeSessionLocal(realId, false);
+ getDistributedCacheManager().removeSessionLocal(getRealId(), false);
}
+
+ @Override
+ public Map<String, Object> getSessionAttributeMap()
+ {
+ Map attrs = new HashMap(getAttributesInternal());
+ removeExcludedAttributes(attrs);
+ return attrs;
+ }
- // ----------------------------------------------HttpSession Public Methods
+ @Override
+ public void update(Integer version, DistributableSessionTimestamp timestamp,
+ DistributableSessionMetadata metadata, Map updatedAttrs)
+ {
+ // A new session may not have sent over any attributes
+ if (updatedAttrs == null)
+ {
+ updatedAttrs = new HashMap();
+ }
+
+ super.update(version, timestamp, metadata, updatedAttrs);
+
+ Map existing = getAttributesInternal();
+ Map excluded = removeExcludedAttributes(existing);
+ if (excluded != null)
+ updatedAttrs.putAll(excluded);
+ existing.clear();
+
+ existing.putAll(updatedAttrs);
+ }
+
+ // -------------------------------------------- Overridden Protected Methods
/**
* Does nothing -- all attributes are populated already
*/
+ @Override
protected void populateAttributes()
{
// no-op
}
- protected Object getJBossInternalAttribute(String name)
+ @Override
+ protected Object getAttributeInternal(String name)
{
- Object result = attributes.get(name);
+ Object result = getAttributesInternal().get(name);
// Do dirty check even if result is null, as w/ SET_AND_GET null
// still makes us dirty (ensures timely replication w/o using ACCESS)
@@ -114,50 +137,21 @@
}
- protected Object removeJBossInternalAttribute(String name,
- boolean localCall,
- boolean localOnly)
+ @Override
+ protected Object removeAttributeInternal(String name,
+ boolean localCall,
+ boolean localOnly)
{
if (localCall)
sessionAttributesDirty();
- return attributes.remove(name);
+ return getAttributesInternal().remove(name);
}
- protected Map getJBossInternalAttributes()
+ @Override
+ protected Object setAttributeInternal(String name, Object value)
{
- return attributes;
- }
-
- protected Object setJBossInternalAttribute(String name, Object value)
- {
sessionAttributesDirty();
- return attributes.put(name, value);
+ return getAttributesInternal().put(name, value);
}
- @Override
- public Map<String, Object> getSessionAttributeMap()
- {
- Map attrs = new HashMap(attributes);
- removeExcludedAttributes(attrs);
- return attrs;
- }
-
- public void update(Integer version, DistributableSessionTimestamp timestamp,
- DistributableSessionMetadata metadata, Map updatedAttrs)
- {
- // A new session may not have sent over any attributes
- if (updatedAttrs == null)
- {
- updatedAttrs = new HashMap();
- }
-
- super.update(version, timestamp, metadata, updatedAttrs);
-
- Map excluded = removeExcludedAttributes(attributes);
- if (excluded != null)
- updatedAttrs.putAll(excluded);
-
- this.attributes = updatedAttrs;
- }
-
}
More information about the jboss-cvs-commits
mailing list