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


The following commit(s) were added to refs/heads/javax by this push:
     new 4b9739579 TAP5-2813: NPE when @Cached applied to method returning a 
generic type
4b9739579 is described below

commit 4b9739579d58891e139190443da66fba36e3f83c
Author: Thiago H. de Paula Figueiredo <[email protected]>
AuthorDate: Sat Oct 4 17:51:07 2025 -0300

    TAP5-2813: NPE when @Cached applied to method returning a generic type
    
    in multiple classloader mode
---
 .../internal/GuavaGenericsResolver.java            |  2 +-
 .../services/ComponentModelSourceImpl.java         | 10 ++++++++--
 .../tapestry5/internal/transform/CachedWorker.java |  8 +++++++-
 tapestry-core/src/test/app1/CachedGenericsDemo.tml |  3 +++
 .../tapestry5/integration/app1/CacheTests.java     |  7 +++++++
 .../tapestry5/integration/app1/GenericsClass.java  |  6 ++++++
 .../tapestry5/integration/app1/GenericsEntity.java | 23 ++++++++++++++++++++++
 .../app1/base/AbstractCachedGenerics.java          | 20 +++++++++++++++++++
 .../app1/components/CachedGenerics.java            | 18 +++++++++++++++++
 .../integration/app1/pages/CachedGenericsDemo.java |  5 +++++
 .../tapestry5/integration/app1/pages/Index.java    |  4 +++-
 .../app1/base/AbstractCachedGenerics.tml           | 11 +++++++++++
 .../groovy/ioc/specs/ChainBuilderImplSpec.groovy   |  2 +-
 13 files changed, 113 insertions(+), 6 deletions(-)

diff --git 
a/genericsresolver-guava/src/main/java/org/apache/tapestry5/genericsresolverguava/internal/GuavaGenericsResolver.java
 
b/genericsresolver-guava/src/main/java/org/apache/tapestry5/genericsresolverguava/internal/GuavaGenericsResolver.java
index 65338b23e..b5cf116e3 100644
--- 
a/genericsresolver-guava/src/main/java/org/apache/tapestry5/genericsresolverguava/internal/GuavaGenericsResolver.java
+++ 
b/genericsresolver-guava/src/main/java/org/apache/tapestry5/genericsresolverguava/internal/GuavaGenericsResolver.java
@@ -22,7 +22,7 @@ import 
org.apache.tapestry5.genericsresolverguava.internal.GuavaGenericsResolver
 import com.google.common.reflect.TypeToken;
 
 /**
- * {@link GuavaGenericsResolver} implementation using Guava.
+ * {@link GenericsResolver} implementation using Guava.
  */
 public class GuavaGenericsResolver implements GenericsResolver {
 
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentModelSourceImpl.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentModelSourceImpl.java
index dcbfbc47e..bec14d236 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentModelSourceImpl.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentModelSourceImpl.java
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry5.internal.services;
 
+import java.util.HashSet;
 import java.util.Set;
 
 import org.apache.tapestry5.SymbolConstants;
@@ -33,6 +34,9 @@ public class ComponentModelSourceImpl implements 
ComponentModelSource
     private final PageSource pageSource;
     
     private final boolean multipleClassLoaders;
+    
+    private final static ThreadLocal<Set<String>> CALL_STACK = 
+            ThreadLocal.withInitial(HashSet::new);    
 
     public ComponentModelSourceImpl(ComponentClassResolver resolver, 
ComponentInstantiatorSource source,
             ComponentDependencyRegistry componentDependencyRegistry,
@@ -51,15 +55,16 @@ public class ComponentModelSourceImpl implements 
ComponentModelSource
     {
         if (multipleClassLoaders && isPage(componentClassName))
         {
-            
             final Set<String> superclasses = 
componentDependencyRegistry.getDependencies(
                     componentClassName, DependencyType.SUPERCLASS);
             
             if (!superclasses.isEmpty())
             {
                 final String superclass = superclasses.iterator().next();
-                if (isPage(superclass))
+                final Set<String> callStack = CALL_STACK.get();
+                if (!callStack.contains(superclass) && isPage(superclass))
                 {
+                    callStack.add(superclass);
                     getModel(superclass);
                     try
                     {
@@ -73,6 +78,7 @@ public class ComponentModelSourceImpl implements 
ComponentModelSource
                         // so the objective of the line above is already
                         // fulfilled and we can safely ignore the exception
                     }
+                    callStack.remove(superclass);
                 }
             }
         }
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 045188d09..643abdd63 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
@@ -274,11 +274,17 @@ public class CachedWorker implements 
ComponentClassTransformWorker2
     private static JSONObject toJSONObject(PlasticMethod method) 
     {
         final MethodDescription description = method.getDescription();
+        
+        // TAP5-2813
+        final String genericSignature = description.genericSignature != null ?
+                description.genericSignature.replaceAll("<[^>]+>", "") : null;
+        
+        
         return new JSONObject(
                 MODIFIERS, description.modifiers,
                 RETURN_TYPE, description.returnType,
                 NAME, description.methodName,
-                GENERIC_SIGNATURE, description.genericSignature,
+                GENERIC_SIGNATURE, genericSignature,
                 ARGUMENT_TYPES, new JSONArray(description.argumentTypes),
                 CHECKED_EXCEPTION_TYPES, new 
JSONArray(description.checkedExceptionTypes),
                 WATCH, method.getAnnotation(Cached.class).watch());
diff --git a/tapestry-core/src/test/app1/CachedGenericsDemo.tml 
b/tapestry-core/src/test/app1/CachedGenericsDemo.tml
new file mode 100644
index 000000000..9f5b46817
--- /dev/null
+++ b/tapestry-core/src/test/app1/CachedGenericsDemo.tml
@@ -0,0 +1,3 @@
+<html t:type="Border" 
xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd";>
+       <t:cachedGenerics/>
+</html>
\ No newline at end of file
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CacheTests.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CacheTests.java
index 6dccd8301..e7c2381af 100644
--- 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CacheTests.java
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CacheTests.java
@@ -66,4 +66,11 @@ public class CacheTests extends App1TestCase
                 "Method 
org.apache.tapestry5.integration.app1.pages.ParamsMethodWithCached.invalidMethod(java.lang.String)",
                 "may not be used with @Cached because it has parameters.");
     }
+    
+    @Test 
+    public void at_cached_at_method_returning_generic_types()
+    {
+        // Fails without the fix
+        openLinks("@Cached on method returning type with generics");
+    }
 }
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/GenericsClass.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/GenericsClass.java
new file mode 100644
index 000000000..dd004b952
--- /dev/null
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/GenericsClass.java
@@ -0,0 +1,6 @@
+package org.apache.tapestry5.integration.app1;
+
+
+public class GenericsClass<T, H> {
+
+}
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/GenericsEntity.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/GenericsEntity.java
new file mode 100644
index 000000000..32123c66e
--- /dev/null
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/GenericsEntity.java
@@ -0,0 +1,23 @@
+package org.apache.tapestry5.integration.app1;
+
+public class GenericsEntity<T> {
+
+    private final String id_;
+
+    private final String label_;
+
+    public GenericsEntity(String id, String label) {
+        id_ = id;
+        label_ = label;
+    }
+
+    @SuppressWarnings("unused")
+    private String getId() {
+        return id_;
+    }
+
+    @SuppressWarnings("unused")
+    private String getLabel() {
+        return label_;
+    }
+}
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/AbstractCachedGenerics.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/AbstractCachedGenerics.java
new file mode 100644
index 000000000..053279e6d
--- /dev/null
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/base/AbstractCachedGenerics.java
@@ -0,0 +1,20 @@
+package org.apache.tapestry5.integration.app1.base;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tapestry5.annotations.Cached;
+import org.apache.tapestry5.integration.app1.GenericsClass;
+
+public abstract class AbstractCachedGenerics<T, H> 
+{
+
+    protected abstract GenericsClass<T, H> createTable(List<T> 
itemsInCurrentPage);
+
+    @Cached
+    public GenericsClass<T, H> getEmptyTable() 
+    {
+        return createTable(new ArrayList<>());
+    }
+    
+}
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/CachedGenerics.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/CachedGenerics.java
new file mode 100644
index 000000000..8e0fef614
--- /dev/null
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/CachedGenerics.java
@@ -0,0 +1,18 @@
+package org.apache.tapestry5.integration.app1.components;
+
+import java.util.List;
+
+import org.apache.tapestry5.integration.app1.GenericsClass;
+import org.apache.tapestry5.integration.app1.GenericsEntity;
+import org.apache.tapestry5.integration.app1.base.AbstractCachedGenerics;
+
+public class CachedGenerics extends AbstractCachedGenerics<GenericsEntity, 
String> 
+{
+    
+    @Override
+    protected GenericsClass<GenericsEntity, String> 
createTable(List<GenericsEntity> itemsInCurrentPage) 
+    {
+        return new GenericsClass<>();
+    }
+
+}
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/CachedGenericsDemo.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/CachedGenericsDemo.java
new file mode 100644
index 000000000..1e4cfa4b0
--- /dev/null
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/CachedGenericsDemo.java
@@ -0,0 +1,5 @@
+package org.apache.tapestry5.integration.app1.pages;
+
+public class CachedGenericsDemo
+{
+}
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
index cbbc34c5d..bf35a7a2b 100644
--- 
a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
@@ -632,7 +632,9 @@ public class Index
                     
                     new Item("SelfRecursiveDemo", "Self-Recursive Demo", 
"check for handling of self-recursive components"),
                     
-                    new Item("EsModuleDemo", "ES Module Demo", "tests and 
demonstrations for the ES module support")
+                    new Item("EsModuleDemo", "ES Module Demo", "tests and 
demonstrations for the ES module support"),
+                    
+                    new Item("CachedGenericsDemo", "@Cached on method 
returning type with generics", "tests fix for TAP5-2813")
                 );
 
     static
diff --git 
a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/base/AbstractCachedGenerics.tml
 
b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/base/AbstractCachedGenerics.tml
new file mode 100644
index 000000000..87b8639d0
--- /dev/null
+++ 
b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/base/AbstractCachedGenerics.tml
@@ -0,0 +1,11 @@
+<div xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd";
+          xmlns:p="tapestry:parameter">
+          
+    <p>
+               ${EmptyTable}
+       </p>
+       
+    <p>
+               ${EmptyTable}
+       </p>
+</div>
\ No newline at end of file
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ChainBuilderImplSpec.groovy 
b/tapestry-ioc/src/test/groovy/ioc/specs/ChainBuilderImplSpec.groovy
index 12ff3a7dc..4277cd0e9 100644
--- a/tapestry-ioc/src/test/groovy/ioc/specs/ChainBuilderImplSpec.groovy
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ChainBuilderImplSpec.groovy
@@ -118,7 +118,7 @@ class ChainBuilderImplSpec extends 
AbstractSharedRegistrySpecification {
     chain.toString() == "<Command chain of ioc.specs.ChainCommand>"
   }
   
-  final private static class InterfaceWithStaticMethodImpl extends 
InterfaceWithStaticMethod 
+  final private static class InterfaceWithStaticMethodImpl implements 
InterfaceWithStaticMethod 
   {
     public int something() { return 2; }
   }

Reply via email to