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 e8564a9ff715d8a21c38b4f3b50cdb93293f126a
Author: Thiago H. de Paula Figueiredo <thi...@arsmachina.com.br>
AuthorDate: Wed Dec 20 19:00:29 2023 -0300

    TAP5-2770: avoiding using FieldHandle when in multiple classloader mode
    
    For now, just in ImportWorker.
---
 .../internal/plastic/InstructionBuilderImpl.java   |  45 ++++-
 .../tapestry5/plastic/FieldValueProvider.java      |  64 +++++++
 .../tapestry5/plastic/InstructionBuilder.java      |  16 ++
 .../org/apache/tapestry5/plastic/PlasticUtils.java | 191 ++++++++++++++++++++-
 .../tapestry5/plastic/PropertyValueProvider.java   |  74 ++++++++
 .../apache/tapestry5/plastic/PlasticUtilsTest.java | 105 +++++++++++
 .../plastic/test/PlasticUtilsTestObject.java       | 105 +++++++++++
 .../test/PlasticUtilsTestObjectSuperclass.java     |  34 ++++
 .../tapestry5/internal/transform/ImportWorker.java |  80 +++++++--
 9 files changed, 689 insertions(+), 25 deletions(-)

diff --git 
a/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
 
b/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
index dd4488349..a8be0b928 100644
--- 
a/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
+++ 
b/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
@@ -14,16 +14,28 @@
 
 package org.apache.tapestry5.internal.plastic;
 
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
 import org.apache.tapestry5.internal.plastic.InstructionBuilderState.LVInfo;
 import org.apache.tapestry5.internal.plastic.asm.Label;
 import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
-import org.apache.tapestry5.plastic.*;
-
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
+import org.apache.tapestry5.plastic.Condition;
+import org.apache.tapestry5.plastic.InstructionBuilder;
+import org.apache.tapestry5.plastic.InstructionBuilderCallback;
+import org.apache.tapestry5.plastic.LocalVariable;
+import org.apache.tapestry5.plastic.LocalVariableCallback;
+import org.apache.tapestry5.plastic.MethodDescription;
+import org.apache.tapestry5.plastic.PlasticField;
+import org.apache.tapestry5.plastic.PlasticMethod;
+import org.apache.tapestry5.plastic.PlasticUtils;
+import org.apache.tapestry5.plastic.SwitchCallback;
+import org.apache.tapestry5.plastic.TryCatchCallback;
+import org.apache.tapestry5.plastic.WhenCallback;
+import org.apache.tapestry5.plastic.WhileCallback;
 
 @SuppressWarnings("rawtypes")
 public class InstructionBuilderImpl extends Lockable implements Opcodes, 
InstructionBuilder
@@ -469,6 +481,29 @@ public class InstructionBuilderImpl extends Lockable 
implements Opcodes, Instruc
 
         return checkcast(cache.toTypeName(clazz));
     }
+    
+    @Override
+    public InstructionBuilder instanceOf(String className)
+    {
+        check();
+
+        // Found out the hard way that array names are handled differently; 
you cast to the descriptor, not the internal
+        // name.
+
+        String internalName = className.contains("[") ? 
cache.toDesc(className) : cache.toInternalName(className);
+
+        v.visitTypeInsn(INSTANCEOF, internalName);
+
+        return this;
+    }
+
+    @Override
+    public InstructionBuilder instanceOf(Class clazz)
+    {
+        check();
+
+        return instanceOf(cache.toTypeName(clazz));
+    }
 
     @Override
     public InstructionBuilder startTryCatch(TryCatchCallback callback)
diff --git 
a/plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java 
b/plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java
new file mode 100644
index 000000000..79087e8b8
--- /dev/null
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/FieldValueProvider.java
@@ -0,0 +1,64 @@
+// 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.plastic;
+
+/**
+ * <p>
+ * Interface that can be implemented to provide access to field values based 
on their name.
+ * Usually implemented with {@linkplain 
PlasticUtils#implementFieldValueProvider(PlasticClass, java.util.Set)},
+ * which doesn't use reflection.
+ * <p/>
+ * <p>
+ * The name of its abstract method is intended to avoid clashes with other 
existing methods
+ * in the class.
+ * </p>
+ * @see PlasticUtils#implementFieldValueProvider(PlasticClass, java.util.Set)
+ * @since 5.8.4
+ */
+public interface FieldValueProvider
+{
+    /**
+     * Returns the value of a given field.
+     * @param fieldName the field name.
+     * @return the field value.
+     */
+    Object __fieldValueProvider__get(String fieldName);
+    
+    /**
+     * <p>
+     * Returns the value of a given field in a given object if it belongs to a 
class
+     * that implements {@linkplain FieldValueProvider}. Otherwise, it throws 
an exception.
+     * </p>
+     * <p>
+     * This is an utility method to avoid having to make casts very time you 
need to call
+     * {@linkplain #__fieldValueProvider__get(String)}.
+     * </p>
+     * @param object an object.
+     * @param fieldName the field name.
+     * @return the field value.
+     */
+    static Object get(Object object, String fieldName)
+    {
+        if (object instanceof FieldValueProvider)
+        {
+            return ((FieldValueProvider) 
object).__fieldValueProvider__get(fieldName);
+        }
+        else
+        {
+            throw new RuntimeException("Class " + object.getClass().getName() 
+ " doesn't implement " + FieldValueProvider.class.getSimpleName());
+        }
+    }
+    
+}
diff --git 
a/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java 
b/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
index 97a4f1768..7f14c860d 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
@@ -247,6 +247,22 @@ public interface InstructionBuilder
     @Opcodes("CHECKCAST")
     InstructionBuilder checkcast(Class clazz);
 
+    /**
+     * Adds a check that the object on top of the stack is assignable to the 
indicated class.
+     *
+     * @param className class to cast to
+     * @since 5.8.4
+     */
+    @Opcodes("CHECKCAST")
+    InstructionBuilder instanceOf(String className);
+
+    /**
+     * Adds a check that the object on top of the stack is assignable to the 
indicated class.
+     * @since 5.8.4
+     */
+    @Opcodes("CHECKCAST")
+    InstructionBuilder instanceOf(Class clazz);
+
     /**
      * Defines the start of a block that can have exception handlers and 
finally blocks applied.
      * Continue using this InstructionBuilder to define code inside the block, 
then call
diff --git 
a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java 
b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
index 2fab09efd..9c35d3428 100644
--- a/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
+++ b/plastic/src/main/java/org/apache/tapestry5/plastic/PlasticUtils.java
@@ -12,11 +12,14 @@
 
 package org.apache.tapestry5.plastic;
 
-import org.apache.tapestry5.internal.plastic.PrimitiveType;
-
 import java.lang.reflect.Method;
+import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicLong;
 
+import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
+import org.apache.tapestry5.internal.plastic.PrimitiveType;
+
 /**
  * Utilities for user code making use of Plastic.
  */
@@ -33,6 +36,18 @@ public class PlasticUtils
     public static final MethodDescription TO_STRING_DESCRIPTION = new 
MethodDescription(TO_STRING);
 
     private static final AtomicLong UID_GENERATOR = new 
AtomicLong(System.nanoTime());
+    
+    private static final MethodDescription 
PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION;
+    
+    static
+    {
+        try {
+            PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION = new 
MethodDescription(PropertyValueProvider.class.getMethod("__propertyValueProvider__get",
 String.class));
+        } catch (Exception e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
 
     /**
      * Returns a string that can be used as part of a Java identifier and is 
unique
@@ -151,4 +166,176 @@ public class PlasticUtils
         int index = className.indexOf('$');
         return index <= 0 ? className : className.substring(0, index);
     }
+
+    /**
+     * Utility method for creating {@linkplain FieldInfo} instances.
+     * @param field a {@linkplain PlasticField}.
+     * @return a corresponding {@linkplain FieldInfo}.
+     * @since 5.8.4
+     */
+    public static FieldInfo toFieldInfo(PlasticField field)
+    {
+        return new FieldInfo(field.getName(), field.getTypeName());
+    }
+    
+    /**
+     * Transforms this {@linkplain PlasticClass} so it implements
+     * {@linkplain FieldValueProvider} for the given set of field names.
+     * Notice attempts to read a superclass' private field will result in 
+     * an {@linkplain IllegalAccessError}.
+     * 
+     * @param plasticClass a {@linkplain PlasticClass} instance.
+     * @param fieldNames a {@linkplain Set} of {@linkplain String}s containing 
the field names.
+     * @since 5.8.4
+     */
+    public static void implementFieldValueProvider(PlasticClass plasticClass, 
Set<FieldInfo> fields)
+    {
+        
+        final Set<PlasticMethod> methods = 
plasticClass.introduceInterface(FieldValueProvider.class);
+        
+        if (!methods.isEmpty())
+        {
+            final PlasticMethod method = methods.iterator().next();
+            
+            method.changeImplementation((builder) -> {
+                
+                for (FieldInfo field : fields) 
+                {
+                    builder.loadArgument(0);
+                    builder.loadConstant(field.name);
+                    builder.invokeVirtual(String.class.getName(), "boolean", 
"equals", Object.class.getName());
+                    builder.when(Condition.NON_ZERO, ifBuilder -> {
+                        ifBuilder.loadThis();
+                        ifBuilder.getField(plasticClass.getClassName(), 
field.name, field.type);
+                        ifBuilder.boxPrimitive(field.type);
+                        ifBuilder.returnResult();
+                    });
+                }
+                
+                builder.throwException(RuntimeException.class, "Field not 
found or not supported");
+                
+            });
+            
+        }
+        
+    }
+    
+    /**
+     * Transforms this {@linkplain PlasticClass} so it implements
+     * {@linkplain PropertyValueProvider} for the given set of field names.
+     * The implementation will use the fields' corresponding getters instead
+     * of direct fields access.
+     * 
+     * @param plasticClass a {@linkplain PlasticClass} instance.
+     * @param fieldNames a {@linkplain Set} of {@linkplain String}s containing 
the filed (i.e. property) names.
+     * @since 5.8.4
+     */
+    public static void implementPropertyValueProvider(PlasticClass 
plasticClass, Set<FieldInfo> fields)
+    {
+        
+        final Set<PlasticMethod> methods = 
plasticClass.introduceInterface(PropertyValueProvider.class);
+        
+        final InstructionBuilderCallback callback = (builder) -> {
+            
+            for (FieldInfo field : fields) 
+            {
+                builder.loadArgument(0);
+                builder.loadConstant(field.name);
+                builder.invokeVirtual(String.class.getName(), "boolean", 
"equals", Object.class.getName());
+                builder.when(Condition.NON_ZERO, ifBuilder -> 
+                {
+                    final String prefix = field.type.equals("boolean") ? "is" 
: "get";
+                    final String methodName = prefix + 
PlasticInternalUtils.capitalize(field.name);
+                    
+                    ifBuilder.loadThis();
+                    builder.invokeVirtual(
+                            plasticClass.getClassName(), 
+                            field.type, 
+                            methodName);
+                    ifBuilder.boxPrimitive(field.type);
+                    ifBuilder.returnResult();
+                });
+                
+            }
+            
+            builder.loadThis();
+            builder.instanceOf(PropertyValueProvider.class);
+            
+            builder.when(Condition.NON_ZERO, ifBuilder -> {
+                builder.loadThis();
+                builder.loadArgument(0);
+                ifBuilder.invokeSpecial(
+                        plasticClass.getSuperClassName(), 
+                        PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION);
+                ifBuilder.returnResult();
+            });
+            
+            // Field/property not found, so let's try the superclass in case
+            // it also implement
+            
+            builder.throwException(RuntimeException.class, "Property not found 
or not supported");
+            
+        };
+        
+        final PlasticMethod method;
+        
+        // Superclass has already defined this method, so we need to override 
it so
+        // it can also find the subclasses' declared fields/properties.
+        if (methods.isEmpty())
+        {
+            method = 
plasticClass.introduceMethod(PROPERTY_VALUE_PROVIDER_METHOD_DESCRIPTION , 
callback);
+        }
+        else
+        {
+            method = methods.iterator().next();
+        }
+        
+        method.changeImplementation(callback);
+        
+    }
+
+    /**
+     * Class used to represent a field name and its type for 
+     * {@linkplain PlasticUtils#implementFieldValueProvider(PlasticClass, 
Set)}.
+     * It shouldn't be used directly. Use {@linkplain 
PlasticUtils#toFieldInfo(PlasticField)}
+     * instead.
+     * @see PlasticUtils#implementFieldValueProvider(PlasticClass, Set)
+     * @since 5.8.4
+     */
+    public static class FieldInfo {
+        final private String name;
+        final private String type;
+        public FieldInfo(String name, String type) 
+        {
+            super();
+            this.name = name;
+            this.type = type;
+        }
+        @Override
+        public int hashCode() 
+        {
+            return Objects.hash(name);
+        }
+        @Override
+        public boolean equals(Object obj) 
+        {
+            if (this == obj) 
+            {
+                return true;
+            }
+            if (!(obj instanceof FieldInfo)) 
+            {
+                return false;
+            }
+            FieldInfo other = (FieldInfo) obj;
+            return Objects.equals(name, other.name);
+        }
+        @Override
+        public String toString() 
+        {
+            return "FieldInfo [name=" + name + ", type=" + type + "]";
+        }
+        
+    }
+    
 }
diff --git 
a/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java 
b/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
new file mode 100644
index 000000000..e33301f48
--- /dev/null
+++ 
b/plastic/src/main/java/org/apache/tapestry5/plastic/PropertyValueProvider.java
@@ -0,0 +1,74 @@
+// 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.plastic;
+
+import java.lang.reflect.Method;
+
+/**
+ * <p>
+ * Interface that can be implemented to provide access to field values based 
on their name.
+ * Usually implemented with {@linkplain} FieldValueProviderTransformation}.
+ * <p/>
+ * <p>
+ * The name of its abstract method is intended to avoid clashes with other 
existing methods
+ * in the class.
+ * </p>
+ * @see FieldValueProviderTransformation
+ * @since 5.8.4
+ */
+public interface PropertyValueProvider
+{
+    /**
+     * Returns the value of a given field.
+     * @param fieldName the field name.
+     * @return the field value.
+     */
+    Object __propertyValueProvider__get(String fieldName);
+    
+    /**
+     * <p>
+     * Returns the value of a given field in a given object if it belongs to a 
class
+     * that implements {@linkplain PropertyValueProvider}. Otherwise, it 
throws an exception.
+     * </p>
+     * <p>
+     * This is an utility method to avoid having to make casts very time you 
need to call
+     * {@linkplain #__propertyValueProvider__get(String)}.
+     * </p>
+     * @param object an object.
+     * @param fieldName the field name.
+     * @return the field value.
+     */
+    static Object get(Object object, String fieldName)
+    {
+        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
+        {
+            throw new RuntimeException("Class " + object.getClass().getName() 
+ " doesn't implement " + PropertyValueProvider.class.getSimpleName());
+        }
+    }
+    
+}
diff --git 
a/plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java 
b/plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java
new file mode 100644
index 000000000..3d0e4f14f
--- /dev/null
+++ b/plastic/src/test/java/org/apache/tapestry5/plastic/PlasticUtilsTest.java
@@ -0,0 +1,105 @@
+// 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.plastic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.tapestry5.plastic.test.PlasticUtilsTestObject;
+import org.apache.tapestry5.plastic.test.PlasticUtilsTestObjectSuperclass;
+import org.junit.jupiter.api.Test;
+
+// [Thiago] This is only here because I couldn't get Groovy tests to run on 
Eclipse
+// (and I admit to not liking Groovy anyway . . .).
+public class PlasticUtilsTest 
+{
+    
+    public static void main(String[] args) throws ClassNotFoundException {
+        final PlasticUtilsTest plasticUtilsTest = new PlasticUtilsTest();
+        plasticUtilsTest.implement_field_value_pProvider();
+        plasticUtilsTest.implement_property_value_provider();
+    }
+    
+    @Test
+    public void implement_field_value_pProvider() throws ClassNotFoundException
+    {
+        
+        Set<String> packages = new HashSet<>();
+        packages.add(PlasticUtilsTestObject.class.getPackage().getName());
+        PlasticManager plasticManager = PlasticManager.withContextClassLoader()
+                .packages(packages).create();
+        final PlasticClassTransformation<Object> transformation = 
plasticManager.getPlasticClass(PlasticUtilsTestObject.class.getName());
+        PlasticClass pc = transformation.getPlasticClass();
+        Set<PlasticUtils.FieldInfo> fieldInfos = new 
HashSet<PlasticUtils.FieldInfo>();
+        for (PlasticField field : pc.getAllFields()) {
+            fieldInfos.add(PlasticUtils.toFieldInfo(field));
+        }
+        fieldInfos.add(new PlasticUtils.FieldInfo("superString", 
"java.lang.String"));
+        PlasticUtils.implementFieldValueProvider(pc, fieldInfos);
+        Object object = transformation.createInstantiator().newInstance();
+        
+        Class<?> original = PlasticUtilsTestObject.class;
+        Class<?> transformed = object.getClass();
+        
+        assertNotEquals(original, transformed);
+        
+        assertEquals(PlasticUtilsTestObject.STRING, 
FieldValueProvider.get(object, "string"));
+        assertEquals(PlasticUtilsTestObject.OTHER_STRING, 
FieldValueProvider.get(object, "otherString"));
+        assertEquals(null, FieldValueProvider.get(object, "nullString"));
+        assertEquals(PlasticUtilsTestObject.ENUMERATION.toString(), 
FieldValueProvider.get(object, "enumeration").toString());
+        assertTrue(Arrays.equals(PlasticUtilsTestObject.INT_ARRAY, (int[]) 
FieldValueProvider.get(object, "intArray")));
+        assertEquals(PlasticUtilsTestObject.TRUE_OF_FALSE, (Boolean) 
FieldValueProvider.get(object, "trueOrFalse"));
+        
+    }
+    
+    @Test
+    public void implement_property_value_provider() throws 
ClassNotFoundException
+    {
+        
+        Set<String> packages = new HashSet<>();
+        packages.add(PlasticUtilsTestObject.class.getPackage().getName());
+        PlasticManager plasticManager = PlasticManager.withContextClassLoader()
+                .packages(packages).create();
+        final PlasticClassTransformation<Object> transformation = 
plasticManager.getPlasticClass(PlasticUtilsTestObject.class.getName());
+        PlasticClass pc = transformation.getPlasticClass();
+        Set<PlasticUtils.FieldInfo> fieldInfos = new 
HashSet<PlasticUtils.FieldInfo>();
+        for (PlasticField field : pc.getAllFields()) {
+            fieldInfos.add(PlasticUtils.toFieldInfo(field));
+        }
+        fieldInfos.add(new PlasticUtils.FieldInfo("superString", 
"java.lang.String"));
+        PlasticUtils.implementPropertyValueProvider(pc, fieldInfos);
+        Object object = transformation.createInstantiator().newInstance();
+        
+        Class<?> original = PlasticUtilsTestObject.class;
+        Class<?> transformed = object.getClass();
+        
+        assertNotEquals(original, transformed);
+        
+        assertEquals(PlasticUtilsTestObject.STRING, 
PropertyValueProvider.get(object, "string"));
+        assertEquals(PlasticUtilsTestObject.OTHER_STRING, 
PropertyValueProvider.get(object, "otherString"));
+        assertEquals(null, PropertyValueProvider.get(object, "nullString"));
+        assertEquals(PlasticUtilsTestObject.ENUMERATION.toString(), 
PropertyValueProvider.get(object, "enumeration").toString());
+        assertTrue(Arrays.equals(PlasticUtilsTestObject.INT_ARRAY, (int[]) 
PropertyValueProvider.get(object, "intArray")));
+        assertEquals(PlasticUtilsTestObject.TRUE_OF_FALSE, (Boolean) 
PropertyValueProvider.get(object, "trueOrFalse"));
+        assertEquals(PlasticUtilsTestObjectSuperclass.SUPER, 
PropertyValueProvider.get(object, "superString"));
+        
+    }
+    
+}
diff --git 
a/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObject.java
 
b/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObject.java
new file mode 100644
index 000000000..bd34620ff
--- /dev/null
+++ 
b/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObject.java
@@ -0,0 +1,105 @@
+// 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.plastic.test;
+
+public class PlasticUtilsTestObject extends PlasticUtilsTestObjectSuperclass
+{
+    
+    public static final String STRING = "A nice string";
+    public static final int[] INT_ARRAY = new int[] {1, 42};
+    public static final String OTHER_STRING = "Another nice string";
+    public static final Enumeration ENUMERATION = 
PlasticUtilsTestObject.Enumeration.FILE_NOT_FOUND;
+    public static final boolean TRUE_OF_FALSE = true;
+    
+    public static enum Enumeration
+    {
+        TRUE,
+        FALSE,
+        FILE_NOT_FOUND
+    }
+    
+    private String string = STRING;
+    
+    private String otherString = OTHER_STRING;
+    
+    private String nullString = null;
+    
+    private Enumeration enumeration = ENUMERATION;
+    
+    private int[] intArray = INT_ARRAY;
+    
+    private boolean trueOrFalse = TRUE_OF_FALSE;
+
+    public String getString() 
+    {
+        return string;
+    }
+    
+    public void setString(String string) 
+    {
+        this.string = string;
+    }
+
+    public String getOtherString() 
+    {
+        return otherString;
+    }
+    
+    public void setOtherString(String otherString) 
+    {
+        this.otherString = otherString;
+    }
+    
+    public String getNullString() 
+    {
+        return nullString;
+    }
+    
+    public void setNullString(String nullString) 
+    {
+        this.nullString = nullString;
+    }
+    
+    public Enumeration getEnumeration() 
+    {
+        return enumeration;
+    }
+    
+    public void setEnumeration(Enumeration enumeration) 
+    {
+        this.enumeration = enumeration;
+    }
+    
+    public int[] getIntArray() 
+    {
+        return intArray;
+    }
+    
+    public void setIntArray(int[] intArray) 
+    {
+        this.intArray = intArray;
+    }
+    
+    public boolean isTrueOrFalse() 
+    {
+        return trueOrFalse;
+    }
+    
+    public void setTrueOrFalse(boolean trueOrFalse) 
+    {
+        this.trueOrFalse = trueOrFalse;
+    }
+    
+}
diff --git 
a/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObjectSuperclass.java
 
b/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObjectSuperclass.java
new file mode 100644
index 000000000..f82995f88
--- /dev/null
+++ 
b/plastic/src/test/java/org/apache/tapestry5/plastic/test/PlasticUtilsTestObjectSuperclass.java
@@ -0,0 +1,34 @@
+// 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.plastic.test;
+
+public class PlasticUtilsTestObjectSuperclass 
+{
+    
+    public static final String SUPER = "Super!!!";
+    
+    private String superString = SUPER;
+
+    public String getSuperString() 
+    {
+        return superString;
+    }
+
+    public void setSuperString(String superString) 
+    {
+        this.superString = superString;
+    }
+    
+}
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 e47553fbd..9e5b85967 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
@@ -24,6 +24,7 @@ import 
org.apache.tapestry5.internal.services.assets.ResourceChangeTracker;
 import org.apache.tapestry5.ioc.services.SymbolSource;
 import org.apache.tapestry5.model.MutableComponentModel;
 import org.apache.tapestry5.plastic.*;
+import org.apache.tapestry5.plastic.PlasticUtils.FieldInfo;
 import org.apache.tapestry5.services.AssetSource;
 import org.apache.tapestry5.services.TransformConstants;
 import org.apache.tapestry5.services.javascript.Initialization;
@@ -32,7 +33,9 @@ import 
org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
 import org.apache.tapestry5.services.transform.TransformationSupport;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Implements the {@link Import} annotation, both at the class and at the 
method level.
@@ -41,6 +44,9 @@ import java.util.List;
  */
 public class ImportWorker implements ComponentClassTransformWorker2
 {
+    
+    private static final String FIELD_PREFIX = "importedAssets_";
+    
     private final JavaScriptSupport javascriptSupport;
 
     private final SymbolSource symbolSource;
@@ -90,17 +96,26 @@ public class ImportWorker implements 
ComponentClassTransformWorker2
     public void transform(PlasticClass componentClass, TransformationSupport 
support, MutableComponentModel model)
     {
         
resourceChangeTracker.setCurrentClassName(model.getComponentClassName());
-        processClassAnnotationAtSetupRenderPhase(componentClass, model);
+        
+        Set<PlasticUtils.FieldInfo> fieldInfos = multipleClassLoaders ? new 
HashSet<>() : null;
+        processClassAnnotationAtSetupRenderPhase(componentClass, model, 
fieldInfos);
 
-        for (PlasticMethod m : 
componentClass.getMethodsWithAnnotation(Import.class))
+        final List<PlasticMethod> methods = 
componentClass.getMethodsWithAnnotation(Import.class);
+        for (PlasticMethod m : methods)
         {
-            decorateMethod(componentClass, model, m);
+            decorateMethod(componentClass, model, m, fieldInfos);
+        }
+        
+        if (multipleClassLoaders && !fieldInfos.isEmpty())
+        {
+            PlasticUtils.implementPropertyValueProvider(componentClass, 
fieldInfos);
         }
         
         resourceChangeTracker.clearCurrentClassName();
     }
 
-    private void processClassAnnotationAtSetupRenderPhase(PlasticClass 
componentClass, MutableComponentModel model)
+    private void processClassAnnotationAtSetupRenderPhase(PlasticClass 
componentClass, MutableComponentModel model,
+            Set<FieldInfo> fieldInfos)
     {
         Import annotation = componentClass.getAnnotation(Import.class);
 
@@ -108,27 +123,27 @@ public class ImportWorker implements 
ComponentClassTransformWorker2
         {
             PlasticMethod setupRender = 
componentClass.introduceMethod(TransformConstants.SETUP_RENDER_DESCRIPTION);
 
-            decorateMethod(componentClass, model, setupRender, annotation);
+            decorateMethod(componentClass, model, setupRender, annotation, 
fieldInfos);
 
             model.addRenderPhase(SetupRender.class);
         }
     }
 
-    private void decorateMethod(PlasticClass componentClass, 
MutableComponentModel model, PlasticMethod method)
+    private void decorateMethod(PlasticClass componentClass, 
MutableComponentModel model, PlasticMethod method, Set<FieldInfo> fieldInfos)
     {
         Import annotation = method.getAnnotation(Import.class);
 
-        decorateMethod(componentClass, model, method, annotation);
+        decorateMethod(componentClass, model, method, annotation, fieldInfos);
     }
 
     private void decorateMethod(PlasticClass componentClass, 
MutableComponentModel model, PlasticMethod method,
-                                Import annotation)
+                                Import annotation, Set<FieldInfo> fieldInfos)
     {
         importStacks(method, annotation.stack());
 
-        importLibraries(componentClass, model, method, annotation.library());
+        importLibraries(componentClass, model, method, annotation.library(), 
fieldInfos);
 
-        importStylesheets(componentClass, model, method, 
annotation.stylesheet());
+        importStylesheets(componentClass, model, method, 
annotation.stylesheet(), fieldInfos);
 
         importModules(method, annotation.module());
     }
@@ -215,19 +230,20 @@ public class ImportWorker implements 
ComponentClassTransformWorker2
     }
 
     private void importLibraries(PlasticClass plasticClass, 
MutableComponentModel model, PlasticMethod method,
-                                 String[] paths)
+                                 String[] paths, Set<FieldInfo> fieldInfos)
     {
-        decorateMethodWithOperation(plasticClass, model, method, paths, 
importLibrary);
+        decorateMethodWithOperation(plasticClass, model, method, paths, 
importLibrary, fieldInfos);
     }
 
     private void importStylesheets(PlasticClass plasticClass, 
MutableComponentModel model, PlasticMethod method,
-                                   String[] paths)
+                                   String[] paths, Set<FieldInfo> fieldInfos)
     {
-        decorateMethodWithOperation(plasticClass, model, method, paths, 
importStylesheet);
+        decorateMethodWithOperation(plasticClass, model, method, paths, 
importStylesheet, fieldInfos);
     }
 
     private void decorateMethodWithOperation(PlasticClass componentClass, 
MutableComponentModel model,
-                                             PlasticMethod method, String[] 
paths, Worker<Asset> operation)
+                                             PlasticMethod method, String[] 
paths, Worker<Asset> operation,
+                                             Set<FieldInfo> fieldInfos)
     {
         if (paths.length == 0)
         {
@@ -236,14 +252,32 @@ public class ImportWorker implements 
ComponentClassTransformWorker2
 
         String[] expandedPaths = expandPaths(paths);
 
-        PlasticField assetListField = 
componentClass.introduceField(Asset[].class,
-                "importedAssets_" + method.getDescription().methodName);
+        final String fieldName = getFieldName(method);
+        PlasticField assetListField = 
componentClass.introduceField(Asset[].class, fieldName);
+        
+        if (multipleClassLoaders)
+        {
+            fieldInfos.add(PlasticUtils.toFieldInfo(assetListField));
+            assetListField.createAccessors(PropertyAccessType.READ_ONLY);
+        }
 
         initializeAssetsFromPaths(expandedPaths, assetListField, 
model.getLibraryName());
 
         addMethodAssetOperationAdvice(method, assetListField.getHandle(), 
operation);
     }
 
+    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 String[] expandPaths(String[] paths)
     {
         return F.flow(paths).map(expandSymbols).toArray(String.class);
@@ -277,13 +311,17 @@ public class ImportWorker implements 
ComponentClassTransformWorker2
                                                final Worker<Asset> operation)
     {
         final String className = method.getPlasticClass().getClassName();
+        final String fieldName = getFieldName(method);
         method.addAdvice(new MethodAdvice()
         {
             public void advise(MethodInvocation invocation)
             {
                 invocation.proceed();
 
-                Asset[] assets = (Asset[]) 
access.get(invocation.getInstance());
+                final Object instance = invocation.getInstance();
+                Asset[] assets = (Asset[]) (multipleClassLoaders ?
+                        PropertyValueProvider.get(instance, fieldName) :
+                        access.get(instance));
 
                 if (multipleClassLoaders)
                 {
@@ -299,4 +337,10 @@ public class ImportWorker implements 
ComponentClassTransformWorker2
             }
         });
     }
+    
+    public static interface ImportWorkerDataProvider 
+    {
+        Asset[] get(int fieldNameHashcode);
+    }
+    
 }


Reply via email to