[jboss-cvs] JBossAS SVN: r74444 - trunk/tomcat/src/main/org/jboss/web/tomcat/service/session.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Wed Jun 11 21:13:05 EDT 2008
Author: bstansberry at jboss.com
Date: 2008-06-11 21:13:05 -0400 (Wed, 11 Jun 2008)
New Revision: 74444
Modified:
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListenerBase.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedJBossCacheService.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java
trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
Log:
[JBAS-5620] Replace monitoring of Pojos via Observer pattern with using CacheListener
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java 2008-06-12 01:03:02 UTC (rev 74443)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListener.java 2008-06-12 01:13:05 UTC (rev 74444)
@@ -1,35 +1,31 @@
/*
-* JBoss, Home of Professional Open Source
-* Copyright 2005, 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.
-*/
+ * 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 java.util.Map;
import org.jboss.cache.Fqn;
-import org.jboss.cache.notifications.annotation.CacheStarted;
-import org.jboss.cache.notifications.annotation.CacheStopped;
import org.jboss.cache.notifications.annotation.NodeModified;
import org.jboss.cache.notifications.annotation.NodeRemoved;
-import org.jboss.cache.notifications.event.CacheStartedEvent;
-import org.jboss.cache.notifications.event.CacheStoppedEvent;
import org.jboss.cache.notifications.event.NodeModifiedEvent;
import org.jboss.cache.notifications.event.NodeRemovedEvent;
import org.jboss.logging.Logger;
@@ -37,6 +33,12 @@
import org.jboss.web.tomcat.service.session.ClusteredSession.SessionMetadata;
import org.jboss.web.tomcat.service.session.ClusteredSession.SessionTimestamp;
+/**
+ * Listens for removals and modifications in the cache, notifying the
+ * session manager of significant events.
+ *
+ * @author Brian Stansberry
+ */
@org.jboss.cache.notifications.annotation.CacheListener
public class CacheListener extends CacheListenerBase
{
@@ -44,8 +46,10 @@
private static final int POJO_ATTRIBUTE_FQN_INDEX = SESSION_ID_FQN_INDEX + 1;
// Element within an FQN that is the root of an individual Pojo attribute
private static final int POJO_KEY_FQN_INDEX = POJO_ATTRIBUTE_FQN_INDEX + 1;
- // Size of an Fqn that points to the root of a session
- private static final int POJO_KEY_FQN_SIZE = POJO_KEY_FQN_INDEX + 1;
+ // Element within an FQN that is the root of a session's internal pojo storage area
+ private static final int POJO_INTERNAL_FQN_INDEX = SESSION_ID_FQN_INDEX + 1;
+ // Minimum size of an FQN that is below the root of a session's internal pojo storage area
+ private static final int POJO_INTERNAL_FQN_SIZE = POJO_INTERNAL_FQN_INDEX + 1;
private static Logger log_ = Logger.getLogger(CacheListener.class);
private boolean fieldBased_;
@@ -55,124 +59,144 @@
fieldBased_ = (manager_.getReplicationGranularity() == ReplicationGranularity.FIELD);
}
- // --------------- TreeCacheListener methods ------------------------------------
+ // --------------- CacheListener methods ------------------------------------
@NodeRemoved
public void nodeRemoved(NodeRemovedEvent event)
{
- if (event.isPre() || event.isOriginLocal())
+ if (event.isPre())
return;
+ boolean local = event.isOriginLocal();
+ if (!fieldBased_ && local)
+ return;
+
Fqn fqn = event.getFqn();
boolean isBuddy = isBuddyFqn(fqn);
- // Potential removal of a Pojo where we need to unregister as an Observer.
- if (fieldBased_
- && isFqnPojoKeySized(fqn, isBuddy)
+ if (!local
+ && isFqnSessionRootSized(fqn, isBuddy)
&& isFqnForOurWebapp(fqn, isBuddy))
{
+ // A session has been invalidated from another node;
+ // need to inform manager
String sessId = getIdFromFqn(fqn, isBuddy);
- String attrKey = getPojoKeyFromFqn(fqn, isBuddy);
- manager_.processRemoteAttributeRemoval(sessId, attrKey);
+ manager_.notifyRemoteInvalidation(sessId);
}
- else if(isFqnSessionRootSized(fqn, isBuddy)
+ else if (local && !isBuddy
+ && isPossibleInternalPojoFqn(fqn)
&& isFqnForOurWebapp(fqn, isBuddy))
{
- // A session has been invalidated from another node;
- // need to inform manager
+ // One of our sessions' pojos is modified; need to inform
+ // the manager so it can mark the session dirty
String sessId = getIdFromFqn(fqn, isBuddy);
- manager_.processRemoteInvalidation(sessId);
+ manager_.notifyLocalAttributeModification(sessId);
}
}
@NodeModified
public void nodeModified(NodeModifiedEvent event)
- {
- if (event.isPre() || event.isOriginLocal())
+ {
+ if (event.isPre())
return;
+ boolean local = event.isOriginLocal();
+ if (!fieldBased_ && local)
+ return;
+
Fqn fqn = event.getFqn();
boolean isBuddy = isBuddyFqn(fqn);
- // We only care if there is a chance this is for a session root
- if (!isFqnSessionRootSized(fqn, isBuddy))
- return;
- // We only care if this is for our webapp
- if (!isFqnForOurWebapp(fqn, isBuddy))
- return;
-
- // Query if we have version value in the distributed cache.
- // If we have a version value, compare the version and invalidate if necessary.
- Map data = event.getData();
- Integer version = (Integer) data.get(JBossCacheService.VERSION_KEY);
- if(version != null)
+ if (isFqnSessionRootSized(fqn, isBuddy)
+ &&isFqnForOurWebapp(fqn, isBuddy))
{
- String realId = getIdFromFqn(fqn, isBuddy);
-
- ClusteredSession session = manager_.findLocalSession(realId);
- if (session == null)
+ // Query if we have version value in the distributed cache.
+ // If we have a version value, compare the version and invalidate if necessary.
+ Map data = event.getData();
+ Integer version = (Integer) data.get(JBossCacheService.VERSION_KEY);
+ if(version != null)
{
- String owner = isBuddy ? getBuddyOwner(fqn) : null;
- // Notify the manager that an unloaded session has been updated
- manager_.unloadedSessionChanged(realId, owner,
- (SessionTimestamp) data.get(JBossCacheService.TIMESTAMP_KEY),
- (SessionMetadata) data.get(JBossCacheService.METADATA_KEY));
- }
- else if (session.isNewData(version.intValue()))
- {
- // Need to invalidate the loaded session
- session.setOutdatedVersion(version.intValue());
- if(log_.isTraceEnabled())
+ String realId = getIdFromFqn(fqn, isBuddy);
+
+ ClusteredSession session = manager_.findLocalSession(realId);
+ if (session == null)
{
- log_.trace("nodeDirty(): session in-memory data is " +
- "invalidated with id: " + realId + " and version: " +
- version.intValue());
+ String owner = isBuddy ? getBuddyOwner(fqn) : null;
+ // Notify the manager that an unloaded session has been updated
+ manager_.unloadedSessionChanged(realId, owner,
+ (SessionTimestamp) data.get(JBossCacheService.TIMESTAMP_KEY),
+ (SessionMetadata) data.get(JBossCacheService.METADATA_KEY));
}
+ else if (session.isNewData(version.intValue()))
+ {
+ // Need to invalidate the loaded session
+ session.setOutdatedVersion(version.intValue());
+ if(log_.isTraceEnabled())
+ {
+ log_.trace("nodeDirty(): session in-memory data is " +
+ "invalidated with id: " + realId + " and version: " +
+ version.intValue());
+ }
+ }
+ else if (!isBuddy)
+ {
+ log_.warn("Possible concurrency problem: Replicated version id " +
+ version + " matches in-memory version for session " + realId);
+ }
+ /*else
+ {
+ We have a local session but got a modification for the buddy tree.
+ This means another node is in the process of taking over the session;
+ we don't worry about it
+ }
+ */
}
- else if (!isBuddy)
+ else
{
- log_.warn("Possible concurrency problem: Replicated version id " +
- version + " matches in-memory version for session " + realId);
+ log_.warn("No VERSION_KEY attribute found in " + fqn);
}
- /*else
- {
- We have a local session but got a modification for the buddy tree.
- This means another node is in the process of taking over the session;
- we don't worry about it
- }
- */
}
- /* else if (fieldBased_)
+ else if (local && !isBuddy
+ && isPossibleInternalPojoFqn(fqn)
+ && isFqnForOurWebapp(fqn, isBuddy))
{
- // could be for the __JBossInternal__ fqn, which we want to ignore
+ // One of our sessions' pojos is modified; need to inform
+ // the manager so it can mark the session dirty
+ String sessId = getIdFromFqn(fqn, isBuddy);
+ manager_.notifyLocalAttributeModification(sessId);
}
- else
- {
- log_.warn("No VERSION_KEY attribute found in " + fqn);
- } */
}
-
- @CacheStarted
- public void cacheStarted(CacheStartedEvent event)
- {
- // TODO will need to synchronize this with local sessions
- // BES -- 2007/07/03 Not sure what this means
- }
-
- @CacheStopped
- public void cacheStopped(CacheStoppedEvent event)
- {
- // TODO will need to synchronize this with local sessions
- // BES -- 2007/07/03 Not sure what this means
- }
- public static boolean isFqnPojoKeySized(Fqn fqn, boolean isBuddy)
- {
- return fqn.size() == (isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_SIZE : POJO_KEY_FQN_SIZE);
- }
-
public static String getPojoKeyFromFqn(Fqn fqn, boolean isBuddy)
{
return (String) fqn.get(isBuddy ? BUDDY_BACKUP_ROOT_OWNER_SIZE + POJO_KEY_FQN_INDEX: POJO_KEY_FQN_INDEX);
}
+
+ /**
+ * Check if the fqn is big enough to be in the internal pojo area but
+ * isn't in the regular attribute area.
+ *
+ * Structure in the cache is:
+ *
+ * /JSESSION
+ * ++ /hostname
+ * ++++ /contextpath
+ * ++++++ /sessionid
+ * ++++++++ /ATTRIBUTE
+ * ++++++++ /_JBossInternal_
+ * ++++++++++ etc etc
+ *
+ * If the Fqn size is big enough to be "etc etc" or lower, but the 4th
+ * level is "ATTRIBUTE", it must be under _JBossInternal_. We discriminate
+ * based on != ATTRIBUTE to avoid having to code to the internal PojoCache
+ * _JBossInternal_ name.
+ *
+ * @param fqn
+ * @return
+ */
+ public static boolean isPossibleInternalPojoFqn(Fqn fqn)
+ {
+ return (fqn.size() > POJO_INTERNAL_FQN_SIZE
+ && JBossCacheService.ATTRIBUTE.equals(fqn.get(POJO_INTERNAL_FQN_INDEX)) == false);
+ }
}
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListenerBase.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListenerBase.java 2008-06-12 01:03:02 UTC (rev 74443)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/CacheListenerBase.java 2008-06-12 01:13:05 UTC (rev 74444)
@@ -1,3 +1,24 @@
+/*
+ * 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.cache.Fqn;
@@ -3,4 +24,9 @@
import org.jboss.cache.buddyreplication.BuddyManager;
+/**
+ * Base class for JBC cache listener impls.
+ *
+ * @author Brian Stansberry
+ */
public class CacheListenerBase
{
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-06-12 01:03:02 UTC (rev 74443)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedClusteredSession.java 2008-06-12 01:13:05 UTC (rev 74444)
@@ -21,9 +21,6 @@
*/
package org.jboss.web.tomcat.service.session;
-import org.jboss.aspects.patterns.observable.Observer;
-import org.jboss.aspects.patterns.observable.Subject;
-
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -34,6 +31,8 @@
import javax.servlet.http.HttpSessionActivationListener;
+import org.jboss.aop.Advised;
+
/**
* <p>
* Implementation of a clustered session for JBossCacheManager. The replication granularity
@@ -69,7 +68,7 @@
* @version $Revision: 58586 $
*/
class FieldBasedClusteredSession
- extends JBossCacheClusteredSession implements Observer
+ extends JBossCacheClusteredSession
{
/** The serialVersionUID */
private static final long serialVersionUID = 8347544395334247623L;
@@ -216,21 +215,10 @@
{
String name = (String) it.next();
- Object oldAttrib = null;
Object newAttrib = fieldProxy_.getPojo(realId, name);
if (newAttrib != null)
{
- oldAttrib = attributes_.put(name, newAttrib);
-
- if (oldAttrib != newAttrib)
- {
- // Need to observe this pojo as well
- // for any modification events.
- fieldProxy_.addObserver(this, newAttrib);
-
- // Stop observing the old pojo
- fieldProxy_.removeObserver(this, oldAttrib); // null pojo OK :)
- }
+ attributes_.put(name, newAttrib);
// Check if we have a listener
if (newAttrib instanceof HttpSessionActivationListener)
@@ -238,12 +226,8 @@
}
else
{
- // This shouldn't happen -- if we had a key, newAttrib s/b not null
-
- oldAttrib = attributes_.remove(name);
- // Stop observing this pojo
- fieldProxy_.removeObserver(this, oldAttrib); // null pojo OK :)
-
+ // This shouldn't happen -- if we had a key, newAttrib s/b not null
+ attributes_.remove(name);
}
}
}
@@ -251,11 +235,9 @@
hasActivationListener = hasListener ? Boolean.TRUE : Boolean.FALSE;
// Cycle through remaining old keys and remove them
- // and also remove ourself as Observer
for (Iterator it = oldKeys.iterator(); it.hasNext(); )
{
- Object oldAttrib = attributes_.remove(it.next());
- fieldProxy_.removeObserver(this, oldAttrib);
+ attributes_.remove(it.next());
}
// Restore any excluded attributes
@@ -289,7 +271,7 @@
*/
protected boolean isMutable(Object attribute)
{
- boolean pojo = (attribute instanceof Subject);
+ boolean pojo = (attribute instanceof Advised);
boolean mutable = (!pojo && super.isMutable(attribute));
return mutable;
}
@@ -312,7 +294,6 @@
log.warn("removeJBossInternalAttribute(): null value to remove with key: "+ name);
return null;
}
- fieldProxy_.removeObserver(this, result);
return result;
}
@@ -358,10 +339,6 @@
if (!replicationExcludes.contains(key))
{
oldVal = fieldProxy_.setPojo(realId, key, value, this);
- if(oldVal != null)
- { // We are done with the old one.
- fieldProxy_.removeObserver(this, oldVal);
- }
if(value != null)
{
@@ -371,9 +348,6 @@
// We need to obtain the proxy first.
value = fieldProxy_.getPojo(realId, key);
}
-
- // Need to use obj since it can return as a proxy.
- fieldProxy_.addObserver(this, value);
}
// Only mark session dirty if we can replicate the attribute
@@ -385,20 +359,4 @@
return oldVal;
}
-
- /**
- * Call back handler for the aop Subject/Observer pattern.
- * We subscribe to the event of field write and mark ourself dirty.
- *
- * @param subject the object we are Observing
- */
- public void fireChange(Subject subject)
- {
- // Currently we don't care who is modified, we will simply mark session is dirty for replication purpose.
- if(log.isTraceEnabled())
- {
- log.trace("fireChange(): subject has changed: " +subject);
- }
- sessionAttributesDirty();
- }
}
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedJBossCacheService.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedJBossCacheService.java 2008-06-12 01:03:02 UTC (rev 74443)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/FieldBasedJBossCacheService.java 2008-06-12 01:13:05 UTC (rev 74444)
@@ -22,23 +22,14 @@
package org.jboss.web.tomcat.service.session;
-import java.lang.reflect.Field;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
-import java.util.WeakHashMap;
-import org.jboss.aspects.patterns.observable.Observer;
-import org.jboss.aspects.patterns.observable.Subject;
import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.pojo.PojoCache;
-import org.jboss.cache.pojo.PojoCacheAlreadyDetachedException;
/**
* Adds PojoCache-specific capabilities to JBossCacheService.
@@ -49,7 +40,6 @@
public class FieldBasedJBossCacheService extends JBossCacheService
{
private final PojoCache pojoCache_;
- private WeakHashMap typeMap = new WeakHashMap();
/**
* Create a new FieldBasedJBossCacheService.
@@ -248,270 +238,4 @@
}
}
- /**
- * Recursively adds session as observer to the pojo graph. Assumes the
- * whole object graph has Subject "introduction" declared. If a portion
- * of the graph isn't a Subject, the recursion does not continue below
- * that part of the graph.
- *
- * @param session the session
- * @param pojo the pojo. Can be <code>null</code>.
- */
- public void addObserver(Observer session, Object pojo)
- {
- addObserver(session, pojo, new HashSet());
- }
-
- private void addObserver(Observer session, Object pojo, Set processed)
- {
- if ( pojo instanceof Collection )
- {
- if(log_.isTraceEnabled())
- {
- log_.trace("addObserver(): pojo of type " +pojo.getClass().getName() + " is a Collection; trying to observe elements");
- }
- Collection col = (Collection)pojo;
- for (Iterator i = col.iterator(); i.hasNext();) {
- // If not a managed pojo, will return anyway
- addObserver(session, i.next(), processed);
- }
- }
- else if (pojo instanceof Map)
- {
- if(log_.isTraceEnabled())
- {
- log_.trace("addObserver(): pojo of type " +pojo.getClass().getName() + " is a Map; trying to observe entries");
- }
- for (Iterator i = ((Map)pojo).entrySet().iterator(); i.hasNext();)
- {
- Map.Entry entry = (Map.Entry) i.next();
-
- // Walk thru key and value
- addObserver(session, entry.getKey(), processed);
- addObserver(session, entry.getValue(), processed);
- }
- }
-
- if(! (pojo instanceof Subject) )
- {
- if(log_.isTraceEnabled())
- {
- log_.trace("addObserver(): pojo of type " +pojo.getClass().getName() + " is not a Subject");
- }
- return; // No need to add observer since it is primitive.
- }
-
- Subject subject = (Subject)pojo;
- subject.addObserver(session);
-
- if(log_.isTraceEnabled())
- {
- log_.trace("addObserver(): session: " +session + " pojo name: " +pojo.getClass().getName());
- }
-
- // Examine each field of the type and its superclasses to see if
- // we need to add the observer to the pojo held by that field
- // Traverse recursively
-
- // First identify and cache the names of all the class'
- // non-immediate fields
- Class type = pojo.getClass();
- Set complexFields = (Set) typeMap.get(type);
- if (complexFields == null)
- {
- complexFields = Util.parseComplexFields(type);
- typeMap.put(type, complexFields);
- }
-
- if (complexFields.size() == 0)
- return;
-
- // Store a ref to the pojo to avoid cyclic additions
- processed.add(pojo);
-
- for (Iterator iter = complexFields.iterator(); iter.hasNext();)
- {
- String fieldName = (String) iter.next();
- Class curType = type;
- while (curType != null)
- {
- try
- {
- Field field = curType.getDeclaredField(fieldName);
- boolean accessible = field.isAccessible();
- Object value = null;
- try
- {
- field.setAccessible(true);
-
- value=field.get(pojo);
- // Continue recursively unless we've already handled this value
- if (value != null && !processed.contains(value))
- addObserver(session, value, processed);
- break;
- }
- catch(IllegalAccessException e)
- {
- throw new RuntimeException("field access failed", e);
- }
- finally
- {
- field.setAccessible(accessible);
- }
- }
- catch (NoSuchFieldException e)
- {
- // Check if the field is declared in a superclass
- curType = curType.getSuperclass();
- if (curType == null)
- throw new RuntimeException("Field " + fieldName +
- " does not exist", e);
- }
- }
- }
- }
-
- /**
- * Recursively removes session as observer to the pojo graph. Assumes the
- * whole object graph has Subject "introduction" declared. If a portion
- * of the graph isn't a Subject, the recursion does not continue below
- * that part of the graph.
- *
- * @param session the session
- * @param pojo the pojo to stop observing. Can be <code>null</code>.
- */
- public void removeObserver(Observer session, Object pojo)
- {
- removeObserver(session, pojo, new HashSet());
- }
-
- private void removeObserver(Observer session, Object pojo, Set stack)
- {
- if ( pojo instanceof Collection )
- {
- Collection col = (Collection)pojo;
- for (Iterator i = col.iterator(); i.hasNext();) {
- Object obj = i.next();
- // If not a managed pojo, will return anyway
- removeObserver(session, obj, stack);
- }
-
- return;
- }
- else if (pojo instanceof Map)
- {
- Map map = (Map)pojo;
- for (Iterator i = map.keySet().iterator(); i.hasNext();) {
- Object key = i.next();
- Object value = map.get(key);
-
- // Walk thru key and value
- removeObserver(session, key, stack);
- removeObserver(session, value, stack);
- }
-
- return;
- }
- // BRIAN 3/14 changed this from checking Advised to checking Subject
- // since that is what we cast to below
- if(! (pojo instanceof Subject) )
- {
- return; // No need to add observer since it is primitive.
- }
-
- Subject subject = (Subject)pojo;
- try
- {
- subject.removeObserver(session);
- }
- catch (PojoCacheAlreadyDetachedException e)
- {
- // We've hit a stale pojo; can't continue further
- // This will regularly happen when we handle notifications
- // of remote attribute removals and remote invalidations
- if(log_.isTraceEnabled())
- {
- log_.trace("removeObserver(): found detached in session: " +session + " pojo name: " +pojo.getClass().getName());
- }
- return;
- }
-
- if(log_.isTraceEnabled())
- {
- log_.trace("removeObserver(): session: " +session + " pojo name: " +pojo.getClass().getName());
- }
-
- // Examine each field of the type and its superclasses to see if
- // we need to remove the observer from the pojo held by that field
- // Traverse recursively
-
- // First identify and cache the names of all the class'
- // non-immediate fields
- Class type = pojo.getClass();
- Set complexFields = (Set) typeMap.get(type);
- if (complexFields == null)
- {
- complexFields = Util.parseComplexFields(type);
- typeMap.put(type, complexFields);
- }
-
- if (complexFields.size() == 0)
- return;
-
- // Store a ref to the pojo to avoid cyclic removals
- stack.add(pojo);
-
- for (Iterator iter = complexFields.iterator(); iter.hasNext();)
- {
- String fieldName = (String) iter.next();
- Class curType = type;
- while (curType != null)
- {
- try
- {
- Field field = curType.getDeclaredField(fieldName);
- boolean accessible = field.isAccessible();
- Object value = null;
- try
- {
- field.setAccessible(true);
-
- value=field.get(pojo);
- // Continue recursively unless we've already handled this value
- if (value != null && !stack.contains(value))
- removeObserver(session, value, stack);
- break;
- }
- catch (PojoCacheAlreadyDetachedException e)
- {
- // We've hit a stale proxy; can't continue further
- // This will regularly happen when we handle notifications
- // of remote attribute removals and remote invalidations
- if(log_.isTraceEnabled())
- {
- log_.trace("removeObserver(): found detached pojo in field " + field.getName() + " in object graph under session: " +session + " pojo name: " +pojo.getClass().getName());
- }
- break;
- }
- catch(IllegalAccessException e)
- {
- throw new RuntimeException("field access failed", e);
- }
- finally
- {
- field.setAccessible(accessible);
- }
- }
- catch (NoSuchFieldException e)
- {
- // Check if the field is declared in a superclass
- curType = curType.getSuperclass();
- if (curType == null)
- throw new RuntimeException("Field " + fieldName +
- " does not exist", e);
- }
- }
- }
- }
-
}
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-06-12 01:03:02 UTC (rev 74443)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/JBossCacheManager.java 2008-06-12 01:13:05 UTC (rev 74444)
@@ -1530,40 +1530,14 @@
}
}
}
-
- public void processRemoteAttributeRemoval(String realId, String attrKey)
- {
-
- ClusteredSession session = findLocalSession(realId);
- if (session != null)
- {
- boolean localCall = false; // call is due to remote event
- boolean localOnly = true; // don't call back into cache
- boolean notify = false; // SRV.10.7 gives us leeway
- // not to notify listeners,
- // which is safer
-
- // Ensure the correct TCL is in place
- ClassLoader prevTcl = Thread.currentThread().getContextClassLoader();
- try
- {
- Thread.currentThread().setContextClassLoader(tcl_);
- synchronized (session)
- {
- session.removeAttributeInternal(attrKey, localCall, localOnly, notify);
- }
- if (log_.isTraceEnabled())
- log_.trace("processRemoteAttributeRemoval: removed attribute " +
- attrKey + " from " + realId);
- }
- finally
- {
- Thread.currentThread().setContextClassLoader(prevTcl);
- }
- }
- }
- public void processRemoteInvalidation(String realId)
+ /**
+ * Notifies the manager that a session in the distributed cache has
+ * been invalidated
+ *
+ * @param realId the session id excluding any jvmRoute
+ */
+ public void notifyRemoteInvalidation(String realId)
{
// Remove the session from our local map
ClusteredSession session = (ClusteredSession) sessions_.remove(realId);
@@ -1609,6 +1583,26 @@
}
}
+ /**
+ * Callback from the distributed cache notifying of a local modification
+ * to a session's attributes. Meant for use with FIELD granularity,
+ * where the session may not be aware of modifications.
+ *
+ * @param realId the session id excluding any jvmRoute
+ */
+ public void notifyLocalAttributeModification(String realId)
+ {
+ ClusteredSession session = (ClusteredSession) sessions_.get(realId);
+ if (session != null)
+ {
+ session.sessionAttributesDirty();
+ }
+ else
+ {
+ log_.warn("");
+ }
+ }
+
private void sessionPassivated()
{
int pc = passivatedCount_.incrementAndGet();
Modified: trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java
===================================================================
--- trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java 2008-06-12 01:03:02 UTC (rev 74443)
+++ trunk/tomcat/src/main/org/jboss/web/tomcat/service/session/Util.java 2008-06-12 01:13:05 UTC (rev 74444)
@@ -23,8 +23,6 @@
package org.jboss.web.tomcat.service.session;
import java.io.Serializable;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -179,49 +177,8 @@
|| (pojo instanceof Advised)
|| (immediates.contains(pojo.getClass())));
}
-
- public static Set parseComplexFields(Class clazz)
- {
- Set result = new HashSet();
-
- while (clazz != null)
- {
- Field[] fields = clazz.getDeclaredFields();
- for (int i = 0; i < fields.length; i++)
- {
- if (!immediates.contains(fields[i].getType())
- && isReplicatable(fields[i]))
- {
- result.add(fields[i].getName());
- }
- }
-
- clazz = clazz.getSuperclass();
- }
- return result;
- }
/**
- * Returns false if the given field is static, transient or final.
- *
- * @param f the field
- * @return
- */
- public static boolean isReplicatable(Field f) {
- int mods = f.getModifiers();
- /**
- * The following modifiers are ignored in the cache, i.e., they will not be stored in the cache.
- * Whenever, user trying to access these fields, it will be accessed from the in-memory version.
- */
- if (Modifier.isStatic(mods)
- || Modifier.isTransient(mods)
- || Modifier.isFinal(mods)) {
- return false;
- }
- return true;
- }
-
- /**
* Prevent instantiation.
*/
private Util() {}
More information about the jboss-cvs-commits
mailing list