[jbosscache-commits] JBoss Cache SVN: r5176 - in core/trunk/src: main/java/org/jboss/cache and 11 other directories.

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Mon Jan 21 20:06:22 EST 2008


Author: manik.surtani at jboss.com
Date: 2008-01-21 20:06:22 -0500 (Mon, 21 Jan 2008)
New Revision: 5176

Added:
   core/trunk/src/main/java/org/jboss/cache/interceptors/MarshalledValueInterceptor.java
   core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValue.java
   core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValueHelper.java
   core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValueMap.java
   core/trunk/src/test/java/org/jboss/cache/interceptors/MarshalledValueInterceptorTest.java
   core/trunk/src/test/java/org/jboss/cache/marshall/AsyncReplMarshalledValuesTest.java
   core/trunk/src/test/java/org/jboss/cache/marshall/MarshalledValueTest.java
   core/trunk/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingMarshalledValuesTest.java
   core/trunk/src/test/java/org/jboss/cache/marshall/SyncReplMarshalledValuesTest.java
Modified:
   core/trunk/src/main/docbook/userguide/en/modules/configuration_reference.xml
   core/trunk/src/main/java/org/jboss/cache/Cache.java
   core/trunk/src/main/java/org/jboss/cache/CacheSPI.java
   core/trunk/src/main/java/org/jboss/cache/Node.java
   core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
   core/trunk/src/main/java/org/jboss/cache/config/Configuration.java
   core/trunk/src/main/java/org/jboss/cache/config/Option.java
   core/trunk/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java
   core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java
   core/trunk/src/main/java/org/jboss/cache/invocation/NodeInvocationDelegate.java
   core/trunk/src/main/java/org/jboss/cache/marshall/CacheMarshaller200.java
   core/trunk/src/main/java/org/jboss/cache/marshall/MethodDeclarations.java
   core/trunk/src/main/java/org/jboss/cache/notifications/Notifier.java
   core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java
   core/trunk/src/test/java/org/jboss/cache/factories/InterceptorChainFactoryTest.java
   core/trunk/src/test/java/org/jboss/cache/lock/IdentityLockTest.java
   core/trunk/src/test/java/org/jboss/cache/lock/LockReleaseTest.java
   core/trunk/src/test/java/org/jboss/cache/marshall/AsyncReplTest.java
   core/trunk/src/test/java/org/jboss/cache/marshall/CustomCollectionTest.java
   core/trunk/src/test/java/org/jboss/cache/marshall/RegionBasedMarshallingTestBase.java
   core/trunk/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingTest.java
   core/trunk/src/test/java/org/jboss/cache/marshall/SelectedClassnameClassLoader.java
   core/trunk/src/test/java/org/jboss/cache/marshall/SyncReplTest.java
Log:
JBCACHE-1231 - Implicit marshalled values

Modified: core/trunk/src/main/docbook/userguide/en/modules/configuration_reference.xml
===================================================================
--- core/trunk/src/main/docbook/userguide/en/modules/configuration_reference.xml	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/docbook/userguide/en/modules/configuration_reference.xml	2008-01-22 01:06:22 UTC (rev 5176)
@@ -645,6 +645,11 @@
                         support use of different classloaders for different cache regions. This defaults to
                         <literal>false</literal>
                         if unspecified.
+                        <p/>
+                        <b>DEPRECATED.</b>
+                        This option will disappear in JBoss Cache 3.x. See
+                        <literal>UseLazyDeserialization</literal>
+                        instead.
                      </para>
                   </entry>
                </row>
@@ -669,12 +674,21 @@
                   </entry>
 
                   <entry>
-                     <para>An optional parameter that controls whether JBoss Cache registers a shutdown hook with the JVM
-                        runtime.  Allowed values are <literal>DEFAULT</literal>, <literal>REGISTER</literal> and
-                        <literal>DONT_REGISTER</literal>.  <literal>REGISTER</literal> and <literal>DONT_REGISTER</literal>
-                        forces or suppresses the registration of a shutdown hook, respectively, and <literal>DEFAULT</literal>
-                        registers one if an MBean server (other than the JDK default) cannot be found and it is assumed that the
-                        cache is running in a managed environment.  The default if unspecified is, as expected, <literal>DEFAULT</literal>.
+                     <para>An optional parameter that controls whether JBoss Cache registers a shutdown hook with the
+                        JVM
+                        runtime. Allowed values are<literal>DEFAULT</literal>,
+                        <literal>REGISTER</literal>
+                        and
+                        <literal>DONT_REGISTER</literal>.
+                        <literal>REGISTER</literal>
+                        and
+                        <literal>DONT_REGISTER</literal>
+                        forces or suppresses the registration of a shutdown hook, respectively, and
+                        <literal>DEFAULT</literal>
+                        registers one if an MBean server (other than the JDK default) cannot be found and it is assumed
+                        that the
+                        cache is running in a managed environment. The default if unspecified is, as expected,<literal>
+                        DEFAULT</literal>.
                      </para>
                   </entry>
                </row>
@@ -686,7 +700,8 @@
 
                   <entry>
                      <para>An optional parameter that can be used to enable or disable the use of lazy deserialization
-                        for cached objects. Defaults to <literal>true</literal>.
+                        for cached objects. Defaults to<literal>true</literal>. If lazy deserialization is disabled,
+                        support for implicitly using context class loaders registered with the calling thread goes away.
                      </para>
                   </entry>
                </row>

Modified: core/trunk/src/main/java/org/jboss/cache/Cache.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/Cache.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/Cache.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -240,12 +240,12 @@
     * @param fqn fqn of the node to retrieve
     * @return a Node object, or a null if the node does not exist.
     */
-   Node getNode(Fqn<?> fqn);
+   Node<K, V> getNode(Fqn<?> fqn);
 
    /**
     * Convenience method that takes a string representation of an Fqn.  Otherwise identical to {@link #getNode(Fqn)}
     */
-   Node getNode(String fqn);
+   Node<K, V> getNode(String fqn);
 
 
    /**

Modified: core/trunk/src/main/java/org/jboss/cache/CacheSPI.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/CacheSPI.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/CacheSPI.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -52,6 +52,23 @@
    NodeSPI<K, V> getRoot();
 
    /**
+    * Overrides {@link Cache#getNode(String)} to return a {@link org.jboss.cache.NodeSPI} instead of a {@link org.jboss.cache.Node}.
+    *
+    * @param s string representation of an Fqn
+    * @return a NodeSPI
+    */
+   NodeSPI<K, V> getNode(String s);
+
+   /**
+    * Overrides {@link Cache#getNode(Fqn)} to return a {@link org.jboss.cache.NodeSPI} instead of a {@link org.jboss.cache.Node}.
+    *
+    * @param f an Fqn
+    * @return a NodeSPI
+    */
+   NodeSPI<K, V> getNode(Fqn<?> f);
+
+
+   /**
     * Retrieves a reference to a running {@link javax.transaction.TransactionManager}, if one is configured.
     * <p/>
     * From 2.1.0, Interceptor authors should obtain this by injection rather than this method.  See the

Modified: core/trunk/src/main/java/org/jboss/cache/Node.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/Node.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/Node.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -272,19 +272,19 @@
    /**
     * Tests if a node reference is still valid.  A node reference may become invalid if it has been removed, invalidated
     * or moved, either locally or remotely.  If a node is invalid, it should be fetched again from the cache or a valid
-    * parent node.  Operations on invalid nodes will throw a {@link org.jboss.cache.NodeNotValidException}. 
+    * parent node.  Operations on invalid nodes will throw a {@link org.jboss.cache.NodeNotValidException}.
     *
     * @return true if the node is valid.
     */
    boolean isValid();
 
-     /**
+   /**
     * Nodes marked resident would be ignored by the eviction algorithms. E.g. if the algorithm is
     * "keep LRU 10 nodes" - the resident nodes won't be counted within those 10 nodes,
     * and also won't be evicted when the threshold is reached.
     * N.B. calling this method won't have any effect on node's eviction, e.g. we won't consider this node as being
     * 'used' in a LRU scenario. If the cache is used in a replicated environment then the resident property is NOT
-    * replicated across the cluster. Also the property is not transactionable. 
+    * replicated across the cluster. Also the property is not transactionable.
     */
    boolean isResident();
 
@@ -295,12 +295,13 @@
 
    /**
     * Tests whether this node is configured to be exclusively locked when inserting or removing children.
-    * <p />
+    * <p/>
     * The default
     * value for this is what is configured in the <tt>LockParentForChildInsertRemove</tt> configuration property,
     * programatically reachable by querying {@link org.jboss.cache.config.Configuration#isLockParentForChildInsertRemove()}
-    * <p />
+    * <p/>
     * This can also be configured on a per-node basis using {@link #setLockForChildInsertRemove(boolean)}
+    *
     * @return true if the node is configured to be exclusively locked for child insertions and removal, false otherwise.
     * @since 2.1.0
     */
@@ -308,9 +309,21 @@
 
    /**
     * Configures the behaviour of how this node is locked when adding/removing children.
+    *
     * @param lockForChildInsertRemove if true, exclusive locks will be obtained when children are added/removed.  If
-    * false, a shared "read lock" will be obtained instead.
+    *                                 false, a shared "read lock" will be obtained instead.
     * @since 2.1.0
     */
    void setLockForChildInsertRemove(boolean lockForChildInsertRemove);
+
+   /**
+    * Method that releases object references of cached objects held in the cache by serializing them to byte buffers.
+    * Cached objects are lazily deserialized when accessed again, based on the calling thread's context class loader.
+    * <p/>
+    * This can be expensive, based on the effort required to serialize cached objects.
+    * <p/>
+    *
+    * @param recursive if true, child nodes will have their object references released as well.
+    */
+   void releaseObjectReferences(boolean recursive);
 }

Modified: core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -10,6 +10,7 @@
 import org.apache.commons.logging.LogFactory;
 import org.jboss.cache.factories.annotations.Inject;
 import org.jboss.cache.lock.IdentityLock;
+import org.jboss.cache.marshall.MarshalledValue;
 import org.jboss.cache.marshall.MethodCall;
 import org.jboss.cache.marshall.MethodCallFactory;
 import org.jboss.cache.marshall.MethodDeclarations;
@@ -919,4 +920,36 @@
       if (data == null) return new HashMap(0);
       return new HashMap(data);
    }
+
+   public void releaseObjectReferences(boolean recursive)
+   {
+      if (recursive && children != null)
+      {
+         for (Node child : children.values())
+         {
+            child.releaseObjectReferences(recursive);
+         }
+      }
+
+      if (data != null)
+      {
+         for (Object key : data.keySet())
+         {
+            // get the key first, before attempting to serialize stuff since data.get() may deserialize the key if doing
+            // a hashcode() or equals().
+
+            Object value = data.get(key);
+            if (key instanceof MarshalledValue)
+            {
+               ((MarshalledValue) key).compact(true, true);
+            }
+
+            if (value instanceof MarshalledValue)
+            {
+               ((MarshalledValue) value).compact(true, true);
+            }
+
+         }
+      }
+   }
 }

Modified: core/trunk/src/main/java/org/jboss/cache/config/Configuration.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/config/Configuration.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/config/Configuration.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -304,6 +304,15 @@
       this.evictionConfig = config;
    }
 
+   /**
+    * This is a deprecated configuration option.  While it will be supported for the 2.x series for backward compatibility,
+    * expect to see it disappear in 3.x.
+    * <p/>
+    * With {@link #isUseLazyDeserialization()}, which is enabled by default, custom class loaders are handled implicitly.
+    * See the user guide for details on how this is handled.
+    * <p/>
+    */
+   @Deprecated
    public void setUseRegionBasedMarshalling(boolean useRegionBasedMarshalling)
    {
       testImmutability("useRegionBasedMarshalling");
@@ -578,6 +587,15 @@
       return lockParentForChildInsertRemove;
    }
 
+   /**
+    * This is a deprecated configuration option.  While it will be supported for the 2.x series for backward compatibility,
+    * expect to see it disappear in 3.x.
+    * <p/>
+    * With {@link #isUseLazyDeserialization()}, which is enabled by default, custom class loaders are handled implicitly.
+    * See the user guide for details on how this is handled.
+    * <p/>
+    */
+   @Deprecated
    public boolean isUseRegionBasedMarshalling()
    {
       return useRegionBasedMarshalling;

Modified: core/trunk/src/main/java/org/jboss/cache/config/Option.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/config/Option.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/config/Option.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -250,17 +250,9 @@
             '}';
    }
 
-   public Option clone()
+   public Option clone() throws CloneNotSupportedException
    {
-      try
-      {
-         return (Option) super.clone();
-      }
-      catch (CloneNotSupportedException e)
-      {
-         // should never happen!
-         throw new RuntimeException(e);
-      }
+      return (Option) super.clone();
    }
 
 

Modified: core/trunk/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -95,7 +95,7 @@
 
       // load the tx interceptor
       addInterceptor(first, TxInterceptor.class);
-
+      if (configuration.isUseLazyDeserialization()) addInterceptor(first, MarshalledValueInterceptor.class);
       addInterceptor(first, NotificationInterceptor.class);
 
       switch (configuration.getCacheMode())

Added: core/trunk/src/main/java/org/jboss/cache/interceptors/MarshalledValueInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/MarshalledValueInterceptor.java	                        (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/MarshalledValueInterceptor.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -0,0 +1,121 @@
+package org.jboss.cache.interceptors;
+
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.marshall.MarshalledValue;
+import org.jboss.cache.marshall.MarshalledValueHelper;
+import org.jboss.cache.marshall.MethodCall;
+import org.jboss.cache.marshall.MethodDeclarations;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Interceptor that handles the wrapping and unwrapping of cached data using {@link org.jboss.cache.marshall.MarshalledValue}s.
+ * Known "excluded" types are not wrapped/unwrapped, which at this time include {@link String}, Java primitives
+ * and their Object wrappers, as well as arrays of excluded types.
+ * <p/>
+ * The {@link org.jboss.cache.marshall.MarshalledValue} wrapper handles lazy deserialization from byte array representations.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @see org.jboss.cache.marshall.MarshalledValue
+ * @since 2.1.0
+ */
+public class MarshalledValueInterceptor extends Interceptor
+{
+   @Override
+   public Object invoke(InvocationContext context) throws Throwable
+   {
+      MethodCall call = context.getMethodCall();
+      boolean isAPICall = false;
+      int id = call.getMethodId();
+      Set<MarshalledValue> marshalledValues = null;
+
+      // needs to work for *all* API method calls.
+      if (MethodDeclarations.isAPIMethodCall(id) && id != MethodDeclarations.getNodeMethodLocal_id && id != MethodDeclarations.removeNodeMethodLocal_id)
+      {
+         marshalledValues = new HashSet<MarshalledValue>();
+         isAPICall = true;
+         if (trace) log.trace("Is API method; wrapping any args that need to be wrapped");
+         // check arguments for any user-defined objects that may need to be wrapped.
+         Object[] args = call.getArgs();
+         Object[] replacementArgs = new Object[args.length];
+         int counter = -1;
+
+         for (Object o : args)
+         {
+            counter++;
+            if (MarshalledValueHelper.excludeFromMarshalledValueWrapping(o) || isInternalCollection(counter, id))
+            {
+               replacementArgs[counter] = o;
+            }
+            else if (needToReplaceMap(counter, id))
+            {
+               if (trace) log.trace("Wrapping map contents of argument " + counter);
+               replacementArgs[counter] = wrapMap((Map) o, marshalledValues, context);
+            }
+            else
+            {
+               if (trace) log.trace("Wrapping argument " + counter + " which contains type " + o.getClass());
+               replacementArgs[counter] = createAndAddMarshalledValue(o, marshalledValues, context);
+            }
+         }
+         call.setArgs(replacementArgs);
+      }
+
+      Object retVal = nextInterceptor(context);
+
+      if (isAPICall)
+      {
+         if (trace) log.trace("Compacting MarshalledValues created");
+         for (MarshalledValue mv : marshalledValues) mv.compact(false, false);
+
+         if (retVal instanceof MarshalledValue)
+         {
+            if (trace) log.trace("Return value is a MarshalledValue.  Unwrapping.");
+            return ((MarshalledValue) retVal).get();
+         }
+      }
+
+      return retVal;
+   }
+
+   /**
+    * prepare methods include Maps and Lists in args.  These should not be mistaken for needing wrapping as MarshalledValues.
+    */
+   protected boolean isInternalCollection(int argSubscript, int methodId)
+   {
+      return (methodId == MethodDeclarations.prepareMethod_id && argSubscript == 1) || (methodId == MethodDeclarations.optimisticPrepareMethod_id && (argSubscript == 1 || argSubscript == 2));
+   }
+
+   /**
+    * put(Map) contents should not be wrapped since the map will need to be iterated over.  The contents of the Map, however, should be wrapped.
+    */
+   protected boolean needToReplaceMap(int argSubscript, int methodId)
+   {
+      return ((methodId == MethodDeclarations.putDataEraseMethodLocal_id || methodId == MethodDeclarations.putDataMethodLocal_id) &&
+            argSubscript == 2);
+   }
+
+   @SuppressWarnings("unchecked")
+   protected Map wrapMap(Map m, Set<MarshalledValue> marshalledValues, InvocationContext ctx)
+   {
+      Map copy = new HashMap();
+      for (Object key : m.keySet())
+      {
+         Object value = m.get(key);
+         copy.put(MarshalledValueHelper.excludeFromMarshalledValueWrapping(key) ? key : createAndAddMarshalledValue(key, marshalledValues, ctx),
+               MarshalledValueHelper.excludeFromMarshalledValueWrapping(value) ? value : createAndAddMarshalledValue(value, marshalledValues, ctx));
+      }
+      return copy;
+   }
+
+   protected MarshalledValue createAndAddMarshalledValue(Object toWrap, Set<MarshalledValue> marshalledValues, InvocationContext ctx)
+   {
+      MarshalledValue mv = new MarshalledValue(toWrap);
+      marshalledValues.add(mv);
+      if (!ctx.isOriginLocal()) mv.setEqualityPreferenceForInstance(false);
+      return mv;
+   }
+}

Modified: core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -434,13 +434,13 @@
       return removeNode(Fqn.fromString(fqn));
    }
 
-   public Node getNode(Fqn<?> fqn)
+   public NodeSPI<K, V> getNode(Fqn<?> fqn)
    {
       MethodCall m = MethodCallFactory.create(MethodDeclarations.getNodeMethodLocal_id, fqn);
-      return (Node) invoke(m);
+      return (NodeSPI) invoke(m);
    }
 
-   public Node getNode(String fqn)
+   public NodeSPI<K, V> getNode(String fqn)
    {
       return getNode(Fqn.fromString(fqn));
    }

Modified: core/trunk/src/main/java/org/jboss/cache/invocation/NodeInvocationDelegate.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/invocation/NodeInvocationDelegate.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/invocation/NodeInvocationDelegate.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -274,12 +274,30 @@
       // TODO: Revisit.  Is this really threadsafe?  See comment in putIfAbsent() - same solution should be applied here too.
       assertValid();
       Fqn nf = new Fqn(getFqn(), f);
-      Option o1 = spi.getInvocationContext().getOptionOverrides().clone();
+      Option o1 = null;
+      try
+      {
+         o1 = spi.getInvocationContext().getOptionOverrides().clone();
+      }
+      catch (CloneNotSupportedException e)
+      {
+         // should never happen
+         throw new RuntimeException(e);
+      }
       Node<K, V> child = getChild(f);
 
       if (child == null)
       {
-         Option o2 = o1.clone();
+         Option o2 = null;
+         try
+         {
+            o2 = o1.clone();
+         }
+         catch (CloneNotSupportedException e)
+         {
+            // should never happen
+            throw new RuntimeException(e);
+         }
          spi.getInvocationContext().setOptionOverrides(o1);
          spi.put(nf, null);
 
@@ -442,6 +460,11 @@
       node.setLockForChildInsertRemove(lockForChildInsertRemove);
    }
 
+   public void releaseObjectReferences(boolean recursive)
+   {
+      node.releaseObjectReferences(recursive);
+   }
+
    public boolean hasChildrenDirect()
    {
       return node.hasChildrenDirect();

Modified: core/trunk/src/main/java/org/jboss/cache/marshall/CacheMarshaller200.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/marshall/CacheMarshaller200.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/marshall/CacheMarshaller200.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -52,6 +52,7 @@
    protected static final int MAGICNUMBER_GRAVITATERESULT = 19;
    protected static final int MAGICNUMBER_SHORT = 20;
    protected static final int MAGICNUMBER_MAPCOPY = 21;
+   protected static final int MAGICNUMBER_MARSHALLEDVALUE = 22;
    protected static final int MAGICNUMBER_NULL = 99;
    protected static final int MAGICNUMBER_SERIALIZABLE = 100;
    protected static final int MAGICNUMBER_REF = 101;
@@ -143,12 +144,13 @@
       ClassLoader toUse = defaultClassLoader;
       Thread current = Thread.currentThread();
       ClassLoader old = current.getContextClassLoader();
+      if (old != null) toUse = old;
 
       try
       {
          if (useRegionBasedMarshalling) // got to check again in case this meth is called directly
          {
-            log.trace("Writing region " + region + " to stream");
+            if (trace) log.trace("Writing region " + region + " to stream");
             Region r = null;
             if (region != null) r = regionManager.getRegion(region, false);
             if (r != null && r.getClassLoader() != null) toUse = r.getClassLoader();
@@ -301,6 +303,11 @@
             throw new IllegalArgumentException("MethodCall does not have a valid method id.  Was this method call created with MethodCallFactory?");
          }
       }
+      else if (o instanceof MarshalledValue)
+      {
+         out.writeByte(MAGICNUMBER_MARSHALLEDVALUE);
+         ((MarshalledValue) o).writeExternal(out);
+      }
       else if (o instanceof Fqn)
       {
          out.writeByte(MAGICNUMBER_FQN);
@@ -550,6 +557,10 @@
             retVal = in.readObject();
             refMap.put(reference, retVal);
             return retVal;
+         case MAGICNUMBER_MARSHALLEDVALUE:
+            MarshalledValue mv = new MarshalledValue();
+            mv.readExternal(in);
+            return mv;
          case MAGICNUMBER_METHODCALL:
             retVal = unmarshallMethodCall(in, refMap);
             return retVal;

Added: core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValue.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValue.java	                        (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValue.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -0,0 +1,200 @@
+package org.jboss.cache.marshall;
+
+import org.jboss.cache.CacheException;
+import org.jboss.util.stream.MarshalledValueInputStream;
+
+import java.io.*;
+import java.util.Arrays;
+
+/**
+ * Wrapper that wraps cached data, providing lazy deserialization using the calling thread's context class loader.
+ * <p/>
+ * The {@link org.jboss.cache.interceptors.MarshalledValueInterceptor} handles transparent
+ * wrapping/unwrapping of cached data.
+ * <p/>
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @see org.jboss.cache.interceptors.MarshalledValueInterceptor
+ * @since 2.1.0
+ */
+public class MarshalledValue implements Externalizable
+{
+   protected Object instance;
+   protected byte[] raw;
+   private int cachedHashCode = 0;
+   // by default equals() will test on the istance rather than the byte array if conversion is required.
+   private transient boolean equalityPreferenceForInstance = true;
+
+   public MarshalledValue(Object instance)
+   {
+      if (instance == null) throw new NullPointerException("Null values cannot be wrapped as MarshalledValues!");
+
+      if (instance instanceof Serializable)
+         this.instance = instance;
+      else
+         throw new CacheException(new NotSerializableException("Marshalled values can only wrap Objects that are serializable!  Instance of " + instance.getClass() + " won't Serialize."));
+   }
+
+   public MarshalledValue()
+   {
+      // empty ctor for serialization
+   }
+
+   public void setEqualityPreferenceForInstance(boolean equalityPreferenceForInstance)
+   {
+      this.equalityPreferenceForInstance = equalityPreferenceForInstance;
+   }
+
+   public synchronized void serialize()
+   {
+      if (raw == null)
+      {
+         try
+         {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(baos);
+            oos.writeObject(instance);
+            oos.close();
+            baos.close();
+            // Do NOT set instance to null over here, since it may be used elsewhere (e.g., in a cache listener).
+            // this will be compacted by the MarshalledValueInterceptor when the call returns.
+//            instance = null;
+            raw = baos.toByteArray();
+         }
+         catch (Exception e)
+         {
+            throw new
+                  CacheException("Unable to marshall value " + instance);
+         }
+      }
+   }
+
+   public synchronized void deserialize()
+   {
+      if (instance == null)
+      {
+         try
+         {
+            ByteArrayInputStream bais = new ByteArrayInputStream(raw);
+            // use a MarshalledValueInputStream since it needs to be aware of any context class loaders on the current thread.
+            ObjectInputStream ois = new MarshalledValueInputStream(bais);
+            instance = ois.readObject();
+            ois.close();
+            bais.close();
+//            raw = null;
+         }
+         catch (Exception e)
+         {
+            throw new CacheException("Unable to unmarshall value", e);
+         }
+      }
+   }
+
+   /**
+    * Compacts the references held by this class to a single reference.  If only one representation exists this method
+    * is a no-op unless the 'force' parameter is used, in which case the reference held is forcefully switched to the
+    * 'preferred representation'.
+    * <p/>
+    * Either way, a call to compact() will ensure that only one representation is held.
+    * <p/>
+    *
+    * @param preferSerializedRepresentation if true and both representations exist, the serialized representation is favoured.  If false, the deserialized representation is preferred.
+    * @param force                          ensures the preferred representation is maintained and the other released, even if this means serializing or deserializing.
+    */
+   public void compact(boolean preferSerializedRepresentation, boolean force)
+   {
+      // reset the equalityPreference
+      equalityPreferenceForInstance = true;
+      if (force)
+      {
+         if (preferSerializedRepresentation && raw == null) serialize();
+         else if (!preferSerializedRepresentation && instance == null) deserialize();
+      }
+
+      if (instance != null && raw != null)
+      {
+         // need to lose one representation!
+
+         if (preferSerializedRepresentation)
+         {
+            instance = null;
+         }
+         else
+         {
+            raw = null;
+         }
+      }
+   }
+
+   public void writeExternal(ObjectOutput out) throws IOException
+   {
+      if (raw == null) serialize();
+      out.writeInt(raw.length);
+      out.write(raw);
+      out.writeInt(hashCode());
+   }
+
+   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+   {
+      int size = in.readInt();
+      raw = new byte[size];
+      cachedHashCode = 0;
+      in.read(raw);
+      cachedHashCode = in.readInt();
+   }
+
+   public Object get() throws IOException, ClassNotFoundException
+   {
+      if (instance == null) deserialize();
+      return instance;
+   }
+
+   @Override
+   public boolean equals(Object o)
+   {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      MarshalledValue that = (MarshalledValue) o;
+
+      // if both versions are serialized or deserialized, just compare the relevant representations.
+      if (raw != null && that.raw != null) return Arrays.equals(raw, that.raw);
+      if (instance != null && that.instance != null) return instance.equals(that.instance);
+
+      // if conversion of one representation to the other is necessary, then see which we prefer converting.
+      if (equalityPreferenceForInstance)
+      {
+         if (instance == null) deserialize();
+         if (that.instance == null) that.deserialize();
+         return instance.equals(that.instance);
+      }
+      else
+      {
+         if (raw == null) serialize();
+         if (that.raw == null) that.serialize();
+         return Arrays.equals(raw, that.raw);
+      }
+   }
+
+   @Override
+   public int hashCode()
+   {
+      if (cachedHashCode == 0)
+      {
+         // always calculate the hashcode based on the instance since this is where we're getting the equals()
+         if (instance == null) deserialize();
+         cachedHashCode = instance.hashCode();
+         if (cachedHashCode == 0) // degenerate case
+         {
+            cachedHashCode = 0xFEED;
+         }
+      }
+      return cachedHashCode;
+   }
+
+   @Override
+   public String toString()
+   {
+      return "MarshalledValue(cachedHashCode=" + cachedHashCode + "; serialized=" + (raw != null) + ")";
+   }
+}

Added: core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValueHelper.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValueHelper.java	                        (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValueHelper.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -0,0 +1,56 @@
+package org.jboss.cache.marshall;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.transaction.GlobalTransaction;
+import org.jgroups.Address;
+
+/**
+ * Common functionality used by the {@link org.jboss.cache.interceptors.MarshalledValueInterceptor} and the {@link MarshalledValueMap}.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @see MarshalledValue
+ * @see org.jboss.cache.interceptors.MarshalledValueInterceptor
+ * @see org.jboss.cache.marshall.MarshalledValueMap
+ * @since 2.1.0
+ */
+public class MarshalledValueHelper
+{
+   /**
+    * Tests whether the type should be excluded from MarshalledValue wrapping.
+    *
+    * @param type type to test.  Should not be null.
+    * @return true if it should be excluded from MarshalledValue wrapping.
+    */
+   public static boolean isTypeExcluded(Class type)
+   {
+      return type.equals(String.class) || type.isPrimitive() ||
+            type.equals(Void.class) || type.equals(Boolean.class) || type.equals(Character.class) ||
+            type.equals(Byte.class) || type.equals(Short.class) || type.equals(Integer.class) ||
+            type.equals(Long.class) || type.equals(Float.class) || type.equals(Double.class) ||
+            (type.isArray() && isTypeExcluded(type.getComponentType()));
+   }
+
+   /**
+    * Tests if the type is a JBoss Cache system type, and hence excluded from MarshalledValue wrapping.
+    *
+    * @param type type to test.  Should not be null.
+    * @return true if this is a system type.
+    */
+   public static boolean isSystemType(Class type)
+   {
+      return type.equals(Fqn.class) || type.equals(GlobalTransaction.class) || type.equals(Address.class) ||
+            type.equals(MethodCall.class) || type.equals(MarshalledValue.class);
+   }
+
+   /**
+    * Tests if an object should be excluded from being wrapped as a MarshalledValue.  Excluded if the object is null or
+    * if it's type returns true when passed into {@link #isTypeExcluded(Class)} or {@link #isSystemType(Class)}.
+    *
+    * @param object object to test
+    * @return true if it is to be excluded from MarshalledValue wrapping.
+    */
+   public static boolean excludeFromMarshalledValueWrapping(Object object)
+   {
+      return object == null || isSystemType(object.getClass()) || isTypeExcluded(object.getClass());
+   }
+}

Added: core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValueMap.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValueMap.java	                        (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/marshall/MarshalledValueMap.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -0,0 +1,182 @@
+package org.jboss.cache.marshall;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.cache.CacheException;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A Map that is able to wrap/unwrap MarshalledValues in keys or values.  Note that calling keySet(), entrySet() or values()
+ * could be expensive if this map is large!!
+ * <p/>
+ * Also note that this is an immutable Map.
+ * <p/>
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @see MarshalledValue
+ * @since 2.1.0
+ */
+ at Immutable
+public class MarshalledValueMap implements Map
+{
+   Map delegate;
+   Map<Object, MarshalledValue> marshalledValues;
+
+   public MarshalledValueMap(Map delegate)
+   {
+      this.delegate = delegate;
+   }
+
+   protected MarshalledValue getMarshalledValue(Object o)
+   {
+      if (marshalledValues == null) marshalledValues = new HashMap<Object, MarshalledValue>();
+      if (marshalledValues.containsKey(o)) return marshalledValues.get(o);
+      MarshalledValue mv = new MarshalledValue(o);
+      marshalledValues.put(o, mv);
+      return mv;
+   }
+
+   public int size()
+   {
+      return delegate.size();
+   }
+
+   public boolean isEmpty()
+   {
+      return delegate.isEmpty();
+   }
+
+   public boolean containsKey(Object key)
+   {
+      if (MarshalledValueHelper.excludeFromMarshalledValueWrapping(key))
+      {
+         return delegate.containsKey(key);
+      }
+      else
+      {
+         return delegate.containsKey(getMarshalledValue(key));
+      }
+   }
+
+   public boolean containsValue(Object value)
+   {
+      if (MarshalledValueHelper.excludeFromMarshalledValueWrapping(value))
+      {
+         return delegate.containsValue(value);
+      }
+      else
+      {
+         return delegate.containsValue(getMarshalledValue(value));
+      }
+   }
+
+   public Object get(Object key)
+   {
+      Object retVal;
+      if (MarshalledValueHelper.excludeFromMarshalledValueWrapping(key))
+      {
+         retVal = delegate.get(key);
+      }
+      else
+      {
+         retVal = delegate.get(getMarshalledValue(key));
+      }
+
+      if (retVal instanceof MarshalledValue) try
+      {
+         retVal = ((MarshalledValue) retVal).get();
+      }
+      catch (Exception e)
+      {
+         throw new CacheException("Unable to unmarshall MarshalledValue", e);
+      }
+
+      return retVal;
+   }
+
+   public Object put(Object key, Object value)
+   {
+      throw new UnsupportedOperationException("This is an immutable map!");
+   }
+
+   public Object remove(Object key)
+   {
+      throw new UnsupportedOperationException("This is an immutable map!");
+   }
+
+   public void putAll(Map t)
+   {
+      throw new UnsupportedOperationException("This is an immutable map!");
+   }
+
+   public void clear()
+   {
+      throw new UnsupportedOperationException("This is an immutable map!");
+   }
+
+   public Set keySet()
+   {
+      try
+      {
+         return unmarshallSet(delegate.keySet());
+      }
+      catch (Exception e)
+      {
+         throw new CacheException("Unable to unmarshall MarshalledValues in collection", e);
+      }
+   }
+
+   public Collection values()
+   {
+      try
+      {
+         return unmarshallList(delegate.values());
+      }
+      catch (Exception e)
+      {
+         throw new CacheException("Unable to unmarshall MarshalledValues in collection", e);
+      }
+   }
+
+   public Set entrySet()
+   {
+      try
+      {
+         return unmarshallSet(delegate.entrySet());
+      }
+      catch (Exception e)
+      {
+         throw new CacheException("Unable to unmarshall MarshalledValues in collection", e);
+      }
+   }
+
+   @SuppressWarnings("unchecked")
+   protected Set unmarshallSet(Set set) throws IOException, ClassNotFoundException
+   {
+      Set newSet = new HashSet();
+      for (Object o : set)
+      {
+         newSet.add(o instanceof MarshalledValue ? ((MarshalledValue) o).get() : o);
+      }
+      return Collections.unmodifiableSet(newSet);
+   }
+
+   @SuppressWarnings("unchecked")
+   protected List unmarshallList(Collection c) throws IOException, ClassNotFoundException
+   {
+      List newList = new LinkedList();
+      for (Object o : c)
+      {
+         newList.add(o instanceof MarshalledValue ? ((MarshalledValue) o).get() : o);
+      }
+      return Collections.unmodifiableList(newList);
+   }
+}

Modified: core/trunk/src/main/java/org/jboss/cache/marshall/MethodDeclarations.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/marshall/MethodDeclarations.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/marshall/MethodDeclarations.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -555,5 +555,17 @@
          throw new CacheException("Attempting to look up a versioned equivalent of a non-crud method");
       }
    }
+
+   /**
+    * Returns true if the method call is declared in the public Cache API.  This includes all put, get, remove, getChildrenNames,
+    * exists calls, but not calls like commit, rollback, prepare, block, unblock, etc.
+    *
+    * @param id method call id to test
+    * @return true if this is an API method call.
+    */
+   public static boolean isAPIMethodCall(int id)
+   {
+      return isCrudMethod(id) || isGetMethod(id);
+   }
 }
 

Modified: core/trunk/src/main/java/org/jboss/cache/notifications/Notifier.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/notifications/Notifier.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/notifications/Notifier.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -15,6 +15,7 @@
 import org.jboss.cache.InvocationContext;
 import org.jboss.cache.buddyreplication.BuddyGroup;
 import org.jboss.cache.factories.annotations.Inject;
+import org.jboss.cache.marshall.MarshalledValueMap;
 import org.jboss.cache.notifications.annotation.*;
 import org.jboss.cache.notifications.event.*;
 import static org.jboss.cache.notifications.event.Event.Type.*;
@@ -634,8 +635,8 @@
 
    private static Map copy(Map data)
    {
-      if (safe(data)) return data;
-      return new MapCopy(data);
+      if (safe(data)) return new MarshalledValueMap(data);
+      return new MarshalledValueMap(new MapCopy(data));
    }
 
    private void restoreInvocationContext(InvocationContext backup)

Modified: core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -389,6 +389,11 @@
       throw new UnsupportedOperationException();
    }
 
+   public void releaseObjectReferences(boolean recursive)
+   {
+      throw new UnsupportedOperationException();
+   }
+
    public NodeSPI<K, V> getChild(Fqn f)
    {
       if (f.size() > 1)

Modified: core/trunk/src/test/java/org/jboss/cache/factories/InterceptorChainFactoryTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/factories/InterceptorChainFactoryTest.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/test/java/org/jboss/cache/factories/InterceptorChainFactoryTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -29,6 +29,7 @@
    {
       cache = (CacheSPI) new DefaultCacheFactory().createCache(false);
       cache.getConfiguration().setCacheMode("LOCAL");
+      cache.getConfiguration().setUseLazyDeserialization(false);
    }
 
    @AfterMethod(alwaysRun = true)

Added: core/trunk/src/test/java/org/jboss/cache/interceptors/MarshalledValueInterceptorTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/interceptors/MarshalledValueInterceptorTest.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/interceptors/MarshalledValueInterceptorTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -0,0 +1,140 @@
+package org.jboss.cache.interceptors;
+
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.DefaultCacheFactory;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.marshall.MarshalledValueHelper;
+import org.jboss.cache.misc.TestingUtil;
+import org.jboss.cache.util.CachePrinter;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at Test(groups = "functional")
+public class MarshalledValueInterceptorTest
+{
+   CacheSPI c;
+
+   @AfterMethod
+   public void tearDown()
+   {
+      TestingUtil.killCaches(c);
+   }
+
+   public void testDefaultInterceptorStack()
+   {
+      c = (CacheSPI) new DefaultCacheFactory().createCache();
+
+      System.out.println(CachePrinter.printCacheInterceptors(c));
+      assert TestingUtil.findInterceptor(c, MarshalledValueInterceptor.class) != null;
+   }
+
+   public void testEnabledInterceptorStack()
+   {
+      Configuration cfg = new Configuration();
+      cfg.setUseLazyDeserialization(true);
+      c = (CacheSPI) new DefaultCacheFactory().createCache(cfg);
+
+      System.out.println(CachePrinter.printCacheInterceptors(c));
+      assert TestingUtil.findInterceptor(c, MarshalledValueInterceptor.class) != null;
+   }
+
+   public void testDisabledInterceptorStack()
+   {
+      Configuration cfg = new Configuration();
+      cfg.setUseLazyDeserialization(false);
+      c = (CacheSPI) new DefaultCacheFactory().createCache(cfg);
+
+      System.out.println(CachePrinter.printCacheInterceptors(c));
+      assert TestingUtil.findInterceptor(c, MarshalledValueInterceptor.class) == null;
+   }
+
+   public void testExcludedTypes()
+   {
+      // Strings
+      assert MarshalledValueHelper.isTypeExcluded(String.class);
+      assert MarshalledValueHelper.isTypeExcluded(String[].class);
+      assert MarshalledValueHelper.isTypeExcluded(String[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(String[][][].class);
+
+      // primitives
+      assert MarshalledValueHelper.isTypeExcluded(void.class);
+      assert MarshalledValueHelper.isTypeExcluded(boolean.class);
+      assert MarshalledValueHelper.isTypeExcluded(char.class);
+      assert MarshalledValueHelper.isTypeExcluded(byte.class);
+      assert MarshalledValueHelper.isTypeExcluded(short.class);
+      assert MarshalledValueHelper.isTypeExcluded(int.class);
+      assert MarshalledValueHelper.isTypeExcluded(long.class);
+      assert MarshalledValueHelper.isTypeExcluded(float.class);
+      assert MarshalledValueHelper.isTypeExcluded(double.class);
+
+      assert MarshalledValueHelper.isTypeExcluded(boolean[].class);
+      assert MarshalledValueHelper.isTypeExcluded(char[].class);
+      assert MarshalledValueHelper.isTypeExcluded(byte[].class);
+      assert MarshalledValueHelper.isTypeExcluded(short[].class);
+      assert MarshalledValueHelper.isTypeExcluded(int[].class);
+      assert MarshalledValueHelper.isTypeExcluded(long[].class);
+      assert MarshalledValueHelper.isTypeExcluded(float[].class);
+      assert MarshalledValueHelper.isTypeExcluded(double[].class);
+
+      assert MarshalledValueHelper.isTypeExcluded(boolean[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(char[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(byte[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(short[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(int[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(long[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(float[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(double[][].class);
+
+      assert MarshalledValueHelper.isTypeExcluded(Void.class);
+      assert MarshalledValueHelper.isTypeExcluded(Boolean.class);
+      assert MarshalledValueHelper.isTypeExcluded(Character.class);
+      assert MarshalledValueHelper.isTypeExcluded(Byte.class);
+      assert MarshalledValueHelper.isTypeExcluded(Short.class);
+      assert MarshalledValueHelper.isTypeExcluded(Integer.class);
+      assert MarshalledValueHelper.isTypeExcluded(Long.class);
+      assert MarshalledValueHelper.isTypeExcluded(Float.class);
+      assert MarshalledValueHelper.isTypeExcluded(Double.class);
+
+      assert MarshalledValueHelper.isTypeExcluded(Boolean[].class);
+      assert MarshalledValueHelper.isTypeExcluded(Character[].class);
+      assert MarshalledValueHelper.isTypeExcluded(Byte[].class);
+      assert MarshalledValueHelper.isTypeExcluded(Short[].class);
+      assert MarshalledValueHelper.isTypeExcluded(Integer[].class);
+      assert MarshalledValueHelper.isTypeExcluded(Long[].class);
+      assert MarshalledValueHelper.isTypeExcluded(Float[].class);
+      assert MarshalledValueHelper.isTypeExcluded(Double[].class);
+
+      assert MarshalledValueHelper.isTypeExcluded(Boolean[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(Character[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(Byte[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(Short[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(Integer[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(Long[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(Float[][].class);
+      assert MarshalledValueHelper.isTypeExcluded(Double[][].class);
+   }
+
+   public void testNonExcludedTypes()
+   {
+      assert !MarshalledValueHelper.isTypeExcluded(Object.class);
+      assert !MarshalledValueHelper.isTypeExcluded(List.class);
+      assert !MarshalledValueHelper.isTypeExcluded(Collection.class);
+      assert !MarshalledValueHelper.isTypeExcluded(Map.class);
+      assert !MarshalledValueHelper.isTypeExcluded(Date.class);
+      assert !MarshalledValueHelper.isTypeExcluded(Thread.class);
+      assert !MarshalledValueHelper.isTypeExcluded(Collection.class);
+      assert !MarshalledValueHelper.isTypeExcluded(new Object()
+      {
+         String blah;
+      }.getClass());
+   }
+}

Modified: core/trunk/src/test/java/org/jboss/cache/lock/IdentityLockTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/lock/IdentityLockTest.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/test/java/org/jboss/cache/lock/IdentityLockTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -7,18 +7,12 @@
 package org.jboss.cache.lock;
 
 
-import static org.testng.AssertJUnit.assertFalse;
-import static org.testng.AssertJUnit.assertTrue;
-import static org.testng.AssertJUnit.fail;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.jboss.cache.NodeSPI;
 import org.jboss.cache.misc.TestingUtil;
 import org.jboss.cache.transaction.GlobalTransaction;
 import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
+
 /**
  * Testing of different locking semantics.
  *
@@ -278,6 +272,7 @@
       };
 
       t1.start();
+      TestingUtil.sleepThread(100);
       t2.start();
       TestingUtil.sleepThread(1000);
 

Modified: core/trunk/src/test/java/org/jboss/cache/lock/LockReleaseTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/lock/LockReleaseTest.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/test/java/org/jboss/cache/lock/LockReleaseTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -157,7 +157,7 @@
       cache.put(NODE2, KEY, VAL1);
       assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld());
 
-      Set<String> keys = cache.getNode(NODE2).getChildrenNames();
+      Set<Object> keys = cache.getNode(NODE2).getChildrenNames();
       System.out.println("keys of " + NODE2 + " are " + keys);
       assertEquals("getChildrenNames() called outside the TX should have released all locks", 0,
             cache.getNumberOfLocksHeld());

Added: core/trunk/src/test/java/org/jboss/cache/marshall/AsyncReplMarshalledValuesTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/AsyncReplMarshalledValuesTest.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/AsyncReplMarshalledValuesTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -0,0 +1,13 @@
+package org.jboss.cache.marshall;
+
+/**
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+public class AsyncReplMarshalledValuesTest extends AsyncReplTest
+{
+   public AsyncReplMarshalledValuesTest()
+   {
+      useMarshalledValues = true;
+   }
+}

Modified: core/trunk/src/test/java/org/jboss/cache/marshall/AsyncReplTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/AsyncReplTest.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/AsyncReplTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -47,6 +47,7 @@
    Address addr_;
    Throwable ex_;
    private Fqn<String> aop = Fqn.fromString("/aop");
+   protected boolean useMarshalledValues = false;
 
    @BeforeMethod(alwaysRun = true)
    public void setUp() throws Exception
@@ -73,7 +74,9 @@
       CacheSPI<Object, Object> cache = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_ASYNC), false);
       cache.getConfiguration().setClusterName(name);
       // Use marshaller
-      cache.getConfiguration().setUseRegionBasedMarshalling(true);
+      cache.getConfiguration().setUseLazyDeserialization(useMarshalledValues);
+      cache.getConfiguration().setUseRegionBasedMarshalling(!useMarshalledValues);
+
       cache.create();
       cache.start();
       return cache;

Modified: core/trunk/src/test/java/org/jboss/cache/marshall/CustomCollectionTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/CustomCollectionTest.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/CustomCollectionTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -6,11 +6,13 @@
 import org.jboss.cache.Region;
 import org.jboss.cache.config.Configuration;
 import org.jboss.cache.lock.IsolationLevel;
+import static org.jboss.cache.marshall.CustomCollectionTest.MarshallingMode.*;
 import static org.testng.AssertJUnit.*;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import java.io.Serializable;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -22,10 +24,10 @@
  * @author <a href="mailto:manik at jboss.org">Manik Surtani (manik at jboss.org)</a>
  */
 @Test(groups = {"functional"})
-public class CustomCollectionTest
+public class CustomCollectionTest extends RegionBasedMarshallingTestBase implements Serializable
 {
-   private Cache<Object, Object> cache1 = null;
-   private Cache<Object, Object> cache2 = null;
+   private transient Cache<Object, Object> cache1 = null;
+   private transient Cache<Object, Object> cache2 = null;
    private String myListClass = MyList.class.getName();
    private String mySetClass = MySet.class.getName();
    private String myMapClass = MyMap.class.getName();
@@ -46,55 +48,96 @@
 
    public void testMap() throws Exception
    {
-      doMapTest(false);
+      doMapTest(DEFAULT_WITHOUT_MARSHALLED_VALUES);
    }
 
    public void testMapWithRegions() throws Exception
    {
-      doMapTest(true);
+      doMapTest(CUSTOM_CLASSLOADER_WITH_REGIONS);
    }
 
+   public void testMapWithMarshalledValues() throws Exception
+   {
+      doMapTest(CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES);
+   }
+
+   public void testMapWithMarshalledValuesDefaultClassloader() throws Exception
+   {
+      doMapTest(DEFAULT_WITH_MARSHALLED_VALUES);
+   }
+
    public void testSet() throws Exception
    {
-      doSetTest(false);
+      doSetTest(DEFAULT_WITHOUT_MARSHALLED_VALUES);
    }
 
    public void testSetWithRegions() throws Exception
    {
-      doSetTest(true);
+      doSetTest(CUSTOM_CLASSLOADER_WITH_REGIONS);
    }
 
+   public void testSetWithMarshalledValues() throws Exception
+   {
+      doSetTest(CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES);
+   }
+
+   public void testSetWithMarshalledValuesDefaultClassloader() throws Exception
+   {
+      doSetTest(DEFAULT_WITH_MARSHALLED_VALUES);
+   }
+
    public void testList() throws Exception
    {
-      doListTest(false);
+      doListTest(DEFAULT_WITHOUT_MARSHALLED_VALUES);
    }
 
    public void testListWithRegions() throws Exception
    {
-      doListTest(true);
+      doListTest(CUSTOM_CLASSLOADER_WITH_REGIONS);
    }
 
+   public void testListWithMarshalledValues() throws Exception
+   {
+      doListTest(CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES);
+   }
+
+   public void testListWithMarshalledValuesDefaultClassloader() throws Exception
+   {
+      doListTest(DEFAULT_WITH_MARSHALLED_VALUES);
+   }
+
+   enum MarshallingMode
+   {
+      DEFAULT_WITH_MARSHALLED_VALUES, DEFAULT_WITHOUT_MARSHALLED_VALUES, CUSTOM_CLASSLOADER_WITH_REGIONS, CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES;
+
+      boolean isUsingCustomClassLoader()
+      {
+         return this == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES || this == CUSTOM_CLASSLOADER_WITH_REGIONS;
+      }
+   }
+
    @SuppressWarnings("unchecked")
-   private void doMapTest(boolean contextClassLoader) throws Exception
+   private void doMapTest(MarshallingMode marshallingMode) throws Exception
    {
-      ClassLoader customClassLoader = getClassLoader();
+      ClassLoader customClassLoader = getCollectionsClassLoader();
       Class mapClass = customClassLoader.loadClass(myMapClass);
-      Map map = (Map) (contextClassLoader ? mapClass.newInstance() : new MyMap());
+      Map map = (Map) (marshallingMode.isUsingCustomClassLoader() ? mapClass.newInstance() : new MyMap());
 
       map.put("k", "v");
 
-      if (contextClassLoader)
-      {
-         enableRegionBasedClassLoading(customClassLoader);
-      }
+      configureCaches(customClassLoader, marshallingMode);
 
       cache1.start();
       cache2.start();
 
+      if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES)
+         Thread.currentThread().setContextClassLoader(customClassLoader);
       cache1.put(fqn("/a"), "key", map);
       Object o = cache2.get(fqn("/a"), "key");
+      if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES) resetContextClassLoader();
+
       assertTrue(o instanceof Map);
-      if (contextClassLoader)
+      if (marshallingMode.isUsingCustomClassLoader())
       {
          assertNotSame(MyMap.class, o.getClass());
          assertSame(mapClass, o.getClass());
@@ -108,27 +151,27 @@
    }
 
    @SuppressWarnings("unchecked")
-   private void doSetTest(boolean contextClassLoader) throws Exception
+   private void doSetTest(MarshallingMode marshallingMode) throws Exception
    {
-
-      ClassLoader customClassLoader = getClassLoader();
+      ClassLoader customClassLoader = getCollectionsClassLoader();
       Class setClass = customClassLoader.loadClass(mySetClass);
-      Set set = (Set) (contextClassLoader ? setClass.newInstance() : new MySet());
+      Set set = (Set) (marshallingMode.isUsingCustomClassLoader() ? setClass.newInstance() : new MySet());
 
       set.add("k");
 
-      if (contextClassLoader)
-      {
-         enableRegionBasedClassLoading(customClassLoader);
-      }
+      configureCaches(customClassLoader, marshallingMode);
 
       cache1.start();
       cache2.start();
 
+      if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES)
+         Thread.currentThread().setContextClassLoader(customClassLoader);
       cache1.put(fqn("/a"), "key", set);
       Object o = cache2.get(fqn("/a"), "key");
+      if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES) resetContextClassLoader();
+
       assertTrue(o instanceof Set);
-      if (contextClassLoader)
+      if (marshallingMode.isUsingCustomClassLoader())
       {
          assertNotSame(MySet.class, o.getClass());
          assertSame(setClass, o.getClass());
@@ -142,28 +185,29 @@
    }
 
    @SuppressWarnings("unchecked")
-   private void doListTest(boolean contextClassLoader) throws Exception
+   private void doListTest(MarshallingMode marshallingMode) throws Exception
    {
 
-      ClassLoader customClassLoader = getClassLoader();
+      ClassLoader customClassLoader = getCollectionsClassLoader();
       Class listClass = customClassLoader.loadClass(myListClass);
 
-      List list = (List) (contextClassLoader ? listClass.newInstance() : new MyList());
+      List list = (List) (marshallingMode.isUsingCustomClassLoader() ? listClass.newInstance() : new MyList());
 
       list.add("k");
 
-      if (contextClassLoader)
-      {
-         enableRegionBasedClassLoading(customClassLoader);
-      }
+      configureCaches(customClassLoader, marshallingMode);
 
       cache1.start();
       cache2.start();
 
+      if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES)
+         Thread.currentThread().setContextClassLoader(customClassLoader);
       cache1.put(fqn("/a"), "key", list);
       Object o = cache2.get(fqn("/a"), "key");
+      if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES) resetContextClassLoader();
+
       assertTrue(o instanceof List);
-      if (contextClassLoader)
+      if (marshallingMode.isUsingCustomClassLoader())
       {
          assertSame(listClass, o.getClass());
          assertNotSame(MyList.class, o.getClass());
@@ -178,7 +222,7 @@
 
    public void testHarnessClassLoader() throws Exception
    {
-      Class myListFromCustomLoader = getClassLoader().loadClass(myListClass);
+      Class myListFromCustomLoader = getCollectionsClassLoader().loadClass(myListClass);
       assertNotNull(myListFromCustomLoader);
       assertFalse(MyList.class.equals(myListFromCustomLoader));
 
@@ -195,23 +239,31 @@
       }
    }
 
-   private void enableRegionBasedClassLoading(ClassLoader customClassLoader)
+   private void configureCaches(ClassLoader customClassLoader, MarshallingMode marshallingMode)
    {
-      cache1.getConfiguration().setUseRegionBasedMarshalling(true);
-      Region region1 = cache1.getRegion(fqn("/a"), true);
-      region1.registerContextClassLoader(customClassLoader);
-//      cache1.getRegionManager().createRegion("/a", customClassLoader);
-//      cache1.getMarshaller().defaultMarshaller.registerClassLoader("/a", customClassLoader);
-//      cache1.getMarshaller().defaultMarshaller.useRegionBasedMarshalling = true;
-      cache2.getConfiguration().setUseRegionBasedMarshalling(true);
-      Region region2 = cache2.getRegion(fqn("/a"), true);
-      region2.registerContextClassLoader(customClassLoader);
-//      cache2.getRegionManager().createRegion("/a", customClassLoader);
-//      cache2.getMarshaller().defaultMarshaller.registerClassLoader("/a", customClassLoader);
-//      cache2.getMarshaller().defaultMarshaller.useRegionBasedMarshalling = true;
+      switch (marshallingMode)
+      {
+         case CUSTOM_CLASSLOADER_WITH_REGIONS:
+            cache1.getConfiguration().setUseRegionBasedMarshalling(true);
+            Region region1 = cache1.getRegion(fqn("/a"), true);
+            region1.registerContextClassLoader(customClassLoader);
+            cache2.getConfiguration().setUseRegionBasedMarshalling(true);
+            Region region2 = cache2.getRegion(fqn("/a"), true);
+            region2.registerContextClassLoader(customClassLoader);
+            // and carry on to the same setting as DEFAULT_WITHOUT_MARSHALLED_VALUES
+         case DEFAULT_WITHOUT_MARSHALLED_VALUES:
+            cache1.getConfiguration().setUseLazyDeserialization(false);
+            cache2.getConfiguration().setUseLazyDeserialization(false);
+            break;
+         case DEFAULT_WITH_MARSHALLED_VALUES:
+         case CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES:
+            cache1.getConfiguration().setUseLazyDeserialization(true);
+            cache2.getConfiguration().setUseLazyDeserialization(true);
+            break;
+      }
    }
 
-   private ClassLoader getClassLoader()
+   private ClassLoader getCollectionsClassLoader()
    {
       String[] includesClasses = {myListClass, mySetClass, myMapClass};
       String[] excludesClasses = {};

Added: core/trunk/src/test/java/org/jboss/cache/marshall/MarshalledValueTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/MarshalledValueTest.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/MarshalledValueTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -0,0 +1,375 @@
+package org.jboss.cache.marshall;
+
+import org.jboss.cache.CacheException;
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.DefaultCacheFactory;
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.NodeSPI;
+import org.jboss.cache.config.CacheLoaderConfig;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.factories.ComponentRegistry;
+import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
+import org.jboss.cache.interceptors.Interceptor;
+import org.jboss.cache.interceptors.MarshalledValueInterceptor;
+import org.jboss.cache.loader.DummyInMemoryCacheLoader;
+import org.jboss.cache.misc.TestingUtil;
+import org.jboss.cache.notifications.annotation.CacheListener;
+import org.jboss.cache.notifications.annotation.NodeModified;
+import org.jboss.cache.notifications.event.NodeModifiedEvent;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.util.Map;
+
+/**
+ * Tests implicit marshalled values
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at Test(groups = "functional")
+public class MarshalledValueTest
+{
+   private CacheSPI<Object, Object> cache1, cache2;
+   private MarshalledValueListenerInterceptor mvli;
+
+
+   @BeforeMethod
+   public void setUp()
+   {
+      cache1 = (CacheSPI<Object, Object>) new DefaultCacheFactory<Object, Object>().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC));
+      cache2 = (CacheSPI<Object, Object>) new DefaultCacheFactory<Object, Object>().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC));
+
+      assert TestingUtil.findInterceptor(cache1, MarshalledValueInterceptor.class) != null : "Marshalled value interceptor not in chain!";
+      assert TestingUtil.findInterceptor(cache2, MarshalledValueInterceptor.class) != null : "Marshalled value interceptor not in chain!";
+
+      mvli = new MarshalledValueListenerInterceptor();
+      cache1.addInterceptor(mvli, MarshalledValueInterceptor.class);
+   }
+
+   @AfterMethod
+   public void tearDown()
+   {
+      TestingUtil.killCaches(cache1, cache2);
+      Pojo.serializationCount = 0;
+      Pojo.deserializationCount = 0;
+   }
+
+   private void assertOnlyOneRepresentationExists(MarshalledValue mv)
+   {
+      assert (mv.instance != null && mv.raw == null) || (mv.instance == null && mv.raw != null) : "Only instance or raw representations should exist in a MarshalledValue; never both";
+   }
+
+   private void assertSerialized(MarshalledValue mv)
+   {
+      assert mv.raw != null : "Should be serialized";
+   }
+
+   private void assertDeserialized(MarshalledValue mv)
+   {
+      assert mv.instance != null : "Should be deserialized";
+   }
+
+   private void assertSerializationCounts(int serializationCount, int deserializationCount)
+   {
+      assert Pojo.serializationCount == serializationCount : "Serialization count: expected " + serializationCount + " but was " + Pojo.serializationCount;
+      assert Pojo.deserializationCount == deserializationCount : "Deserialization count: expected " + deserializationCount + " but was " + Pojo.deserializationCount;
+   }
+
+   public void testNonSerializable()
+   {
+      try
+      {
+         cache1.put("/a", "Hello", new Object());
+         assert false : "Should have failed";
+      }
+      catch (CacheException expected)
+      {
+
+      }
+
+      assert mvli.invocationCount == 0 : "Call should not have gone beyond the MarshalledValueInterceptor";
+
+      try
+      {
+         cache1.put("/a", new Object(), "Hello");
+         assert false : "Should have failed";
+      }
+      catch (CacheException expected)
+      {
+
+      }
+
+      assert mvli.invocationCount == 0 : "Call should not have gone beyond the MarshalledValueInterceptor";
+   }
+
+   public void testNodeReleaseObjectValueReferences()
+   {
+      Pojo value = new Pojo();
+      cache1.put("/a", "key", value);
+      assertSerializationCounts(1, 0);
+      NodeSPI<Object, Object> node = cache1.getNode("/a");
+      Object o = node.getDirect("key");
+      assert o instanceof MarshalledValue;
+      MarshalledValue mv = (MarshalledValue) o;
+      assertDeserialized(mv);
+      assert node.get("key").equals(value);
+      assertDeserialized(mv);
+      assertSerializationCounts(1, 0);
+      node.releaseObjectReferences(false);
+      assertSerializationCounts(2, 0);
+      assertOnlyOneRepresentationExists(mv);
+      assertSerialized(mv);
+
+      // now on cache 2
+      node = cache2.getNode("/a");
+      o = node.getDirect("key");
+      assert o instanceof MarshalledValue;
+      mv = (MarshalledValue) o;
+      assertSerialized(mv); // this proves that unmarshalling on the recipient cache instance is lazy
+
+      assert node.get("key").equals(value);
+      assertDeserialized(mv);
+      assertSerializationCounts(2, 1);
+      node.releaseObjectReferences(false);
+      assertSerializationCounts(2, 1);
+      assertOnlyOneRepresentationExists(mv);
+      assertSerialized(mv);
+   }
+
+   public void testNodeReleaseObjectKeyReferences() throws IOException, ClassNotFoundException
+   {
+      Pojo key = new Pojo();
+      cache1.put("/a", key, "value");
+
+      assertSerializationCounts(1, 0);
+
+      NodeSPI<Object, Object> node = cache1.getNode("/a");
+      Object o = node.getKeysDirect().iterator().next();
+      assert o instanceof MarshalledValue;
+      MarshalledValue mv = (MarshalledValue) o;
+      assertDeserialized(mv);
+
+      assert node.get(key).equals("value");
+      assertDeserialized(mv);
+      assertSerializationCounts(1, 0);
+      node.releaseObjectReferences(false);
+      assertSerializationCounts(2, 0);
+      assertOnlyOneRepresentationExists(mv);
+      assertSerialized(mv);
+
+      // now on cache 2
+      node = cache2.getNode("/a");
+      o = node.getKeysDirect().iterator().next();
+      assert o instanceof MarshalledValue;
+      mv = (MarshalledValue) o;
+      assertSerialized(mv);
+      assert node.get(key).equals("value");
+      assertSerializationCounts(2, 1);
+      assertDeserialized(mv);
+      node.releaseObjectReferences(false);
+
+      assertOnlyOneRepresentationExists(mv);
+      assertSerialized(mv);
+      assertSerializationCounts(2, 1);
+   }
+
+   public void testEqualsAndHashCode() throws Exception
+   {
+      Pojo pojo = new Pojo();
+      MarshalledValue mv = new MarshalledValue(pojo);
+      assertDeserialized(mv);
+      int oldHashCode = mv.hashCode();
+
+      mv.serialize();
+      assertSerialized(mv);
+      assert oldHashCode == mv.hashCode();
+
+      MarshalledValue mv2 = new MarshalledValue(pojo);
+      assertSerialized(mv);
+      assertDeserialized(mv2);
+
+      assert mv2.hashCode() == oldHashCode;
+      assert mv.equals(mv2);
+   }
+
+   public void assertUseOfMagicNumbers() throws Exception
+   {
+      Pojo pojo = new Pojo();
+      MarshalledValue mv = new MarshalledValue(pojo);
+
+      Configuration c = new Configuration();
+      ComponentRegistry cr = new ComponentRegistry(c);
+
+      Marshaller marshaller = new CacheMarshaller210();
+      cr.registerComponent(marshaller, Marshaller.class);
+
+      // Wire the marshaller
+      cr.start();
+
+      // start the test
+      ByteArrayOutputStream bout = new ByteArrayOutputStream();
+      ObjectOutputStream out = new ObjectOutputStream(bout);
+      marshaller.objectToObjectStream(mv, out);
+      out.close();
+      bout.close();
+
+      // check that the rest just contains a byte stream which a MarshalledValue will be able to deserialize.
+      ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
+      ObjectInputStream in = new ObjectInputStream(bin);
+
+      assert in.read() == CacheMarshaller200.MAGICNUMBER_MARSHALLEDVALUE;
+      MarshalledValue recreated = new MarshalledValue();
+      recreated.readExternal(in);
+
+      // there should be nothing more
+      assert in.available() == 0;
+      in.close();
+      bin.close();
+
+      assertSerialized(recreated);
+      assert recreated.equals(mv);
+
+      // since both objects being compared are serialized, the equals() above should just compare byte arrays.  
+      assertSerialized(recreated);
+      assertOnlyOneRepresentationExists(recreated);
+   }
+
+   public void testCacheLoaders() throws CloneNotSupportedException
+   {
+      tearDown();
+      cache1 = (CacheSPI<Object, Object>) new DefaultCacheFactory<Object, Object>().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC), false);
+      cache2 = (CacheSPI<Object, Object>) new DefaultCacheFactory<Object, Object>().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC), false);
+
+      CacheLoaderConfig clc = new CacheLoaderConfig();
+      CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig();
+      iclc.setClassName(DummyInMemoryCacheLoader.class.getName());
+      clc.addIndividualCacheLoaderConfig(iclc);
+      cache1.getConfiguration().setCacheLoaderConfig(clc);
+      cache2.getConfiguration().setCacheLoaderConfig(clc.clone());
+
+      cache1.start();
+      cache2.start();
+
+      TestingUtil.blockUntilViewsReceived(60000, cache1, cache2);
+
+      Pojo pojo = new Pojo();
+      cache1.put("/a", "key", pojo);
+
+      assertSerializationCounts(1, 0);
+
+      cache2.get("/a", "key");
+
+      assertSerializationCounts(1, 1);
+   }
+
+   public void testCallbackValues()
+   {
+      Listener l = new Listener();
+      cache1.addCacheListener(l);
+      Pojo pojo = new Pojo();
+      cache1.put("/a", "key", pojo);
+
+      assert l.modData.size() == 1;
+      assert l.modData.get("key") instanceof Pojo;
+      assertSerializationCounts(1, 0);
+   }
+
+   public void testRemoteCallbackValues()
+   {
+      Listener l = new Listener();
+      cache2.addCacheListener(l);
+      Pojo pojo = new Pojo();
+      cache1.put("/a", "key", pojo);
+
+      assert l.modData.size() == 1;
+      pojo = (Pojo) l.modData.get("key");
+      assert pojo != null;
+      assertSerializationCounts(1, 1);
+   }
+
+   @CacheListener
+   public static class Listener
+   {
+      Map modData;
+
+      @NodeModified
+      public void nodeModified(NodeModifiedEvent e)
+      {
+         if (!e.isPre()) modData = e.getData();
+      }
+   }
+
+   class MarshalledValueListenerInterceptor extends Interceptor
+   {
+      int invocationCount = 0;
+
+      public Object invoke(InvocationContext ctx) throws Throwable
+      {
+         invocationCount++;
+         MethodCall mc = ctx.getMethodCall();
+         for (Object arg : mc.getArgs())
+         {
+            if (arg instanceof MarshalledValue) assertOnlyOneRepresentationExists((MarshalledValue) arg);
+         }
+
+         Object retval = nextInterceptor(ctx);
+
+         if (retval instanceof MarshalledValue) assertOnlyOneRepresentationExists((MarshalledValue) retval);
+         return retval;
+      }
+   }
+
+   public static class Pojo implements Externalizable
+   {
+      int i;
+      boolean b;
+      static int serializationCount, deserializationCount;
+
+      public boolean equals(Object o)
+      {
+         if (this == o) return true;
+         if (o == null || getClass() != o.getClass()) return false;
+
+         Pojo pojo = (Pojo) o;
+
+         if (b != pojo.b) return false;
+         if (i != pojo.i) return false;
+
+         return true;
+      }
+
+      public int hashCode()
+      {
+         int result;
+         result = i;
+         result = 31 * result + (b ? 1 : 0);
+         return result;
+      }
+
+      public void writeExternal(ObjectOutput out) throws IOException
+      {
+         out.writeInt(i);
+         out.writeBoolean(b);
+         serializationCount++;
+      }
+
+      public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+      {
+         i = in.readInt();
+         b = in.readBoolean();
+         deserializationCount++;
+      }
+   }
+}
+
+

Modified: core/trunk/src/test/java/org/jboss/cache/marshall/RegionBasedMarshallingTestBase.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/RegionBasedMarshallingTestBase.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/RegionBasedMarshallingTestBase.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -7,14 +7,27 @@
 {
    protected static final String ADDRESS_CLASSNAME = "org.jboss.cache.marshall.data.Address";
    protected static final String PERSON_CLASSNAME = "org.jboss.cache.marshall.data.Person";
+   protected ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
 
    protected ClassLoader getClassLoader() throws Exception
    {
       String[] includesClasses = {PERSON_CLASSNAME, ADDRESS_CLASSNAME};
-      String [] excludesClasses = {};
+      String[] excludesClasses = {};
       ClassLoader cl = Thread.currentThread().getContextClassLoader();
       return new SelectedClassnameClassLoader(includesClasses, excludesClasses, cl);
    }
 
+   protected ClassLoader getFailingClassLoader() throws Exception
+   {
+      String[] includesClasses = {};
+      String[] excludesClasses = {};
+      String[] failingClasses = {PERSON_CLASSNAME, ADDRESS_CLASSNAME};
+      ClassLoader cl = Thread.currentThread().getContextClassLoader();
+      return new SelectedClassnameClassLoader(includesClasses, excludesClasses, failingClasses, cl);
+   }
 
+   protected void resetContextClassLoader()
+   {
+      Thread.currentThread().setContextClassLoader(originalClassLoader);
+   }
 }

Added: core/trunk/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingMarshalledValuesTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingMarshalledValuesTest.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingMarshalledValuesTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -0,0 +1,16 @@
+package org.jboss.cache.marshall;
+
+import org.testng.annotations.Test;
+
+/**
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at Test(groups = {"functional", "jgroups"})
+public class ReturnValueMarshallingMarshalledValuesTest extends ReturnValueMarshallingTest
+{
+   public ReturnValueMarshallingMarshalledValuesTest()
+   {
+      useMarshalledValues = true;
+   }
+}

Modified: core/trunk/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingTest.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -21,8 +21,9 @@
  * @since 2.0.0
  */
 @Test(groups = {"functional", "jgroups"})
-public class ReturnValueMarshallingTest
+public class ReturnValueMarshallingTest extends RegionBasedMarshallingTestBase
 {
+   protected boolean useMarshalledValues = false;
    private CacheSPI<Object, Object> cache1, cache2;
    private Fqn fqn = Fqn.fromString("/a");
    private ClassLoader classLoader;
@@ -34,26 +35,30 @@
    public void setUp() throws Exception
    {
       cache1 = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(false);
-      cache1.getConfiguration().setUseRegionBasedMarshalling(true);
+      cache1.getConfiguration().setUseLazyDeserialization(useMarshalledValues);
+      cache1.getConfiguration().setUseRegionBasedMarshalling(!useMarshalledValues);
       cache1.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC);
       cache1.getConfiguration().setSyncReplTimeout(60000);// to aid with debugging
       cache1.start();
 
       cache2 = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(false);
-      cache2.getConfiguration().setUseRegionBasedMarshalling(true);
+      cache2.getConfiguration().setUseLazyDeserialization(useMarshalledValues);
+      cache2.getConfiguration().setUseRegionBasedMarshalling(!useMarshalledValues);
       cache2.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC);
       cache2.getConfiguration().setSyncReplTimeout(60000);// to aid with debugging
       cache2.start();
 
       classLoader = getClassLoader();
-      Region r1 = cache1.getRegion(fqn, true);
-      r1.setActive(true);
-      r1.registerContextClassLoader(classLoader);
+      if (!useMarshalledValues)
+      {
+         Region r1 = cache1.getRegion(fqn, true);
+         r1.setActive(true);
+         r1.registerContextClassLoader(classLoader);
+         Region r2 = cache2.getRegion(fqn, true);
+         r2.setActive(true);
+         r2.registerContextClassLoader(classLoader);
+      }
 
-      Region r2 = cache2.getRegion(fqn, true);
-      r2.setActive(true);
-      r2.registerContextClassLoader(classLoader);
-
       listClass = classLoader.loadClass(className);
       value = listClass.newInstance();
 
@@ -64,9 +69,11 @@
    public void tearDown()
    {
       TestingUtil.killCaches(cache1, cache2);
+      resetContextClassLoader();
    }
 
-   private ClassLoader getClassLoader()
+   @Override
+   protected ClassLoader getClassLoader()
    {
       String[] includesClasses = {className};
       String[] excludesClasses = {};
@@ -76,6 +83,7 @@
 
    public void testClusteredGet() throws Exception
    {
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(classLoader);
       assertNotNull(cache1.get(fqn, key));
       assertNotSame(MyList.class, cache1.get(fqn, key).getClass());
       assertSame(listClass, cache1.get(fqn, key).getClass());
@@ -99,12 +107,14 @@
 
       // now test that the data returned has been marshalled using the appropriate class loader.
       assertNotNull(data);
+      if (useMarshalledValues) data = ((MarshalledValue) data).get();
       assertNotSame(MyList.class, data.getClass());
       assertSame(listClass, data.getClass());
    }
 
    public void testDataGravitation() throws Exception
    {
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(classLoader);
       assertNotNull(cache1.get(fqn, key));
       assertNotSame(MyList.class, cache1.get(fqn, key).getClass());
       assertSame(listClass, cache1.get(fqn, key).getClass());
@@ -125,6 +135,7 @@
       Object value = data.getNodeData().get(0).getAttributes().get(key);
 
       assertNotNull(value);
+      if (useMarshalledValues) value = ((MarshalledValue) value).get();
       assertNotSame(MyList.class, value.getClass());
       assertSame(listClass, value.getClass());
    }

Modified: core/trunk/src/test/java/org/jboss/cache/marshall/SelectedClassnameClassLoader.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/SelectedClassnameClassLoader.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/SelectedClassnameClassLoader.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -131,7 +131,6 @@
 
    protected Class<?> findClass(String name) throws ClassNotFoundException
    {
-
       log.info("In SelectedClassnameClassLoader.findClass()");
       Class result = classes.get(name);
       if (result != null)

Added: core/trunk/src/test/java/org/jboss/cache/marshall/SyncReplMarshalledValuesTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/SyncReplMarshalledValuesTest.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/SyncReplMarshalledValuesTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -0,0 +1,22 @@
+package org.jboss.cache.marshall;
+
+import org.testng.annotations.Test;
+
+/**
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at Test(groups = {"functional", "jgroups"})
+public class SyncReplMarshalledValuesTest extends SyncReplTest
+{
+   public SyncReplMarshalledValuesTest()
+   {
+      useMarshalledValues = true;
+   }
+
+   @Override
+   public void testCustomFqn()
+   {
+      // meaningless when using marshalled values
+   }
+}

Modified: core/trunk/src/test/java/org/jboss/cache/marshall/SyncReplTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/marshall/SyncReplTest.java	2008-01-21 23:13:15 UTC (rev 5175)
+++ core/trunk/src/test/java/org/jboss/cache/marshall/SyncReplTest.java	2008-01-22 01:06:22 UTC (rev 5176)
@@ -44,6 +44,7 @@
    Address addr_;
    Throwable ex_;
    private Fqn aop = Fqn.fromString("/aop");
+   protected boolean useMarshalledValues = false;
 
    @BeforeMethod(alwaysRun = true)
    public void setUp() throws Exception
@@ -69,7 +70,9 @@
       CacheSPI<Object, Object> cache = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false);
       cache.getConfiguration().setClusterName(name);
       // Use marshaller
-      cache.getConfiguration().setUseRegionBasedMarshalling(true);
+      cache.getConfiguration().setUseLazyDeserialization(useMarshalledValues);
+      cache.getConfiguration().setUseRegionBasedMarshalling(!useMarshalledValues);
+
       cache.create();
       cache.start();
       return cache;
@@ -90,6 +93,8 @@
          log("stopping cache2");
          cache2.stop();
       }
+
+      resetContextClassLoader();
    }
 
    public void testPlainPut() throws Exception
@@ -102,17 +107,26 @@
 
    public void testCCE() throws Exception
    {
-      ClassLoader cl = getClassLoader();
-      Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
-      r1.registerContextClassLoader(cl);
+      ClassLoader c1 = getClassLoader();
+      ClassLoader c2 = getClassLoader();
 
-      cl = getClassLoader();
-      Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
-      r2.registerContextClassLoader(cl);
+      if (!useMarshalledValues)
+      {
+         Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
+         r1.registerContextClassLoader(c1);
 
+
+         Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
+         r2.registerContextClassLoader(c2);
+      }
+
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(c1);
       cache1.put(aop, "person", ben_);
+      if (useMarshalledValues) resetContextClassLoader();
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(getFailingClassLoader());
       try
       {
+         if (useMarshalledValues) Thread.currentThread().setContextClassLoader(c2);
          @SuppressWarnings("unused")
          Person person = (Person) cache2.get(aop, "person");
       }
@@ -127,16 +141,20 @@
    public void testPut() throws Exception
    {
       ClassLoader cla = getClassLoader();
-      Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
-      r1.registerContextClassLoader(cla);
       ClassLoader clb = getClassLoader();
-      Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
-      r2.registerContextClassLoader(clb);
-
+      if (!useMarshalledValues)
+      {
+         Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
+         r1.registerContextClassLoader(cla);
+         Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
+         r2.registerContextClassLoader(clb);
+      }
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla);
       cache1.put(aop, "person", ben_);
 
       Object ben2;
-      // Can't cast it to Person. CCE will resutl.
+      // Can't cast it to Person. CCE will result.
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb);
       ben2 = cache2.get(aop, "person");
       assertEquals(ben_.toString(), ben2.toString());
    }
@@ -144,35 +162,31 @@
    public void testCLSet() throws Exception
    {
       ClassLoader cla = getClassLoader();
-      Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
-      r1.registerContextClassLoader(cla);
       ClassLoader clb = getClassLoader();
-      Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
-      r2.registerContextClassLoader(clb);
+      if (!useMarshalledValues)
+      {
+         Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
+         r1.registerContextClassLoader(cla);
+         Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
+         r2.registerContextClassLoader(clb);
+      }
 
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla);
       cache1.put(aop, "person", ben_);
 
       Object ben2;
-      // Can't cast it to Person. CCE will resutl.
+      // Can't cast it to Person. CCE will result.
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb);
       ben2 = cache2.get(aop, "person");
       assertEquals(ben_.toString(), ben2.toString());
 
       Class claz = clb.loadClass(ADDRESS_CLASSNAME);
       Object add = claz.newInstance();
-      {
-         Class[] types = {String.class};
-         Method setValue = claz.getMethod("setCity", types);
-         Object[] margs = {"Sunnyvale"};
-         setValue.invoke(add, margs);
-      }
-
-      {
-         Class clasz1 = clb.loadClass(PERSON_CLASSNAME);
-         Class[] types = {claz};
-         Method setValue = clasz1.getMethod("setAddress", types);
-         Object[] margs = {add};
-         setValue.invoke(ben2, margs);
-      }
+      Method setValue = claz.getMethod("setCity", String.class);
+      setValue.invoke(add, "Sunnyvale");
+      Class clasz1 = clb.loadClass(PERSON_CLASSNAME);
+      setValue = clasz1.getMethod("setAddress", claz);
+      setValue.invoke(ben2, add);
    }
 
    /**
@@ -183,111 +197,121 @@
    public void testCLSet2() throws Exception
    {
       ClassLoader cla = getClassLoader();
-      Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
-      r1.registerContextClassLoader(cla);
       ClassLoader clb = getClassLoader();
-      Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
-      r2.registerContextClassLoader(clb);
 
+      if (!useMarshalledValues)
+      {
+         Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
+         r1.registerContextClassLoader(cla);
+         Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
+         r2.registerContextClassLoader(clb);
+      }
+
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla);
       cache1.put(aop, "person", ben_);
 
       Object ben2;
-      // Can't cast it to Person. CCE will resutl.
+      // Can't cast it to Person. CCE will result.
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb);
       ben2 = cache2.get(aop, "person");
       assertEquals(ben_.toString(), ben2.toString());
 
       Class claz = clb.loadClass(ADDRESS_CLASSNAME);
       Object add = claz.newInstance();
-      {
-         Class[] types = {String.class};
-         Method setValue = claz.getMethod("setCity", types);
-         Object[] margs = {"Sunnyvale"};
-         setValue.invoke(add, margs);
-      }
+      Method setValue = claz.getMethod("setCity", String.class);
+      setValue.invoke(add, "Sunnyvale");
+      Class clasz1 = clb.loadClass(PERSON_CLASSNAME);
+      setValue = clasz1.getMethod("setAddress", claz);
+      setValue.invoke(ben2, add);
 
-      {
-         Class clasz1 = clb.loadClass(PERSON_CLASSNAME);
-         Class[] types = {claz};
-         Method setValue = clasz1.getMethod("setAddress", types);
-         Object[] margs = {add};
-         setValue.invoke(ben2, margs);
-      }
-
       // Set it back to the cache
       // Can't cast it to Person. CCE will resutl.
       cache2.put(aop, "person", ben2);
+
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla);
       Object ben3 = cache1.get(aop, "person");
       assertEquals(ben2.toString(), ben3.toString());
-
    }
 
    public void testPuts() throws Exception
    {
-      ClassLoader cl = getClassLoader();
-      Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
-      r1.registerContextClassLoader(cl);
+      ClassLoader cla = getClassLoader();
+      ClassLoader clb = getClassLoader();
 
+      if (!useMarshalledValues)
+      {
+         Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
+         r1.registerContextClassLoader(cla);
+         Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
+         r2.registerContextClassLoader(clb);
+      }
+
       // Create an empty Person loaded by this classloader
-      Object scopedBen1 = getPersonFromClassloader(cl);
-
-      cl = getClassLoader();
-      Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
-      r2.registerContextClassLoader(cl);
-
+      Object scopedBen1 = getPersonFromClassloader(cla);
       // Create another empty Person loaded by this classloader
-      Object scopedBen2 = getPersonFromClassloader(cl);
+      Object scopedBen2 = getPersonFromClassloader(clb);
 
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla);
       cache1.put(Fqn.fromString("/aop/1"), "person", ben_);
       cache1.put(Fqn.fromString("/aop/2"), "person", scopedBen1);
+      if (useMarshalledValues) resetContextClassLoader();
+      Object ben2;
+      // Can't cast it to Person. CCE will resutl.
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb);
+      ben2 = cache2.get(Fqn.fromString("/aop/1"), "person");
+      if (useMarshalledValues) resetContextClassLoader();
+      assertEquals(ben_.toString(), ben2.toString());
 
-      Object ben2 = null;
-      try
-      {
-         // Can't cast it to Person. CCE will resutl.
-         ben2 = cache2.get(Fqn.fromString("/aop/1"), "person");
-         assertEquals(ben_.toString(), ben2.toString());
-
-         ben2 = cache2.get(Fqn.fromString("/aop/2"), "person");
-         assertFalse("cache2 deserialized with scoped classloader", ben2 instanceof Person);
-         assertFalse("cache2 deserialized with cache2 classloader", scopedBen1.equals(ben2));
-         assertEquals("scopedBen deserialized properly", scopedBen2, ben2);
-      }
-      catch (Exception ex)
-      {
-         fail("Test fails with exception " + ex);
-      }
-
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb);
+      ben2 = cache2.get(Fqn.fromString("/aop/2"), "person");
+      if (useMarshalledValues) resetContextClassLoader();
+      assertFalse("cache2 deserialized with scoped classloader", ben2 instanceof Person);
+      assertFalse("cache2 deserialized with cache2 classloader", scopedBen1.equals(ben2));
+      assertEquals("scopedBen deserialized properly", scopedBen2, ben2);
    }
 
    public void testMethodCall() throws Exception
    {
-      ClassLoader cl = getClassLoader();
-      Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
-      r1.registerContextClassLoader(cl);
-      cl = getClassLoader();
-      Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
-      r2.registerContextClassLoader(cl);
+      ClassLoader cla = getClassLoader();
+      ClassLoader clb = getClassLoader();
 
+      if (!useMarshalledValues)
+      {
+         Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
+         r1.registerContextClassLoader(cla);
+
+         Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
+         r2.registerContextClassLoader(clb);
+      }
+
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla);
       cache1.put(Fqn.fromString("/aop/1"), "person", ben_);
       cache1.remove(Fqn.fromString("/aop/1"), "person");
       HashMap<Object, Object> map = new HashMap<Object, Object>();
       map.put("1", "1");
       map.put("2", "2");
+
       cache1.put(Fqn.fromString("/aop/2"), map);
       cache1.removeNode(Fqn.fromString("/aop/2"));
-
+      if (useMarshalledValues) resetContextClassLoader();
       TestingUtil.sleepThread(1000);
    }
 
    public void testTxMethodCall() throws Exception
    {
-      ClassLoader cl = getClassLoader();
-      Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
-      r1.registerContextClassLoader(cl);
-      cl = getClassLoader();
-      Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
-      r2.registerContextClassLoader(cl);
+      ClassLoader cla = getClassLoader();
+      ClassLoader clb = getClassLoader();
 
+      if (!useMarshalledValues)
+      {
+         Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
+         r1.registerContextClassLoader(cla);
+
+         Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
+         r2.registerContextClassLoader(clb);
+      }
+
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla);
       TransactionManager tm = beginTransaction();
       cache1.put(Fqn.fromString("/aop/1"), "person", ben_);
       cache1.remove(Fqn.fromString("/aop/1"), "person");
@@ -299,70 +323,53 @@
       tm.commit();
 
       TestingUtil.sleepThread(1000);
+      if (useMarshalledValues) resetContextClassLoader();
    }
 
-   public void testTxPut() throws Exception
-   {
-      TransactionManager tm = beginTransaction();
-      cache1.put(aop, "person", ben_);
-      cache1.put(aop, "person1", ben_);
-      cache1.removeNode(aop);
-      cache1.put(aop, "person", ben_);
-      tm.commit();
-      Person ben2 = (Person) cache2.get(aop, "person");
-      assertNotNull("Person from 2nd cache should not be null ", ben2);
-      assertEquals(ben_.toString(), ben2.toString());
-   }
-
-   public void testTxRollback() throws Exception
-   {
-      TransactionManager tm = beginTransaction();
-      cache1.put(aop, "person", ben_);
-      cache1.put(aop, "person1", ben_);
-      tm.rollback();
-      Person ben2 = (Person) cache2.get(aop, "person");
-      assertNull("Person from 2nd cache should be null ", ben2);
-   }
-
    public void testTxCLSet2() throws Exception
    {
       ClassLoader cla = getClassLoader();
-      Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
-      r1.registerContextClassLoader(cla);
+      if (!useMarshalledValues)
+      {
+         Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false);
+         r1.registerContextClassLoader(cla);
+      }
       ClassLoader clb = getClassLoader();
-      Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
-      r2.registerContextClassLoader(clb);
+      if (!useMarshalledValues)
+      {
+         Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false);
+         r2.registerContextClassLoader(clb);
+      }
 
       TransactionManager tm = beginTransaction();
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla);
       cache1.put(aop, "person", ben_);
       tm.commit();
+      if (useMarshalledValues) resetContextClassLoader();
 
       Object ben2;
       // Can't cast it to Person. CCE will resutl.
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb);
       ben2 = cache2.get(aop, "person");
+      if (useMarshalledValues) resetContextClassLoader();
       assertEquals(ben_.toString(), ben2.toString());
 
       Class claz = clb.loadClass(ADDRESS_CLASSNAME);
       Object add = claz.newInstance();
-      {
-         Class[] types = {String.class};
-         Method setValue = claz.getMethod("setCity", types);
-         Object[] margs = {"Sunnyvale"};
-         setValue.invoke(add, margs);
-      }
+      Method setValue = claz.getMethod("setCity", String.class);
+      setValue.invoke(add, "Sunnyvale");
+      Class clasz1 = clb.loadClass(PERSON_CLASSNAME);
+      setValue = clasz1.getMethod("setAddress", claz);
+      setValue.invoke(ben2, add);
 
-      {
-         Class clasz1 = clb.loadClass(PERSON_CLASSNAME);
-         Class[] types = {claz};
-         Method setValue = clasz1.getMethod("setAddress", types);
-         Object[] margs = {add};
-         setValue.invoke(ben2, margs);
-      }
-
       // Set it back to the cache
       // Can't cast it to Person. CCE will resutl.
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb);
       cache2.put(aop, "person", ben2);
+      if (useMarshalledValues) resetContextClassLoader();
+      if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla);
       Object ben3 = cache1.get(aop, "person");
+      if (useMarshalledValues) resetContextClassLoader();
       assertEquals(ben2.toString(), ben3.toString());
    }
 




More information about the jbosscache-commits mailing list