You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2010/07/07 20:27:37 UTC

svn commit: r961456 - /tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java

Author: hlship
Date: Wed Jul  7 18:27:37 2010
New Revision: 961456

URL: http://svn.apache.org/viewvc?rev=961456&view=rev
Log:
TAP5-1197: Rebuild CachedWorker around the non-pooled page model

Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java?rev=961456&r1=961455&r2=961456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java Wed Jul  7 18:27:37 2010
@@ -22,17 +22,10 @@ import org.apache.tapestry5.BindingConst
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.annotations.Cached;
 import org.apache.tapestry5.internal.TapestryInternalUtils;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
 import org.apache.tapestry5.model.MutableComponentModel;
-import org.apache.tapestry5.runtime.PageLifecycleAdapter;
-import org.apache.tapestry5.services.BindingSource;
-import org.apache.tapestry5.services.ClassTransformation;
-import org.apache.tapestry5.services.ComponentClassTransformWorker;
-import org.apache.tapestry5.services.ComponentMethodAdvice;
-import org.apache.tapestry5.services.ComponentMethodInvocation;
-import org.apache.tapestry5.services.FieldAccess;
-import org.apache.tapestry5.services.TransformField;
-import org.apache.tapestry5.services.TransformMethod;
-import org.apache.tapestry5.services.TransformMethodSignature;
+import org.apache.tapestry5.runtime.Component;
+import org.apache.tapestry5.services.*;
 
 /**
  * Caches method return values for methods annotated with {@link Cached}.
@@ -41,6 +34,13 @@ public class CachedWorker implements Com
 {
     private final BindingSource bindingSource;
 
+    private final PerthreadManager perThreadManager;
+
+    interface MethodResultCacheFactory
+    {
+        MethodResultCache create(Component instance);
+    }
+
     /**
      * Handles the watching of a binding (usually a property or property expression), invalidating the
      * cache early if the watched binding's value changes.
@@ -100,9 +100,10 @@ public class CachedWorker implements Com
         }
     }
 
-    public CachedWorker(BindingSource bindingSource)
+    public CachedWorker(BindingSource bindingSource, PerthreadManager perthreadManager)
     {
         this.bindingSource = bindingSource;
+        this.perThreadManager = perthreadManager;
     }
 
     public void transform(ClassTransformation transformation, MutableComponentModel model)
@@ -119,26 +120,43 @@ public class CachedWorker implements Com
 
     private void adviseMethod(ClassTransformation transformation, TransformMethod method)
     {
-        FieldAccess resultCacheAccess = createMethodResultCacheField(transformation, method);
+        // The key needs to reflect not just the method name, but also the containing
+        // page and component (otherwise, there would be unwanted sharing of cache
+        // between different instances of the same component within or across pages). This
+        // name can't be calculated until page instantiation time.
+
+        FieldAccess keyAccess = createKeyField(transformation, method);
 
         Cached annotation = method.getAnnotation(Cached.class);
 
-        ComponentMethodAdvice advice = createAdvice(resultCacheAccess, annotation.watch());
+        MethodResultCacheFactory factory = createFactory(transformation, annotation.watch(), method);
+
+        ComponentMethodAdvice advice = createAdvice(keyAccess, factory);
 
         method.addAdvice(advice);
     }
 
-    private FieldAccess createMethodResultCacheField(ClassTransformation transformation, TransformMethod method)
+    private FieldAccess createKeyField(ClassTransformation transformation, TransformMethod method)
     {
-        TransformField resultCacheField = transformation.createField(Modifier.PRIVATE, MethodResultCache.class
-                .getName(), "cache$" + method.getName());
+        final String methodId = method.getMethodIdentifier();
+
+        TransformField field = transformation.createField(Modifier.PROTECTED, String.class.getName(), "cacheKey$"
+                + method.getName());
+
+        field.injectIndirect(new ComponentValueProvider<String>()
+        {
+            public String get(ComponentResources resources)
+            {
+                return String.format("MethodResultCache:%s/%s", resources.getCompleteId(), methodId);
+            }
+        });
 
-        return resultCacheField.getAccess();
+        return field.getAccess();
     }
 
-    private ComponentMethodAdvice createAdvice(final FieldAccess resultCacheAccess, final String watch)
+    private ComponentMethodAdvice createAdvice(final FieldAccess keyAccess, final MethodResultCacheFactory factory)
     {
-        ComponentMethodAdvice advice = new ComponentMethodAdvice()
+        return new ComponentMethodAdvice()
         {
             public void advise(ComponentMethodInvocation invocation)
             {
@@ -159,44 +177,67 @@ public class CachedWorker implements Com
 
             private MethodResultCache getOrCreateCache(ComponentMethodInvocation invocation)
             {
-                MethodResultCache cache = (MethodResultCache) resultCacheAccess.read(invocation.getInstance());
+                Component instance = invocation.getInstance();
+
+                Object key = keyAccess.read(instance);
+
+                MethodResultCache cache = (MethodResultCache) perThreadManager.get(key);
 
                 if (cache == null)
-                    cache = createAndStoreCache(invocation);
+                {
+                    cache = factory.create(instance);
+
+                    perThreadManager.put(key, cache);
+                }
 
                 return cache;
             }
+        };
+    }
 
-            private MethodResultCache createAndStoreCache(ComponentMethodInvocation invocation)
+    private MethodResultCacheFactory createFactory(ClassTransformation transformation, final String watch,
+            TransformMethod method)
+    {
+        if (watch.equals(""))
+            return new MethodResultCacheFactory()
             {
-                final MethodResultCache cache = createMethodResultCache(invocation.getComponentResources());
+                public MethodResultCache create(Component instance)
+                {
+                    return new SimpleMethodResultCache();
+                }
+            };
+
+        // Each component instance will get its own Binding instance. That handles both different locales,
+        // and reuse of a component (with a cached method) within a page or across pages.
 
-                invocation.getComponentResources().addPageLifecycleListener(new PageLifecycleAdapter()
+        TransformField bindingField = transformation.createField(Modifier.PROTECTED, Binding.class.getName(),
+                "cache$watchBinding$" + method.getName());
+
+        final FieldAccess bindingAccess = bindingField.getAccess();
+
+        transformation.getOrCreateMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE).addAdvice(
+                new ComponentMethodAdvice()
                 {
-                    @Override
-                    public void containingPageDidDetach()
+                    public void advise(ComponentMethodInvocation invocation)
                     {
-                        cache.reset();
-                    }
-                });
+                        Binding binding = bindingSource.newBinding("@Cached watch", invocation.getComponentResources(),
+                                BindingConstants.PROP, watch);
 
-                resultCacheAccess.write(invocation.getInstance(), cache);
+                        bindingAccess.write(invocation.getInstance(), binding);
 
-                return cache;
-            }
+                        invocation.proceed();
+                    }
+                });
 
-            private SimpleMethodResultCache createMethodResultCache(ComponentResources resources)
+        return new MethodResultCacheFactory()
+        {
+            public MethodResultCache create(Component instance)
             {
-                if (watch.equals(""))
-                    return new SimpleMethodResultCache();
-
-                Binding binding = bindingSource.newBinding("@Cached watch", resources, BindingConstants.PROP, watch);
+                Binding binding = (Binding) bindingAccess.read(instance);
 
                 return new WatchedBindingMethodResultCache(binding);
             }
         };
-
-        return advice;
     }
 
     private void validateMethod(TransformMethod method)