This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch javax
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit 4a226d063914bdcce39d92ae64b7d33c783009ab
Author: Thiago H. de Paula Figueiredo <thi...@arsmachina.com.br>
AuthorDate: Wed Dec 20 21:17:32 2023 -0300

    TAP5-2770: avoiding using FieldHandle when in multiple classloader mode
    
    Now, in CachedWorker too.
---
 .../tapestry5/plastic/PropertyValueProvider.java   |  9 ---
 .../tapestry5/internal/transform/CachedWorker.java | 57 +++++++++++++++---
 .../tapestry5/internal/transform/ImportWorker.java |  7 ++-
 .../transform/PropertyValueProviderWorker.java     | 68 ++++++++++++++++++++++
 .../apache/tapestry5/modules/TapestryModule.java   | 13 ++++-
 .../app1/components/SubclassWithImport.java        | 14 ++++-
 .../app1/components/SuperclassWithImport.java      | 13 +++++
 7 files changed, 161 insertions(+), 20 deletions(-)

diff --git 
a/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java 
b/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
index e33301f48..bbba55a3b 100644
--- 
a/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
+++ 
b/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
@@ -54,16 +54,7 @@ public interface PropertyValueProvider
     {
         if (object instanceof PropertyValueProvider)
         {
-            try {
             return ((PropertyValueProvider) 
object).__propertyValueProvider__get(fieldName);
-            }
-            catch (Exception e) {
-                final Method[] methods = object.getClass().getMethods();
-                for (Method method : methods) {
-                    System.out.println(method);
-                }
-                throw new RuntimeException(e.getMessage() + ": " + fieldName, 
e);
-            }
         }
         else
         {
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java
index e5eae27c6..7a7fbe38f 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/CachedWorker.java
@@ -17,19 +17,25 @@ package org.apache.tapestry5.internal.transform;
 import org.apache.tapestry5.Binding;
 import org.apache.tapestry5.BindingConstants;
 import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.annotations.Cached;
 import org.apache.tapestry5.internal.TapestryInternalUtils;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.ioc.annotations.Symbol;
 import org.apache.tapestry5.ioc.services.PerThreadValue;
 import org.apache.tapestry5.ioc.services.PerthreadManager;
 import org.apache.tapestry5.model.MutableComponentModel;
 import org.apache.tapestry5.plastic.*;
+import org.apache.tapestry5.plastic.PlasticUtils.FieldInfo;
 import org.apache.tapestry5.runtime.PageLifecycleListener;
 import org.apache.tapestry5.services.BindingSource;
 import org.apache.tapestry5.services.TransformConstants;
 import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
 import org.apache.tapestry5.services.transform.TransformationSupport;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Caches method return values for methods annotated with {@link Cached}.
@@ -37,9 +43,15 @@ import java.util.List;
 @SuppressWarnings("all")
 public class CachedWorker implements ComponentClassTransformWorker2
 {
+    private static final String FIELD_PREFIX = "cache$";
+
     private final BindingSource bindingSource;
 
     private final PerthreadManager perThreadManager;
+    
+    private final PropertyValueProviderWorker propertyValueProviderWorker;
+    
+    private final boolean multipleClassLoaders;
 
     interface MethodResultCacheFactory
     {
@@ -117,32 +129,43 @@ public class CachedWorker implements 
ComponentClassTransformWorker2
         }
     }
 
-    public CachedWorker(BindingSource bindingSource, PerthreadManager 
perthreadManager)
+    public CachedWorker(BindingSource bindingSource, PerthreadManager 
perthreadManager,
+            PropertyValueProviderWorker propertyValueProviderWorker,
+            @Inject @Symbol(SymbolConstants.PRODUCTION_MODE) boolean 
productionMode,
+            @Inject @Symbol(SymbolConstants.MULTIPLE_CLASSLOADERS) boolean 
multipleClassloaders)
     {
         this.bindingSource = bindingSource;
         this.perThreadManager = perthreadManager;
+        this.propertyValueProviderWorker = propertyValueProviderWorker;
+        this.multipleClassLoaders = !productionMode && multipleClassloaders;
     }
 
 
     public void transform(PlasticClass plasticClass, TransformationSupport 
support, MutableComponentModel model)
     {
         List<PlasticMethod> methods = 
plasticClass.getMethodsWithAnnotation(Cached.class);
+        Set<PlasticUtils.FieldInfo> fieldInfos = multipleClassLoaders ? new 
HashSet<>() : null;
 
         for (PlasticMethod method : methods)
         {
             validateMethod(method);
 
-            adviseMethod(plasticClass, method);
+            adviseMethod(plasticClass, method, fieldInfos);
         }
+        
+        if (multipleClassLoaders && !fieldInfos.isEmpty())
+        {
+            this.propertyValueProviderWorker.add(plasticClass, fieldInfos);
+        }        
     }
 
-    private void adviseMethod(PlasticClass plasticClass, PlasticMethod method)
+    private void adviseMethod(PlasticClass plasticClass, PlasticMethod method, 
Set<FieldInfo> fieldInfos)
     {
-        // Every instance of the clas srequires its own per-thread value. This 
handles the case of multiple
+        // Every instance of the class requires its own per-thread value. This 
handles the case of multiple
         // pages containing the component, or the same page containing the 
component multiple times.
 
         PlasticField cacheField =
-                plasticClass.introduceField(PerThreadValue.class, "cache$" + 
method.getDescription().methodName);
+                plasticClass.introduceField(PerThreadValue.class, 
getFieldName(method));
 
         cacheField.injectComputed(new ComputedValue<PerThreadValue>()
         {
@@ -152,6 +175,12 @@ public class CachedWorker implements 
ComponentClassTransformWorker2
                 return perThreadManager.createValue();
             }
         });
+        
+        if (multipleClassLoaders)
+        {
+            fieldInfos.add(PlasticUtils.toFieldInfo(cacheField));
+            cacheField.createAccessors(PropertyAccessType.READ_ONLY);
+        }
 
         Cached annotation = method.getAnnotation(Cached.class);
 
@@ -162,11 +191,23 @@ public class CachedWorker implements 
ComponentClassTransformWorker2
         method.addAdvice(advice);
     }
 
+    private String getFieldName(PlasticMethod method) {
+        final StringBuilder builder = new StringBuilder(FIELD_PREFIX);
+        builder.append(method.getDescription().methodName);
+        if (multipleClassLoaders)
+        {
+            builder.append("_");
+            
builder.append(method.getPlasticClass().getClassName().replace('.', '_'));
+        }
+        return builder.toString();
+    }
+
 
     private MethodAdvice createAdvice(PlasticField cacheField,
                                       final MethodResultCacheFactory factory)
     {
         final FieldHandle fieldHandle = cacheField.getHandle();
+        final String fieldName = multipleClassLoaders ? cacheField.getName() : 
null;
 
         return new MethodAdvice()
         {
@@ -194,8 +235,10 @@ public class CachedWorker implements 
ComponentClassTransformWorker2
 
                 // The PerThreadValue is created in the instance constructor.
 
-                PerThreadValue<MethodResultCache> value = 
(PerThreadValue<MethodResultCache>) fieldHandle
-                        .get(instance);
+                PerThreadValue<MethodResultCache> value = 
(PerThreadValue<MethodResultCache>) (
+                        multipleClassLoaders ?
+                        PropertyValueProvider.get(instance, fieldName) :
+                        fieldHandle.get(instance));
 
                 // But it will be empty when first created, or at the start of 
a new request.
                 if (value.exists())
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java
index 9e5b85967..70bbe4464 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ImportWorker.java
@@ -56,6 +56,8 @@ public class ImportWorker implements 
ComponentClassTransformWorker2
     private final ResourceChangeTracker resourceChangeTracker;
     
     private final boolean multipleClassLoaders;
+    
+    private final PropertyValueProviderWorker propertyValueProviderWorker;
 
     private final Worker<Asset> importLibrary = new Worker<Asset>()
     {
@@ -82,12 +84,13 @@ public class ImportWorker implements 
ComponentClassTransformWorker2
     };
 
     public ImportWorker(JavaScriptSupport javascriptSupport, SymbolSource 
symbolSource, AssetSource assetSource,
-            ResourceChangeTracker resourceChangeTracker)
+            ResourceChangeTracker resourceChangeTracker, 
PropertyValueProviderWorker propertyValueProviderWorker)
     {
         this.javascriptSupport = javascriptSupport;
         this.symbolSource = symbolSource;
         this.assetSource = assetSource;
         this.resourceChangeTracker = resourceChangeTracker;
+        this.propertyValueProviderWorker = propertyValueProviderWorker;
         this.multipleClassLoaders = 
                 
!Boolean.valueOf(symbolSource.valueForSymbol(SymbolConstants.PRODUCTION_MODE)) 
&&
                 
Boolean.valueOf(symbolSource.valueForSymbol(SymbolConstants.MULTIPLE_CLASSLOADERS));
@@ -108,7 +111,7 @@ public class ImportWorker implements 
ComponentClassTransformWorker2
         
         if (multipleClassLoaders && !fieldInfos.isEmpty())
         {
-            PlasticUtils.implementPropertyValueProvider(componentClass, 
fieldInfos);
+            propertyValueProviderWorker.add(componentClass, fieldInfos);
         }
         
         resourceChangeTracker.clearCurrentClassName();
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PropertyValueProviderWorker.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PropertyValueProviderWorker.java
new file mode 100644
index 000000000..735368cbd
--- /dev/null
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PropertyValueProviderWorker.java
@@ -0,0 +1,68 @@
+// Copyright 2023 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.transform;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tapestry5.ioc.services.PerThreadValue;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
+import org.apache.tapestry5.model.MutableComponentModel;
+import org.apache.tapestry5.plastic.PlasticClass;
+import org.apache.tapestry5.plastic.PlasticUtils;
+import org.apache.tapestry5.plastic.PlasticUtils.FieldInfo;
+import org.apache.tapestry5.plastic.PropertyValueProvider;
+import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
+import org.apache.tapestry5.services.transform.TransformationSupport;
+
+/**
+ * Worker used to gather {@linkplain FieldInfo} instances and implement
+ * {@linkplain PropertyValueProvider} for any class that has them.
+ * 
+ */
+public final class PropertyValueProviderWorker implements 
ComponentClassTransformWorker2
+{
+    private final PerThreadValue<Map<PlasticClass, Set<FieldInfo>>> 
perThreadMap;
+    
+    public PropertyValueProviderWorker(PerthreadManager perThreadManager)
+    {
+        perThreadMap = perThreadManager.createValue();
+    }
+    
+    public void add(PlasticClass plasticClass, Set<FieldInfo> fieldInfos)
+    {
+        final Map<PlasticClass, Set<FieldInfo>> map = 
perThreadMap.computeIfAbsent(HashMap::new);
+        if (!map.containsKey(plasticClass))
+        {
+            map.put(plasticClass, new HashSet<>(5));
+        }
+        map.get(plasticClass).addAll(fieldInfos);
+    }
+
+    public void transform(PlasticClass plasticClass, TransformationSupport 
support, MutableComponentModel model)
+    {
+        if (perThreadMap.exists())
+        {
+            final Set<FieldInfo> fieldInfos = 
perThreadMap.get().get(plasticClass);
+            if (fieldInfos != null && !fieldInfos.isEmpty())
+            {
+                PlasticUtils.implementPropertyValueProvider(plasticClass, 
fieldInfos);
+            }
+        }
+    }
+
+}
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java 
b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
index 57edc6bd4..b5a2cf557 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
@@ -216,6 +216,7 @@ import 
org.apache.tapestry5.internal.transform.PageLifecycleAnnotationWorker;
 import org.apache.tapestry5.internal.transform.PageResetAnnotationWorker;
 import org.apache.tapestry5.internal.transform.ParameterWorker;
 import org.apache.tapestry5.internal.transform.PersistWorker;
+import org.apache.tapestry5.internal.transform.PropertyValueProviderWorker;
 import org.apache.tapestry5.internal.transform.PropertyWorker;
 import org.apache.tapestry5.internal.transform.RenderCommandWorker;
 import org.apache.tapestry5.internal.transform.RenderPhaseMethodWorker;
@@ -524,6 +525,7 @@ public final class TapestryModule
         binder.bind(Dispatcher.class, AssetDispatcher.class).withSimpleId();
         binder.bind(TranslatorAlternatesSource.class, 
TranslatorAlternatesSourceImpl.class);
         binder.bind(MetaWorker.class, MetaWorkerImpl.class);
+        binder.bind(PropertyValueProviderWorker.class);
         binder.bind(LinkTransformer.class, LinkTransformerImpl.class);
         binder.bind(SelectModelFactory.class, SelectModelFactoryImpl.class);
         binder.bind(DynamicTemplateParser.class, 
DynamicTemplateParserImpl.class);
@@ -680,7 +682,10 @@ public final class TapestryModule
             OrderedConfiguration<ComponentClassTransformWorker2> configuration,
             MetaWorker metaWorker,
             ComponentClassResolver resolver,
-            ComponentDependencyRegistry componentDependencyRegistry)
+            ComponentDependencyRegistry componentDependencyRegistry,
+            PropertyValueProviderWorker propertyValueProviderWorker,
+            @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode,
+            @Symbol(SymbolConstants.MULTIPLE_CLASSLOADERS) boolean 
multipleClassloaders)
     {
         configuration.add("Property", new PropertyWorker());
 
@@ -731,6 +736,12 @@ public final class TapestryModule
                 .addInstance("ActivationRequestParameter", 
ActivationRequestParameterWorker.class);
 
         configuration.addInstance("Cached", CachedWorker.class);
+        
+        if (!productionMode && multipleClassloaders)
+        {
+            configuration.add("PropertyValueProvider", 
propertyValueProviderWorker, 
+                    "after:Cached", "after:Import");
+        }
 
         configuration.addInstance("DiscardAfter", DiscardAfterWorker.class);
 
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SubclassWithImport.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SubclassWithImport.java
index f2ba5940a..9f3a743c4 100644
--- 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SubclassWithImport.java
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SubclassWithImport.java
@@ -1,8 +1,20 @@
 package org.apache.tapestry5.integration.app1.components;
 
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.annotations.Cached;
 import org.apache.tapestry5.annotations.Import;
 
 @Import(stylesheet = "context:css/ie-only.css")
 public class SubclassWithImport extends SuperclassWithImport {
-
+    
+    @Cached
+    public int getInt() 
+    { 
+        return 2; 
+    }
+    
+    public void setupRender(MarkupWriter writer) {
+        writer.element("p").text("Int: " + getInt() + " : " + getInt());
+        writer.end();
+    }
 }
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SuperclassWithImport.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SuperclassWithImport.java
index 7909cc880..2de9d1793 100644
--- 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SuperclassWithImport.java
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/SuperclassWithImport.java
@@ -1,8 +1,21 @@
 package org.apache.tapestry5.integration.app1.components;
 
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.annotations.Cached;
 import org.apache.tapestry5.annotations.Import;
 
 @Import(stylesheet = "context:css/via-import.css")
 public class SuperclassWithImport {
+    
+    @Cached
+    public int getOther() 
+    { 
+        return 4; 
+    }
+    
+    public void cleanupRender(MarkupWriter writer) {
+        writer.element("p").text("Other: " + getOther() + " : " + getOther());
+        writer.end();
+    }
 
 }

Reply via email to