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(); + } }