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 298a4709e TAP5-2742: refactoring URLChangeTracker to make it parameterized 298a4709e is described below commit 298a4709e485660dbadcaec1841a4a19788e2801 Author: Thiago H. de Paula Figueiredo <thi...@arsmachina.com.br> AuthorDate: Sun Nov 27 12:59:28 2022 -0300 TAP5-2742: refactoring URLChangeTracker to make it parameterized --- .../internal/services/ClassNameHolder.java | 30 ++++++++++ .../services/ComponentInstantiatorSourceImpl.java | 57 +++++++++++++++++-- .../services/ComponentTemplateSourceImpl.java | 25 +++++---- .../internal/services/MessagesSourceImpl.java | 2 +- .../internal/services/TemplateTrackingInfo.java | 65 ++++++++++++++++++++++ .../services/ComponentTemplateSourceImplTest.java | 12 +++- .../ioc/internal/util/URLChangeTracker.java | 44 ++++++++------- 7 files changed, 195 insertions(+), 40 deletions(-) diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClassNameHolder.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClassNameHolder.java new file mode 100644 index 000000000..832e50aaf --- /dev/null +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClassNameHolder.java @@ -0,0 +1,30 @@ +// Copyright 2022 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry5.internal.services; + +import org.apache.tapestry5.ioc.internal.util.URLChangeTracker; + +/** + * Interface that defines types who provide a class name for {@linkplain URLChangeTracker} purposes. + * + * @since 5.8.3 + */ +public interface ClassNameHolder +{ + /** + * Returns the class name associated with a given resource tracked by {@link URLChangeTracker}. + */ + String getClassName(); +} 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 c3e91f270..a64798b7b 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 @@ -12,10 +12,11 @@ package org.apache.tapestry5.internal.services; -import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import org.apache.tapestry5.ComponentResources; import org.apache.tapestry5.beanmodel.services.PlasticProxyFactoryImpl; @@ -81,7 +82,7 @@ public final class ComponentInstantiatorSourceImpl implements ComponentInstantia { private final Set<String> controlledPackageNames = CollectionFactory.newSet(); - private final URLChangeTracker changeTracker; + private final URLChangeTracker<ClassName> changeTracker; private final ClassLoader parent; @@ -150,7 +151,7 @@ public final class ComponentInstantiatorSourceImpl implements ComponentInstantia this.transformerChain = transformerChain; this.logger = logger; this.loggerSource = loggerSource; - this.changeTracker = new URLChangeTracker(classpathURLConverter); + this.changeTracker = new URLChangeTracker<ClassName>(classpathURLConverter); this.tracker = tracker; this.invalidationHub = invalidationHub; this.productionMode = productionMode; @@ -173,10 +174,12 @@ public final class ComponentInstantiatorSourceImpl implements ComponentInstantia public synchronized void checkForUpdates() { - final Set<String> changedResources = changeTracker.getChangedResourcesMemos(); + final Set<ClassName> changedResources = changeTracker.getChangedResourcesInfo(); if (!changedResources.isEmpty()) { - invalidationHub.fireInvalidationEvent(new ArrayList<>(changedResources)); + invalidationHub.fireInvalidationEvent(changedResources.stream() + .map(ClassNameHolder::getClassName) + .collect(Collectors.toList())); } } @@ -308,7 +311,7 @@ public final class ComponentInstantiatorSourceImpl implements ComponentInstantia Resource baseResource = new ClasspathResource(parent, PlasticInternalUtils .toClassPath(className)); - changeTracker.add(baseResource.toURL(), className); + changeTracker.add(baseResource.toURL(), new ClassName(className)); if (isRoot) { @@ -502,4 +505,46 @@ public final class ComponentInstantiatorSourceImpl implements ComponentInstantia } } } + private static class ClassName implements ClassNameHolder + { + private String className; + + public ClassName(String className) + { + super(); + this.className = className; + } + + @Override + public String getClassName() + { + return className; + } + + @Override + public int hashCode() { + return Objects.hash(className); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (!(obj instanceof ClassName)) { + return false; + } + ClassName other = (ClassName) obj; + return Objects.equals(className, other.className); + } + + @Override + public String toString() + { + return className; + } + + } + } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentTemplateSourceImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentTemplateSourceImpl.java index f8e2b83ae..4cfaa9dd2 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentTemplateSourceImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentTemplateSourceImpl.java @@ -20,6 +20,7 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.stream.Collectors; import org.apache.tapestry5.TapestryConstants; import org.apache.tapestry5.commons.Location; @@ -53,7 +54,7 @@ public final class ComponentTemplateSourceImpl extends InvalidationEventHubImpl { private final TemplateParser parser; - private final URLChangeTracker tracker; + private final URLChangeTracker<TemplateTrackingInfo> tracker; private final ComponentResourceLocator locator; @@ -120,11 +121,11 @@ public final class ComponentTemplateSourceImpl extends InvalidationEventHubImpl ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer, ThreadLocale threadLocale, Logger logger) { - this(productionMode, parser, locator, new URLChangeTracker(classpathURLConverter), componentRequestSelectorAnalyzer, threadLocale, logger); + this(productionMode, parser, locator, new URLChangeTracker<TemplateTrackingInfo>(classpathURLConverter), componentRequestSelectorAnalyzer, threadLocale, logger); } ComponentTemplateSourceImpl(boolean productionMode, TemplateParser parser, ComponentResourceLocator locator, - URLChangeTracker tracker, ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer, + URLChangeTracker<TemplateTrackingInfo> tracker, ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer, ThreadLocale threadLocale, Logger logger) { super(productionMode, logger); @@ -211,7 +212,7 @@ public final class ComponentTemplateSourceImpl extends InvalidationEventHubImpl if (!r.exists()) return missingTemplate; - tracker.add(r.toURL(), className); + tracker.add(r.toURL(), new TemplateTrackingInfo(r.getPath(), className)); return parser.parseTemplate(r); } @@ -246,18 +247,22 @@ public final class ComponentTemplateSourceImpl extends InvalidationEventHubImpl */ public void checkForUpdates() { - final Set<String> changedResourcesMemos = tracker.getChangedResourcesMemos(); - if (!changedResourcesMemos.isEmpty()) + final Set<TemplateTrackingInfo> changedResourcesInfo = tracker.getChangedResourcesInfo(); + if (!changedResourcesInfo.isEmpty()) { - logger.info("Changed template(s) found: {}", String.join(", ", changedResourcesMemos)); + if (logger.isInfoEnabled()) + { + logger.info("Changed template(s) found: {}", String.join(", ", + changedResourcesInfo.stream().map(TemplateTrackingInfo::getTemplate).collect(Collectors.toList()))); + } final Iterator<Entry<MultiKey, Resource>> templateResourcesIterator = templateResources.entrySet().iterator(); - for (String className : changedResourcesMemos) + for (TemplateTrackingInfo info : changedResourcesInfo) { while (templateResourcesIterator.hasNext()) { final MultiKey key = templateResourcesIterator.next().getKey(); - if (className.equals((String) key.getValues()[0])) + if (info.getClassName().equals((String) key.getValues()[0])) { templates.remove(templateResources.get(key)); templateResourcesIterator.remove(); @@ -265,7 +270,7 @@ public final class ComponentTemplateSourceImpl extends InvalidationEventHubImpl } } - fireInvalidationEvent(new ArrayList<>(changedResourcesMemos)); + fireInvalidationEvent(changedResourcesInfo.stream().map(TemplateTrackingInfo::getClassName).collect(Collectors.toList())); } } 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 43b7e70d1..9acd5dfc9 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 @@ -44,7 +44,7 @@ import java.util.Map; */ public class MessagesSourceImpl extends InvalidationEventHubImpl implements MessagesSource { - private final URLChangeTracker tracker; + private final URLChangeTracker<TemplateTrackingInfo> tracker; private final PropertiesFileParser propertiesFileParser; diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TemplateTrackingInfo.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TemplateTrackingInfo.java new file mode 100644 index 000000000..0f50c3b96 --- /dev/null +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TemplateTrackingInfo.java @@ -0,0 +1,65 @@ +// Copyright 2022 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package org.apache.tapestry5.internal.services; + +import java.util.Objects; + +/** + * Class that holds information about a template for tracking. + */ +final public class TemplateTrackingInfo implements ClassNameHolder +{ + public TemplateTrackingInfo(String template, String className) + { + super(); + this.template = template; + this.className = className; + } + private String template; + private String className; + + public String getTemplate() { + return template; + } + + public String getClassName() + { + return className; + } + + @Override + public int hashCode() { + return Objects.hash(className, template); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (!(obj instanceof TemplateTrackingInfo)) { + return false; + } + TemplateTrackingInfo other = (TemplateTrackingInfo) obj; + return Objects.equals(className, other.className) && Objects.equals(template, other.template); + } + + @Override + public String toString() + { + return "TemplateTrackingInfo [template=" + template + ", className=" + className + "]"; + } + +} \ No newline at end of file diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentTemplateSourceImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentTemplateSourceImplTest.java index e44a4580a..31ff8feb9 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentTemplateSourceImplTest.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentTemplateSourceImplTest.java @@ -106,9 +106,11 @@ public class ComponentTemplateSourceImplTest extends InternalBaseTestCase Resource resource = mockResource(); ComponentResourceLocator locator = mockLocator(model, english, resource); - train_getComponentClassName(model, PACKAGE + ".Fred"); + final String className = PACKAGE + ".Fred"; + train_getComponentClassName(model, className); expect(resource.exists()).andReturn(true); + expect(resource.getPath()).andReturn(className.replace(".", "/") + ".tml"); expect(resource.toURL()).andReturn(null); train_parseTemplate(parser, resource, template); @@ -219,11 +221,13 @@ public class ComponentTemplateSourceImplTest extends InternalBaseTestCase ComponentModel model = mockComponentModel(); ComponentResourceLocator locator = newMock(ComponentResourceLocator.class); - train_getComponentClassName(model, PACKAGE + ".Fred"); + final String className = PACKAGE + ".Fred"; + train_getComponentClassName(model, className); expect(locator.locateTemplate(model, english)).andReturn(resource).once(); expect(resource.exists()).andReturn(true).anyTimes(); + expect(resource.getPath()).andReturn(className.replace(".", "/") + ".tml").anyTimes(); expect(resource.toURL()).andReturn(null).anyTimes(); expect(locator.locateTemplate(model, french)).andReturn(resource).once(); @@ -289,13 +293,15 @@ public class ComponentTemplateSourceImplTest extends InternalBaseTestCase Resource resource = mockResource(); ComponentResourceLocator locator = mockLocator(model, english, null); - train_getComponentClassName(model, "foo.Bar"); + final String className = "foo.Bar"; + train_getComponentClassName(model, className); train_getParentModel(model, parentModel); expect(locator.locateTemplate(parentModel, english)).andReturn(resource).once(); expect(resource.exists()).andReturn(true); + expect(resource.getPath()).andReturn(className.replace(".", "/") + ".tml"); expect(resource.toURL()).andReturn(null); train_parseTemplate(parser, resource, template); diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/URLChangeTracker.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/URLChangeTracker.java index 2e78e0697..151c5d7be 100644 --- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/URLChangeTracker.java +++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/URLChangeTracker.java @@ -14,10 +14,6 @@ package org.apache.tapestry5.ioc.internal.util; -import org.apache.tapestry5.commons.util.CollectionFactory; -import org.apache.tapestry5.ioc.internal.services.ClasspathURLConverterImpl; -import org.apache.tapestry5.ioc.services.ClasspathURLConverter; - import java.io.File; import java.io.IOException; import java.net.URISyntaxException; @@ -26,14 +22,21 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.apache.tapestry5.commons.util.CollectionFactory; +import org.apache.tapestry5.ioc.internal.services.ClasspathURLConverterImpl; +import org.apache.tapestry5.ioc.services.ClasspathURLConverter; + /** * Given a (growing) set of URLs, can periodically check to see if any of the underlying resources has changed. This * class is capable of using either millisecond-level granularity or second-level granularity. Millisecond-level * granularity is used by default. Second-level granularity is provided for compatibility with browsers vis-a-vis * resource caching -- that's how granular they get with their "If-Modified-Since", "Last-Modified" and "Expires" * headers. + * + * @param <T> The type of the optional information about the tracked resource. This type should + * implement <code>equals()</code> and <code>hashCode(). */ -public class URLChangeTracker +public class URLChangeTracker<T> { private static final long FILE_DOES_NOT_EXIST_TIMESTAMP = -1L; @@ -147,13 +150,14 @@ public class URLChangeTracker * * @param url * of the resource to add, or null if not known - * @param memo - * some information about the tracked URL, most probably the associated class name + * @param resourceInfo + * an optional object containing information about the tracked URL. It's + * returned in the {@link #getChangedResourcesInfo()} method. * @return the current timestamp for the URL (possibly rounded off for granularity reasons), or 0 if the URL is * null * @since 5.8.3 */ - public long add(URL url, String memo) + public long add(URL url, T resourceInfo) { if (url == null) return 0; @@ -173,7 +177,7 @@ public class URLChangeTracker // A quick and imperfect fix for TAPESTRY-1918. When a file // is added, add the directory containing the file as well. - fileToTimestamp.put(resourceFile, new TrackingInfo(timestamp, memo)); + fileToTimestamp.put(resourceFile, new TrackingInfo(timestamp, resourceInfo)); if (trackFolderChanges) { @@ -240,12 +244,12 @@ public class URLChangeTracker } /** - * Re-acquires the last updated timestamp for each URL and returns the memo value for all files with a changed timestamp. + * Re-acquires the last updated timestamp for each URL and returns the non-null resource information for all files with a changed timestamp. */ - public Set<String> getChangedResourcesMemos() + public Set<T> getChangedResourcesInfo() { - Set<String> changedResourcesMemos = new HashSet<>(); + Set<T> changedResourcesInfo = new HashSet<>(); for (Map.Entry<File, TrackingInfo> entry : fileToTimestamp.entrySet()) { @@ -255,15 +259,15 @@ public class URLChangeTracker if (current != newTimestamp) { - if (value.memo != null) + if (value.resourceInfo != null) { - changedResourcesMemos.add(value.memo); + changedResourcesInfo.add(value.resourceInfo); } value.timestamp = newTimestamp; } } - return changedResourcesMemos; + return changedResourcesInfo; } @@ -310,22 +314,22 @@ public class URLChangeTracker return fileToTimestamp.size(); } - private static final class TrackingInfo + private final class TrackingInfo { private long timestamp; - private String memo; + private T resourceInfo; - public TrackingInfo(long timestamp, String memo) + public TrackingInfo(long timestamp, T resourceInfo) { this.timestamp = timestamp; - this.memo = memo; + this.resourceInfo = resourceInfo; } @Override public String toString() { - return "Info [timestamp=" + timestamp + ", memo=" + memo + "]"; + return "Info [timestamp=" + timestamp + ", resourceInfo=" + resourceInfo + "]"; } }