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

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

commit f4cd06bbf5ed7628fcb7ff71444240d976fe3c82
Author: Thiago H. de Paula Figueiredo <thi...@arsmachina.com.br>
AuthorDate: Sun May 7 15:34:13 2023 -0300

    TAP5-2742: fixes problems with page classes with subclasses
---
 .../internal/plastic/PlasticClassPool.java         | 28 ++++++++++++++++
 .../services/ComponentInstantiatorSourceImpl.java  |  2 +-
 .../internal/services/PageSourceImpl.java          | 38 ++++++++++++++++------
 .../apache/tapestry5/modules/TapestryModule.java   | 27 ++++++++++++++-
 .../services/pageload/PageClassloaderContext.java  | 12 ++++++-
 .../pageload/PageClassloaderContextManager.java    |  6 ++++
 .../PageClassloaderContextManagerImpl.java         | 32 ++++++++++++++----
 7 files changed, 125 insertions(+), 20 deletions(-)

diff --git 
a/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
 
b/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
index 1a099843c..9daa47d51 100644
--- 
a/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
+++ 
b/plastic/src/main/java/org/apache/tapestry5/internal/plastic/PlasticClassPool.java
@@ -20,6 +20,7 @@ import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -87,6 +88,8 @@ public class PlasticClassPool implements ClassLoaderDelegate, 
Opcodes, PlasticCl
     private final List<PlasticClassListener> listeners = new 
CopyOnWriteArrayList<PlasticClassListener>();
     
     private PlasticClassPool parent;
+    
+    private Collection<PlasticClassPool> children = new ArrayList<>();
 
     private final Cache<String, TypeCategory> typeName2Category = new 
Cache<String, TypeCategory>()
     {
@@ -517,6 +520,13 @@ public class PlasticClassPool implements 
ClassLoaderDelegate, Opcodes, PlasticCl
                 def = current.baseClassDefs.get(baseClassName);
             }
             
+            // Usually, when df is still null, that's because the superclass
+            // is a page class too
+            if (def == null)
+            {
+                def = findBaseClassDef(baseClassName, current);
+            }
+            
             assert def != null;
 
             return new PlasticClassImpl(classNode, implementationClassNode, 
this, def.inheritanceData, def.staticContext, proxy);
@@ -527,6 +537,23 @@ public class PlasticClassPool implements 
ClassLoaderDelegate, Opcodes, PlasticCl
         return new PlasticClassImpl(classNode, implementationClassNode, this, 
emptyInheritanceData, emptyStaticContext, proxy);
     }
 
+    private BaseClassDef findBaseClassDef(String baseClassName, 
PlasticClassPool plasticClassPool) 
+    {
+        BaseClassDef def = plasticClassPool.baseClassDefs.get(baseClassName);
+        if (def == null)
+        {
+            for (PlasticClassPool child : plasticClassPool.children) 
+            {
+                def = child.findBaseClassDef(baseClassName, child);
+                if (def != null)
+                {
+                    break;
+                }
+            }
+        }
+        return def;
+    }
+
     /**
      * Constructs a class node by reading the raw bytecode for a class and 
instantiating a ClassNode
      * (via {@link 
ClassReader#accept(org.apache.tapestry5.internal.plastic.asm.ClassVisitor, 
int)}).
@@ -709,6 +736,7 @@ public class PlasticClassPool implements 
ClassLoaderDelegate, Opcodes, PlasticCl
     public void setParent(PlasticClassPool parent) 
     {
         this.parent = parent;
+        parent.children.add(this);
     }
 
     boolean isEnabled(TransformationOption option)
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java
index d1b8f258f..351b60200 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentInstantiatorSourceImpl.java
@@ -292,7 +292,7 @@ public final class ComponentInstantiatorSourceImpl 
implements ComponentInstantia
             
             PlasticProxyFactory proxyFactory = 
createPlasticProxyFactory(parent);
             rootPageClassloaderContext = new PageClassloaderContext(
-                    "root", null, Collections.emptySet(), proxyFactory);
+                    "root", null, Collections.emptySet(), proxyFactory, 
pageClassloaderContextManager::get);
         }
         else 
         {
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageSourceImpl.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageSourceImpl.java
index d19b22708..ff15ab7a7 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageSourceImpl.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageSourceImpl.java
@@ -113,8 +113,13 @@ public class PageSourceImpl implements PageSource
         this.pageClassloaderContextManager = pageClassloaderContextManager;
         this.logger = logger;
     }
-
+    
     public Page getPage(String canonicalPageName)
+    {
+        return getPage(canonicalPageName, true);
+    }
+
+    public Page getPage(String canonicalPageName, boolean 
invalidateUnknownContext)
     {
         ComponentResourceSelector selector = 
selectorAnalyzer.buildSelectorForRequest();
 
@@ -134,6 +139,17 @@ public class PageSourceImpl implements PageSource
             {
                 return page;
             }
+            
+            // Avoiding problems in PlasticClassPool.createTransformation()
+            // when the class being loaded has a page superclass
+            final String className = 
componentClassResolver.resolvePageNameToClassName(canonicalPageName);
+            PageClassloaderContext context = 
pageClassloaderContextManager.get(className);
+            final Class<?> superclass = getSuperclass(className, context);
+            final String superclassName = superclass.getName();
+            if (componentClassResolver.isPage(superclassName)) 
+            {
+                
getPage(componentClassResolver.resolvePageClassNameToPageName(superclassName), 
false);
+            }
 
             // In rare race conditions, we may see the same page loaded 
multiple times across
             // different threads. The last built one will "evict" the others 
from the page cache,
@@ -151,21 +167,23 @@ public class PageSourceImpl implements PageSource
                 final ComponentPageElement rootElement = page.getRootElement();
                 componentDependencyRegistry.clear(rootElement);
                 componentDependencyRegistry.register(rootElement);
-                final String className = 
componentClassResolver.resolvePageNameToClassName(canonicalPageName);
-                final PageClassloaderContext context = 
pageClassloaderContextManager.get(className);
-                if (context.isUnknown())
-                {
-                    componentDependencyRegistry.disableInvalidations();
-                    
pageClassloaderContextManager.invalidateAndFireInvalidationEvents(context);
-                    componentDependencyRegistry.disableInvalidations();
-                    pageClassloaderContextManager.get(className);
-                }
+                context = pageClassloaderContextManager.get(className);
+                pageClassloaderContextManager.get(className);
                 
             }
             
         }
     }
 
+    private Class<?> getSuperclass(final String className, 
PageClassloaderContext context) 
+    {
+        try {
+            return 
context.getClassLoader().loadClass(className).getSuperclass();
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @PostInjection
     public void setupInvalidation(@ComponentClasses InvalidationEventHub 
classesHub,
                                   @ComponentTemplates InvalidationEventHub 
templatesHub,
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 ba60eb924..045c68173 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
@@ -967,7 +967,9 @@ public final class TapestryModule
     public void contributeRequestHandler(OrderedConfiguration<RequestFilter> 
configuration, Context context,
 
                                          
@Symbol(TapestryHttpSymbolConstants.PRODUCTION_MODE)
-                                         boolean productionMode)
+                                         boolean productionMode,
+                                         
+                                         final PageClassloaderContextManager 
pageClassloaderContextManager)
     {
         RequestFilter staticFilesFilter = new StaticFilesFilter(context);
 
@@ -1010,6 +1012,29 @@ public final class TapestryModule
         configuration.add("EndOfRequest", fireEndOfRequestEvent);
 
         configuration.addInstance("ErrorFilter", RequestErrorFilter.class);
+        
+        if (!productionMode)
+        {
+            
+            // TODO: change this to only invalidate the current page
+            RequestFilter invalidateUnknownContext = new RequestFilter()
+            {
+                public boolean service(Request request, Response response, 
RequestHandler handler) throws IOException
+                {
+                    try
+                    {
+                        return handler.service(request, response);
+                    } finally
+                    {
+                        
pageClassloaderContextManager.invalidateUnknownContext();
+                    }
+                }
+            };
+
+            configuration.add(PageClassloaderContextManager.class.getName(), 
+                    invalidateUnknownContext, "before:EndOfRequest");
+        }
+        
     }
 
     /**
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContext.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContext.java
index ff6daa952..30b3e5afb 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContext.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContext.java
@@ -18,6 +18,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.Function;
 
 import org.apache.tapestry5.commons.services.PlasticProxyFactory;
 import org.apache.tapestry5.internal.plastic.PlasticClassLoader;
@@ -50,6 +51,8 @@ public class PageClassloaderContext
     private final PlasticProxyFactory proxyFactory;
     
     private PageClassloaderContext root;
+    
+    private final Function<String, PageClassloaderContext> provider;
 
     /**
      * Name of the <code>unknown</code> context (i.e. the one for controlled 
classes
@@ -60,7 +63,8 @@ public class PageClassloaderContext
     public PageClassloaderContext(String name, 
             PageClassloaderContext parent, 
             Set<String> classNames, 
-            PlasticProxyFactory plasticProxyFactory) 
+            PlasticProxyFactory plasticProxyFactory,
+            Function<String, PageClassloaderContext> provider) 
     {
         super();
         this.name = name;
@@ -68,6 +72,7 @@ public class PageClassloaderContext
         this.classNames.addAll(classNames);
         this.plasticManager = plasticProxyFactory.getPlasticManager();
         this.proxyFactory = plasticProxyFactory;
+        this.provider = provider;
         children = new HashSet<>();
         if (plasticProxyFactory.getClassLoader() instanceof PlasticClassLoader)
         {
@@ -75,6 +80,7 @@ public class PageClassloaderContext
            plasticClassLoader.setTag(name);
            plasticClassLoader.setFilter(this::filter);
            
plasticClassLoader.setAlternativeClassloading(this::alternativeClassLoading);
+           // getPlasticManager().getPool().setName(name);
            if (parent != null)
            {
                
getPlasticManager().getPool().setParent(parent.getPlasticManager().getPool());
@@ -109,6 +115,10 @@ public class PageClassloaderContext
                 throw new RuntimeException(e);
             }
         }
+        else if 
(root.getPlasticManager().shouldInterceptClassLoading(className))
+        {
+            context = provider.apply(className);
+        }
         return clasz;
     }
     
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContextManager.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContextManager.java
index b6680fbc5..81b3b6167 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContextManager.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContextManager.java
@@ -83,5 +83,11 @@ public interface PageClassloaderContextManager
      * @return a Class instance.
      */
     Class<?> getClassInstance(Class<?> clasz, String pageName);
+
+    /**
+     * Invalidates the <code>unknown</code> context.
+     * TODO: remove this
+     */
+    void invalidateUnknownContext();
     
 }
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContextManagerImpl.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContextManagerImpl.java
index d7753876a..ba8c3e9f6 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContextManagerImpl.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/services/pageload/PageClassloaderContextManagerImpl.java
@@ -78,6 +78,21 @@ public class PageClassloaderContextManagerImpl implements 
PageClassloaderContext
         NESTED_MERGE_COUNT.set(0);
     }
     
+    @Override
+    public void invalidateUnknownContext()
+    {
+        for (PageClassloaderContext context : root.getChildren())
+        {
+            if (context.isUnknown())
+            {
+                componentDependencyRegistry.disableInvalidations();
+                invalidateAndFireInvalidationEvents(context);
+                componentDependencyRegistry.enableInvalidations();
+                break;
+            }
+        }
+    }
+    
     @Override
     public void initialize(
             final PageClassloaderContext root,
@@ -148,7 +163,8 @@ public class PageClassloaderContextManagerImpl implements 
PageClassloaderContext
         {
             unknownContext = new 
PageClassloaderContext(PageClassloaderContext.UNKOWN_CONTEXT_NAME, root, 
                     Collections.emptySet(), 
-                    plasticProxyFactoryProvider.apply(root.getClassLoader()));
+                    plasticProxyFactoryProvider.apply(root.getClassLoader()),
+                    this::get);
             root.addChild(unknownContext);
             LOGGER.debug("Unknown context: {}", unknownContext);
         }
@@ -263,7 +279,8 @@ public class PageClassloaderContextManagerImpl implements 
PageClassloaderContext
                                 getContextName(className), 
                                 root, 
                                 Collections.singleton(className), 
-                                
plasticProxyFactoryProvider.apply(root.getClassLoader()));
+                                
plasticProxyFactoryProvider.apply(root.getClassLoader()),
+                                this::get);
                     }
                     else 
                     {
@@ -280,16 +297,16 @@ public class PageClassloaderContextManagerImpl implements 
PageClassloaderContext
                                 getContextName(className), 
                                 parentContext, 
                                 Collections.singleton(className), 
-                                
plasticProxyFactoryProvider.apply(parentContext.getClassLoader()));
+                                
plasticProxyFactoryProvider.apply(parentContext.getClassLoader()),
+                                this::get);
                     }
                     
                     context.getParent().addChild(context);
                     
                     // Ensure non-page class is initialized in the correct 
context and classloader.
                     // Pages get their own context and classloader, so this 
initialization
-                    // is both non-needed and a cause for an NPE if it 
happened.
-                    if (!componentClassResolver.isPage(className)/*
-                            && circularDependencies.isEmpty()*/)
+                    // is both non-needed and a cause for an NPE if it happens.
+                    if (!componentClassResolver.isPage(className))
                     {
                         loadClass(className, context);
                     }
@@ -448,7 +465,8 @@ public class PageClassloaderContextManagerImpl implements 
PageClassloaderContext
             "merged " + MERGED_COUNTER.getAndIncrement(),
             parent, 
             classNames, 
-            plasticProxyFactoryProvider.apply(parent.getClassLoader()));
+            plasticProxyFactoryProvider.apply(parent.getClassLoader()),
+            this::get);
         
         parent.addChild(merged);
         

Reply via email to