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


The following commit(s) were added to refs/heads/better-page-invalidation by 
this push:
     new af22dd7ae TAP5-2742: smart page cache invalidation for message (i18n) 
files
af22dd7ae is described below

commit af22dd7ae1cffb797492ce451b73cc8976a1cfed
Author: Thiago H. de Paula Figueiredo <thi...@arsmachina.com.br>
AuthorDate: Fri Dec 23 12:26:51 2022 -0300

    TAP5-2742: smart page cache invalidation for message (i18n) files
---
 .../services/ComponentMessagesSourceImpl.java      |  17 ++--
 .../internal/services/MessagesSourceImpl.java      | 109 ++++++++++++++++++---
 .../internal/services/MessagesTrackingInfo.java    |  38 +++----
 .../internal/util/MessageCatalogResource.java      |   6 +-
 .../services/ComponentMessagesSourceImplTest.java  |  17 +++-
 5 files changed, 145 insertions(+), 42 deletions(-)

diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentMessagesSourceImpl.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentMessagesSourceImpl.java
index 223e0dca6..763dccea5 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentMessagesSourceImpl.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentMessagesSourceImpl.java
@@ -31,6 +31,7 @@ import 
org.apache.tapestry5.ioc.services.ClasspathURLConverter;
 import org.apache.tapestry5.ioc.services.ThreadLocale;
 import org.apache.tapestry5.ioc.services.UpdateListener;
 import org.apache.tapestry5.model.ComponentModel;
+import org.apache.tapestry5.services.ComponentClassResolver;
 import org.apache.tapestry5.services.messages.ComponentMessagesSource;
 import org.apache.tapestry5.services.messages.PropertiesFileParser;
 import org.apache.tapestry5.services.pageload.ComponentRequestSelectorAnalyzer;
@@ -82,26 +83,30 @@ public class ComponentMessagesSourceImpl implements 
ComponentMessagesSource, Upd
                                        boolean productionMode, List<Resource> 
appCatalogResources, PropertiesFileParser parser,
                                        ComponentResourceLocator 
resourceLocator, ClasspathURLConverter classpathURLConverter,
                                        ComponentRequestSelectorAnalyzer 
componentRequestSelectorAnalyzer,
-                                       ThreadLocale threadLocale, Logger 
logger)
+                                       ThreadLocale threadLocale, 
ComponentClassResolver componentClassResolver,
+                                       Logger logger)
     {
-        this(productionMode, appCatalogResources, resourceLocator, parser, new 
URLChangeTracker(classpathURLConverter), componentRequestSelectorAnalyzer, 
threadLocale, logger);
+        this(productionMode, appCatalogResources, resourceLocator, parser, new 
URLChangeTracker(classpathURLConverter), 
+                componentRequestSelectorAnalyzer, threadLocale, 
componentClassResolver, logger);
     }
 
     ComponentMessagesSourceImpl(boolean productionMode, Resource 
appCatalogResource,
                                 ComponentResourceLocator resourceLocator, 
PropertiesFileParser parser, 
                                 URLChangeTracker tracker, 
ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer,
-                                ThreadLocale threadLocale, Logger logger)
+                                ThreadLocale threadLocale, 
ComponentClassResolver componentClassResolver, 
+                                Logger logger)
     {
-        this(productionMode, Arrays.asList(appCatalogResource), 
resourceLocator, parser, tracker, componentRequestSelectorAnalyzer, 
threadLocale, logger);
+        this(productionMode, Arrays.asList(appCatalogResource), 
resourceLocator, parser, tracker, componentRequestSelectorAnalyzer, 
threadLocale, componentClassResolver, logger);
     }
 
     ComponentMessagesSourceImpl(boolean productionMode, List<Resource> 
appCatalogResources,
                                 ComponentResourceLocator resourceLocator, 
PropertiesFileParser parser, 
                                 URLChangeTracker tracker, 
ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer,
-                                ThreadLocale threadLocale, Logger logger)
+                                ThreadLocale threadLocale, 
ComponentClassResolver componentClassResolver,
+                                Logger logger)
     {
         messagesSource = new MessagesSourceImpl(productionMode, productionMode 
? null : tracker, resourceLocator,
-                parser, logger);
+                parser, componentClassResolver, logger);
 
         appCatalogBundle = createAppCatalogBundle(appCatalogResources);
         this.componentRequestSelectorAnalyzer = 
componentRequestSelectorAnalyzer;
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/MessagesSourceImpl.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/MessagesSourceImpl.java
index ba4233716..9ef7c4f9e 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/MessagesSourceImpl.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/MessagesSourceImpl.java
@@ -12,6 +12,15 @@
 
 package org.apache.tapestry5.internal.services;
 
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
 import org.apache.tapestry5.commons.Messages;
 import org.apache.tapestry5.commons.Resource;
 import org.apache.tapestry5.commons.util.CaseInsensitiveMap;
@@ -20,18 +29,12 @@ import org.apache.tapestry5.commons.util.MultiKey;
 import org.apache.tapestry5.func.F;
 import org.apache.tapestry5.internal.event.InvalidationEventHubImpl;
 import org.apache.tapestry5.ioc.internal.util.URLChangeTracker;
+import org.apache.tapestry5.services.ComponentClassResolver;
 import org.apache.tapestry5.services.messages.PropertiesFileParser;
 import org.apache.tapestry5.services.pageload.ComponentResourceLocator;
 import org.apache.tapestry5.services.pageload.ComponentResourceSelector;
 import org.slf4j.Logger;
 
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-
 /**
  * A utility class that encapsulates all the logic for reading properties 
files and assembling {@link Messages} from
  * them, in accordance with extension rules and locale. This represents code 
that was refactored out of
@@ -52,7 +55,11 @@ public class MessagesSourceImpl extends 
InvalidationEventHubImpl implements Mess
     private final PropertiesFileParser propertiesFileParser;
 
     private final ComponentResourceLocator resourceLocator;
-
+    
+    private final ComponentClassResolver componentClassResolver;
+    
+    private final Logger logger;        
+    
     /**
      * Keyed on bundle id and ComponentResourceSelector.
      */
@@ -73,6 +80,7 @@ public class MessagesSourceImpl extends 
InvalidationEventHubImpl implements Mess
 
     public MessagesSourceImpl(boolean productionMode, URLChangeTracker tracker,
                               ComponentResourceLocator resourceLocator, 
PropertiesFileParser propertiesFileParser,
+                              ComponentClassResolver componentClassResolver,
                               Logger logger)
     {
         super(productionMode, logger);
@@ -80,6 +88,8 @@ public class MessagesSourceImpl extends 
InvalidationEventHubImpl implements Mess
         this.tracker = tracker;
         this.propertiesFileParser = propertiesFileParser;
         this.resourceLocator = resourceLocator;
+        this.logger = logger;
+        this.componentClassResolver = componentClassResolver;
     }
 
     public void checkForUpdates()
@@ -87,26 +97,77 @@ public class MessagesSourceImpl extends 
InvalidationEventHubImpl implements Mess
         if (tracker != null)
         {
             final Set<MessagesTrackingInfo> changedResources = 
tracker.getChangedResourcesInfo();
+            if (!changedResources.isEmpty())
+            {
+                logger.info("Changed message files: {}", 
changedResources.stream()
+                        .map(MessagesTrackingInfo::getResource)
+                        .map(Resource::toString)
+                        .collect(Collectors.joining(", ")));
+            }
+            
+            boolean applicationLevelChange = false;
+            
             for (MessagesTrackingInfo info : changedResources) 
             {
+                
+                final String className = info.getClassName();
+                
                 // An application-level file was changed, so we need to 
invalidate everything.
-                if (info == null)
+                if (info.getClassName() == null)
                 {
                     invalidate();
+                    applicationLevelChange = true;
                     break;
                 }
                 else
                 {
                     
+                    final Iterator<Entry<MultiKey, Messages>> 
messagesByBundleIdAndSelectorIterator = 
+                            
messagesByBundleIdAndSelector.entrySet().iterator();
+                    
+                    while (messagesByBundleIdAndSelectorIterator.hasNext())
+                    {
+                        final Entry<MultiKey, Messages> entry = 
messagesByBundleIdAndSelectorIterator.next();
+                        if (className.equals(entry.getKey().getValues()[0]))
+                        {
+                            messagesByBundleIdAndSelectorIterator.remove();
+                        }
+                    }
+                    
+                    final Iterator<Entry<MultiKey, Map<String, String>>> 
cookedPropertiesIterator = 
+                            cookedProperties.entrySet().iterator();
                     
+                    while (cookedPropertiesIterator.hasNext())
+                    {
+                        final Entry<MultiKey, Map<String, String>> entry = 
cookedPropertiesIterator.next();
+                        if (className.equals(entry.getKey().getValues()[0]))
+                        {
+                            cookedPropertiesIterator.remove();
+                        }
+                    }
                     
+                    final String resourceFile = info.getResource().getFile();
+                    final Iterator<Entry<Resource, Map<String, String>>> 
rawPropertiesIterator = rawProperties.entrySet().iterator();
+                    while (rawPropertiesIterator.hasNext())
+                    {
+                        final Entry<Resource, Map<String, String>> entry = 
rawPropertiesIterator.next();
+                        if (resourceFile.equals(entry.getKey().getFile()))
+                        {
+                            rawPropertiesIterator.remove();
+                        }
+                    }
                     
                 }
             }
-            fireInvalidationEvent(changedResources.stream()
-                    .map(ClassNameHolder::getClassName)
-                    .filter(Objects::nonNull)
-                    .collect(Collectors.toList()));
+            
+            if (!changedResources.isEmpty() && !applicationLevelChange)
+            {
+                fireInvalidationEvent(changedResources.stream()
+                        .filter(Objects::nonNull)
+                        .map(ClassNameHolder::getClassName)
+                        .filter(Objects::nonNull)
+                        .collect(Collectors.toList()));
+            }
         }
     }
 
@@ -232,8 +293,8 @@ public class MessagesSourceImpl extends 
InvalidationEventHubImpl implements Mess
 
         if (tracker != null)
         {
-            MessagesTrackingInfo info = bundle != null ? new 
MessagesTrackingInfo(
-                    resource.getFile(), bundle.getId(), 
bundle.getBaseResource().getFile()) : null;
+            MessagesTrackingInfo info = new MessagesTrackingInfo(
+                    resource, bundle != null ? bundle.getId() : bundle, 
getClassName(bundle));
             tracker.add(resource.toURL(), info);
         }
 
@@ -246,4 +307,22 @@ public class MessagesSourceImpl extends 
InvalidationEventHubImpl implements Mess
         }
     }
 
+    private String getClassName(MessagesBundle bundle)
+    {
+        String className = null;
+        if (bundle != null && bundle.getBaseResource().getPath() != null)
+        {
+            final String path = bundle.getBaseResource().getPath();
+            if (path.endsWith(".class"))
+            {
+                className = path.replace('/', '.').replace(".class", "");
+                if (!componentClassResolver.isPage(className)) 
+                {
+                    className = null;
+                }
+            }
+        }
+        return className;
+    }
+
 }
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/MessagesTrackingInfo.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/MessagesTrackingInfo.java
index 9943b261f..5eead5445 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/MessagesTrackingInfo.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/MessagesTrackingInfo.java
@@ -15,6 +15,8 @@ package org.apache.tapestry5.internal.services;
 
 import java.util.Objects;
 
+import org.apache.tapestry5.commons.Resource;
+
 /**
  * Class that holds information about a messages properties file for tracking.
  */
@@ -22,13 +24,13 @@ final public class MessagesTrackingInfo implements 
ClassNameHolder
 {
     
     private Object bundleId;
-    private String propertiesFile;
+    private Resource resource;
     private String className;
 
-    public MessagesTrackingInfo(String propertiesFile, Object bundleId, String 
className) 
+    public MessagesTrackingInfo(Resource resource, Object bundleId, String 
className) 
     {
         super();
-        this.propertiesFile = propertiesFile;
+        this.resource = resource;
         this.className = className;
         this.bundleId = bundleId;
     }
@@ -38,9 +40,9 @@ final public class MessagesTrackingInfo implements 
ClassNameHolder
         return bundleId;
     }
     
-    public String getPropertiesFile() 
+    public Resource getResource() 
     {
-        return propertiesFile;
+        return resource;
     }
     
     public String getClassName() 
@@ -49,31 +51,31 @@ final public class MessagesTrackingInfo implements 
ClassNameHolder
     }
 
     @Override
-    public int hashCode() 
+    public int hashCode()
     {
-        return Objects.hash(bundleId, className, propertiesFile);
+        return Objects.hash(bundleId, className, resource);
     }
 
     @Override
-    public boolean equals(Object obj) 
+    public boolean equals(Object obj)
     {
-        if (this == obj) 
-        {
+        if (this == obj)
             return true;
-        }
-        if (!(obj instanceof MessagesTrackingInfo)) 
-        {
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
             return false;
-        }
         MessagesTrackingInfo other = (MessagesTrackingInfo) obj;
-        return Objects.equals(bundleId, other.bundleId) && 
Objects.equals(className, other.className) && Objects.equals(propertiesFile, 
other.propertiesFile);
+        return Objects.equals(bundleId, other.bundleId)
+                && Objects.equals(className, other.className)
+                && Objects.equals(resource, other.resource);
     }
 
     @Override
-    public String toString() 
+    public String toString()
     {
-        return "MessagesTrackingInfo [className=" + className + ", bundleId=" 
+ bundleId + ", propertiesFile=" + propertiesFile + "]";
+        return "MessagesTrackingInfo [resource=" + resource + ", className=" + 
className
+                + ", bundleId=" + bundleId + "]";
     }
-
     
 }
\ No newline at end of file
diff --git 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/MessageCatalogResource.java
 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/MessageCatalogResource.java
index ef89f8cfe..8992efe59 100644
--- 
a/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/MessageCatalogResource.java
+++ 
b/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/MessageCatalogResource.java
@@ -54,7 +54,11 @@ public class MessageCatalogResource extends VirtualResource
                 // but that's the breaks). When that occurs, we tell the 
ResourceChangeTracker to fire its invalidation
                 // event. That flushes out all the assets it has cached, 
including StreamableResources for JavaScript files,
                 // including the one created here to represent the application 
message catalog.
-                changeTracker.forceInvalidationEvent();
+                
+                // TAP-2742: now we're doing smarter page cache invalidation,
+                // so we don't need to clear everything anymore.
+                
+                // changeTracker.forceInvalidationEvent();
             }
         });
     }
diff --git 
a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentMessagesSourceImplTest.java
 
b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentMessagesSourceImplTest.java
index a1e610454..323767acd 100644
--- 
a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentMessagesSourceImplTest.java
+++ 
b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentMessagesSourceImplTest.java
@@ -30,13 +30,16 @@ import 
org.apache.tapestry5.ioc.internal.util.URLChangeTracker;
 import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
 import org.apache.tapestry5.ioc.services.ThreadLocale;
 import org.apache.tapestry5.model.ComponentModel;
+import org.apache.tapestry5.services.ComponentClassResolver;
 import org.apache.tapestry5.services.messages.ComponentMessagesSource;
 import org.apache.tapestry5.services.pageload.ComponentRequestSelectorAnalyzer;
 import org.apache.tapestry5.services.pageload.ComponentResourceLocator;
+import org.easymock.EasyMock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 /**
@@ -67,6 +70,16 @@ public class ComponentMessagesSourceImplTest extends 
InternalBaseTestCase
     private ComponentResourceLocator resourceLocator;
     
     private Logger logger = 
LoggerFactory.getLogger(ComponentMessagesSourceImplTest.class);
+    
+    private final ComponentClassResolver componentClassResolver = 
EasyMock.createMock(ComponentClassResolver.class);
+
+    @BeforeMethod
+    public void setupMethod()
+    {
+        
EasyMock.expect(componentClassResolver.isPage(EasyMock.anyString())).andReturn(false).anyTimes();
+
+    }
+
 
     @BeforeClass
     public void setup()
@@ -74,7 +87,7 @@ public class ComponentMessagesSourceImplTest extends 
InternalBaseTestCase
         resourceLocator = getService(ComponentResourceLocator.class);
 
         source = new ComponentMessagesSourceImpl(false, 
simpleComponentResource.forFile("AppCatalog.properties"),
-                resourceLocator, new PropertiesFileParserImpl(), tracker, 
componentRequestSelectorAnalyzer, threadLocale, logger);
+                resourceLocator, new PropertiesFileParserImpl(), tracker, 
componentRequestSelectorAnalyzer, threadLocale, componentClassResolver, logger);
     }
 
     @AfterClass
@@ -244,7 +257,7 @@ public class ComponentMessagesSourceImplTest extends 
InternalBaseTestCase
         List<Resource> resources = Arrays.asList(resource);
 
         ComponentMessagesSource source = new ComponentMessagesSourceImpl(true, 
resources,
-                new PropertiesFileParserImpl(), resourceLocator, converter, 
componentRequestSelectorAnalyzer, threadLocale, logger);
+                new PropertiesFileParserImpl(), resourceLocator, converter, 
componentRequestSelectorAnalyzer, threadLocale, componentClassResolver, logger);
 
         Messages messages = source.getMessages(model, Locale.ENGLISH);
 

Reply via email to