This is an automated email from the ASF dual-hosted git repository.
thiagohp pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git
The following commit(s) were added to refs/heads/master by this push:
new fe6fa22ff TAP5-2813: NPE when @Cached applied to method returning a
generic type
fe6fa22ff is described below
commit fe6fa22fff64f210475f47409daf2384c50b5603
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 fcad59e0a..5f7bebf6b 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; }
}