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 <[email protected]>
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);