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 40550e1ad TAP5-2742: introducingtypes of dependencies 40550e1ad is described below commit 40550e1ad0732db04612a6d64646f36a34242b27 Author: Thiago H. de Paula Figueiredo <thi...@arsmachina.com.br> AuthorDate: Sat May 13 12:21:27 2023 -0300 TAP5-2742: introducingtypes of dependencies --- .../tapestry5/corelib/pages/PageCatalog.java | 10 +- .../ComponentDependencyGraphvizGeneratorImpl.java | 50 +++--- .../services/ComponentDependencyRegistry.java | 36 +++- .../services/ComponentDependencyRegistryImpl.java | 193 ++++++++++++--------- .../services/ComponentInstantiatorSourceImpl.java | 6 +- .../internal/services/PageSourceImpl.java | 43 ++++- .../PageClassloaderContextManagerImpl.java | 11 +- .../ComponentDependencyRegistryImplTest.java | 87 ++++++++-- 8 files changed, 293 insertions(+), 143 deletions(-) diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java index e887c0da1..31c557649 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/pages/PageCatalog.java @@ -46,6 +46,7 @@ import org.apache.tapestry5.http.services.Request; import org.apache.tapestry5.internal.PageCatalogTotals; import org.apache.tapestry5.internal.services.ComponentDependencyGraphvizGenerator; import org.apache.tapestry5.internal.services.ComponentDependencyRegistry; +import org.apache.tapestry5.internal.services.ComponentDependencyRegistry.DependencyType; import org.apache.tapestry5.internal.services.PageSource; import org.apache.tapestry5.internal.services.ReloadHelper; import org.apache.tapestry5.internal.structure.ComponentPageElement; @@ -370,7 +371,14 @@ public class PageCatalog public List<String> getDependencies() { - List<String> dependencies = new ArrayList<>(componentDependencyRegistry.getDependencies(getSelectedPageClassName())); + final String selectedPageClassName = getSelectedPageClassName(); + List<String> dependencies = new ArrayList<>(); + dependencies.addAll( + componentDependencyRegistry.getDependencies(selectedPageClassName, DependencyType.USAGE)); + dependencies.addAll( + componentDependencyRegistry.getDependencies(selectedPageClassName, DependencyType.INJECT_PAGE)); + dependencies.addAll( + componentDependencyRegistry.getDependencies(selectedPageClassName, DependencyType.SUPERCLASS)); Collections.sort(dependencies); return dependencies; } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyGraphvizGeneratorImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyGraphvizGeneratorImpl.java index 81a179e46..005f2a26a 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyGraphvizGeneratorImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyGraphvizGeneratorImpl.java @@ -17,6 +17,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.Set; +import org.apache.tapestry5.internal.services.ComponentDependencyRegistry.DependencyType; import org.apache.tapestry5.services.ComponentClassResolver; public class ComponentDependencyGraphvizGeneratorImpl implements ComponentDependencyGraphvizGenerator { @@ -48,34 +49,41 @@ public class ComponentDependencyGraphvizGeneratorImpl implements ComponentDepend final Set<String> allClasses = new HashSet<>(); - for (String className : classNames) + for (DependencyType dependencyType : DependencyType.values()) { - addDependencies(className, allClasses); - } - - final StringBuilder dependencySection = new StringBuilder(); - - for (String className : allClasses) - { - final Node node = createNode(componentClassResolver.getLogicalName(className), className); - dotFile.append(getNodeDefinition(node)); - for (String dependency : node.dependencies) + + for (String className : classNames) + { + addDependencies(className, allClasses, dependencyType); + } + + final StringBuilder dependencySection = new StringBuilder(); + + for (String className : allClasses) { - dependencySection.append(getNodeDependencyDefinition(node, dependency)); + final Node node = createNode(componentClassResolver.getLogicalName(className), className, dependencyType); + dotFile.append(getNodeDefinition(node, dependencyType)); + for (String dependency : node.dependencies) + { + dependencySection.append(getNodeDependencyDefinition(node, dependency)); + } } + + dotFile.append("\n"); + dotFile.append(dependencySection); + dotFile.append("\n"); + } - dotFile.append("\n"); - dotFile.append(dependencySection); - dotFile.append("\n"); dotFile.append("}"); return dotFile.toString(); } - private String getNodeDefinition(Node node) + private String getNodeDefinition(Node node, DependencyType dependencyType) { + // TODO: add some diferentiation between dependency types return String.format("\t%s [label=\"%s\", tooltip=\"%s\"];\n", node.id, node.label, node.className); } @@ -116,21 +124,21 @@ public class ComponentDependencyGraphvizGeneratorImpl implements ComponentDepend return label.replace('.', '_').replace('/', '_'); } - private void addDependencies(String className, Set<String> allClasses) + private void addDependencies(String className, Set<String> allClasses, DependencyType type) { if (!allClasses.contains(className)) { allClasses.add(className); - for (String dependency : componentDependencyRegistry.getDependencies(className)) + for (String dependency : componentDependencyRegistry.getDependencies(className, type)) { - addDependencies(dependency, allClasses); + addDependencies(dependency, allClasses, type); } } } - private Node createNode(String logicalName, String className) + private Node createNode(String logicalName, String className, DependencyType type) { - return new Node(logicalName, className, componentDependencyRegistry.getDependencies(className)); + return new Node(logicalName, className, componentDependencyRegistry.getDependencies(className, type)); } private static final class Node { diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistry.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistry.java index 5eebb690e..1613095c2 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistry.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistry.java @@ -34,6 +34,29 @@ import org.apache.tapestry5.plastic.PlasticField; */ public interface ComponentDependencyRegistry { + /** + * Enum class defining the types of dependency components, pages and mixins can + * have among them. + */ + public static enum DependencyType + { + + /** + * Simple usage of components and mixins in components and pages + */ + USAGE, + + /** + * Superclass/subclass dependency. + */ + SUPERCLASS, + + /** + * Dependency by usage of the {@linkplain InjectPage} annotation. + */ + INJECT_PAGE; + } + /** * Name of the file where the dependency information is stored between webapp runs. */ @@ -76,16 +99,11 @@ public interface ComponentDependencyRegistry { Set<String> getDependents(String className); /** - * Returns the fully qualified names of the direct dependencies of a given component. - * Doesn't include dependencies that are pages. - */ - Set<String> getDependencies(String className); - - /** - * Returns the fully qualified names of the direct dependencies of a given component, - * but just the ones that are pages. + * Returns the fully qualified names of the direct dependencies of a given component + * and a given dependency type. + * @see DependencyType */ - Set<String> getPageDependencies(String className); + Set<String> getDependencies(String className, DependencyType type); /** * Signs up this registry to invalidation events from a given hub. diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistryImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistryImpl.java index e86c701ec..92a41e34b 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistryImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistryImpl.java @@ -1,4 +1,4 @@ -// Copyright 2022 The Apache Software Foundation +// Copyright 2022, 2023 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. @@ -33,7 +33,6 @@ import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; import java.util.function.Consumer; -import java.util.function.Predicate; import java.util.stream.Collectors; import org.apache.tapestry5.ComponentResources; @@ -53,7 +52,6 @@ import org.apache.tapestry5.internal.structure.ComponentPageElement; import org.apache.tapestry5.ioc.Orderable; import org.apache.tapestry5.ioc.internal.util.ClasspathResource; import org.apache.tapestry5.ioc.internal.util.InternalUtils; -import org.apache.tapestry5.json.JSONArray; import org.apache.tapestry5.json.JSONObject; import org.apache.tapestry5.model.ComponentModel; import org.apache.tapestry5.model.EmbeddedComponentModel; @@ -77,18 +75,14 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis private static final String META_ATTRIBUTE_SEPARATOR = ","; // Key is a component, values are the components that depend on it. - final private Map<String, Set<String>> map; - - // Dependencies that are only for invalidation (i.e. from one component to - // a page injected through @InjectPage - final private Set<InvalidationOnlyDependency> invalidationOnlyDependencies; + final private Map<String, Set<Dependency>> map; // Cache to check which classes were already processed or not. final private Set<String> alreadyProcessed; final private File storedDependencies; - final private static ThreadLocal<Boolean> INVALIDATIONS_ENABLED = ThreadLocal.withInitial(() -> Boolean.TRUE); + final private static ThreadLocal<Integer> INVALIDATIONS_DISABLED = ThreadLocal.withInitial(() -> 0); final private PlasticManager plasticManager; @@ -113,7 +107,6 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis this.pageClassloaderContextManager = pageClassloaderContextManager; map = new HashMap<>(); alreadyProcessed = new HashSet<>(); - invalidationOnlyDependencies = new HashSet<InvalidationOnlyDependency>(); this.plasticManager = plasticManager; this.resolver = componentClassResolver; this.templateParser = templateParser; @@ -141,14 +134,16 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis .collect(Collectors.toSet()); for (String dependency : dependencies) { - add(className, dependency); + + // TODO: fix storing and reading dependency file +// add(className, dependency); alreadyProcessed.add(dependency); } alreadyProcessed.add(className); } } catch (IOException e) { - throw new TapestryException("Exception trying to read " + ComponentDependencyRegistry.FILENAME, e); + throw new TapestryException("Exception trying to read " + FILENAME, e); } } @@ -183,16 +178,15 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis if (field.isAnnotationPresent(InjectComponent.class)) { final Class<?> dependency = field.getType(); - add(component, dependency); + add(component, dependency, DependencyType.USAGE); processClass.accept(dependency); } // Page injection annotation - if (field.isAnnotationPresent(InjectComponent.class)) + if (field.isAnnotationPresent(InjectPage.class)) { final Class<?> dependency = field.getType(); - invalidationOnlyDependencies.add( - new InvalidationOnlyDependency(className, dependency.getName())); + add(component, dependency, DependencyType.INJECT_PAGE); } // @Component @@ -210,7 +204,7 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis if (isTransformed(superclass)) { processClass.accept(superclass); - add(component, superclass); + add(component, superclass, DependencyType.SUPERCLASS); } alreadyProcessed.add(className); @@ -258,13 +252,13 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis if (logicalName != null) { dependency = resolver.resolveComponentTypeToClassName(logicalName); - add(className, dependency); + add(className, dependency, DependencyType.USAGE); processClassName.accept(dependency); } for (String mixin : TapestryInternalUtils.splitAtCommas(componentToken.getMixins())) { dependency = resolver.resolveMixinTypeToClassName(mixin); - add(className, dependency); + add(className, dependency, DependencyType.USAGE); processClassName.accept(dependency); } } @@ -305,7 +299,7 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis { dependency = resolver.resolveComponentTypeToClassName(typeFromAnnotation); } - add(field.getDeclaringClass().getName(), dependency); + add(field.getDeclaringClass().getName(), dependency, DependencyType.USAGE); processClassName.accept(dependency); } } @@ -319,7 +313,7 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis getFieldTypeClassName(field) : resolver.resolveMixinTypeToClassName(mixinType); - add(getDeclaringClassName(field), mixinClassName); + add(getDeclaringClassName(field), mixinClassName, DependencyType.USAGE); processClassName.accept(mixinClassName); } } @@ -343,7 +337,7 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis { for (Class dependency : mixinClasses.value()) { - add(field.getDeclaringClass(), dependency); + add(field.getDeclaringClass(), dependency, DependencyType.USAGE); processClass.accept(dependency); } } @@ -356,7 +350,7 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis // Logic adapted from MixinsWorker Orderable<String> typeAndOrder = TapestryInternalUtils.mixinTypeAndOrder(mixin); final String dependency = resolver.resolveMixinTypeToClassName(typeAndOrder.getTarget()); - add(getDeclaringClassName(field), dependency); + add(getDeclaringClassName(field), dependency, DependencyType.USAGE); processClassName.accept(dependency); } } @@ -379,7 +373,7 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis for (String id : componentPageElement.getEmbeddedElementIds()) { final ComponentPageElement child = componentPageElement.getEmbeddedElement(id); - add(componentPageElement, child); + add(componentPageElement, child, DependencyType.USAGE); register(child); } @@ -388,7 +382,7 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis final ComponentModel componentModel = componentResources.getComponentModel(); for (String mixinClassName : componentModel.getMixinClassNames()) { - add(componentClassName, mixinClassName); + add(componentClassName, mixinClassName, DependencyType.USAGE); } // Mixins applied to embedded component instances @@ -401,7 +395,7 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis final List<String> mixinClassNames = embeddedComponentModel .getMixinClassNames(); for (String mixinClassName : mixinClassNames) { - add(componentClassName, mixinClassName); + add(componentClassName, mixinClassName, DependencyType.USAGE); } } @@ -410,7 +404,7 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis Class<?> parent = component.getClass().getSuperclass(); if (parent != null && !Object.class.equals(parent)) { - add(componentClassName, parent.getName()); + add(componentClassName, parent.getName(), DependencyType.SUPERCLASS); } // Dependencies from injecting annotations: @@ -420,7 +414,8 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis { for (String dependency : metaDependencies.split(META_ATTRIBUTE_SEPARATOR)) { - add(componentClassName, dependency); + add(componentClassName, dependency, + isPage(dependency) ? DependencyType.INJECT_PAGE : DependencyType.USAGE); } } @@ -468,24 +463,19 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis { alreadyProcessed.remove(className); map.remove(className); - final Collection<Set<String>> allDependentSets = map.values(); - for (Set<String> dependents : allDependentSets) + final Collection<Set<Dependency>> allDependentSets = map.values(); + for (Set<Dependency> dependents : allDependentSets) { if (dependents != null) { - dependents.remove(className); - } - } - } - synchronized (invalidationOnlyDependencies) - { - final Iterator<InvalidationOnlyDependency> iterator = invalidationOnlyDependencies.iterator(); - while (iterator.hasNext()) - { - InvalidationOnlyDependency dependency = iterator.next(); - if (dependency.className.equals(className) || dependency.dependency.equals(className)) - { - iterator.remove(); + final Iterator<Dependency> iterator = dependents.iterator(); + while (iterator.hasNext()) + { + if (className.equals(iterator.next().className)) + { + iterator.remove(); + } + } } } } @@ -506,44 +496,49 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis @Override public Set<String> getDependents(String className) { - final Set<String> dependents = map.get(className); - return dependents != null ? dependents : Collections.emptySet(); + final Set<Dependency> dependents = map.get(className); + return dependents != null + ? dependents.stream().map(d -> d.className).collect(Collectors.toSet()) + : Collections.emptySet(); } - private Set<String> getDependencies(String className, Predicate<String> filter) + @Override + public Set<String> getDependencies(String className, DependencyType type) { Set<String> dependencies = Collections.emptySet(); if (alreadyProcessed.contains(className)) { dependencies = map.entrySet().stream() - .filter(e -> e.getValue().contains(className)) + .filter(e -> contains(e.getValue(), className, type)) .map(e -> e.getKey()) - .filter(filter) .collect(Collectors.toSet()); } return dependencies; } - @Override - public Set<String> getDependencies(String className) - { - return getDependencies(className, this::isNotPage); - } - - @Override - public Set<String> getPageDependencies(String className) + + private boolean contains(Set<Dependency> dependencies, String className, DependencyType type) { - return getDependencies(className, this::isPage); + boolean contains = false; + for (Dependency dependency : dependencies) + { + if (dependency.type.equals(type) && dependency.className.equals(className)) + { + contains = true; + break; + } + } + return contains; } - private void add(ComponentPageElement component, ComponentPageElement dependency) + private void add(ComponentPageElement component, ComponentPageElement dependency, DependencyType type) { - add(getClassName(component), getClassName(dependency)); + add(getClassName(component), getClassName(dependency), type); } // Just for unit tests - void add(String component, String dependency, boolean markAsAlreadyProcessed) + void add(String component, String dependency, DependencyType type, boolean markAsAlreadyProcessed) { if (markAsAlreadyProcessed) { @@ -551,31 +546,32 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis } if (dependency != null) { - add(component, dependency); + add(component, dependency, type); } } - private void add(Class<?> component, Class<?> dependency) + private void add(Class<?> component, Class<?> dependency, DependencyType type) { if (plasticManager.shouldInterceptClassLoading(dependency.getName())) { - add(component.getName(), dependency.getName()); + add(component.getName(), dependency.getName(), type); } } - private void add(String component, String dependency) + private void add(String component, String dependency, DependencyType type) { Objects.requireNonNull(component, "Parameter component cannot be null"); Objects.requireNonNull(dependency, "Parameter dependency cannot be null"); + Objects.requireNonNull(dependency, "Parameter type cannot be null"); synchronized (map) { - Set<String> dependents = map.get(dependency); + Set<Dependency> dependents = map.get(dependency); if (dependents == null) { dependents = new HashSet<>(); map.put(dependency, dependents); } - dependents.add(component); + dependents.add(new Dependency(component, type)); } } @@ -589,7 +585,7 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis List<String> listen(List<String> resources) { List<String> furtherDependents; - if (!INVALIDATIONS_ENABLED.get()) + if (INVALIDATIONS_DISABLED.get() > 0) { furtherDependents = Collections.emptyList(); } @@ -616,14 +612,6 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis } } - for (InvalidationOnlyDependency invalidationOnlyDependency : invalidationOnlyDependencies) - { - if (invalidationOnlyDependency.dependency.equals(resource)) - { - furtherDependents.add(invalidationOnlyDependency.dependency); - } - } - clear(resource); } } @@ -645,14 +633,15 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis JSONObject jsonObject = new JSONObject(); for (String className : map.keySet()) { - final Set<String> dependencies = getDependencies(className); - jsonObject.put(className, JSONArray.from(dependencies)); + // TODO: rewrite this +// final Set<String> dependencies = getDependencies(className); +// jsonObject.put(className, JSONArray.from(dependencies)); } bufferedWriter.write(jsonObject.toString()); } catch (IOException e) { - throw new TapestryException("Exception trying to read " + ComponentDependencyRegistry.FILENAME, e); + throw new TapestryException("Exception trying to read " + FILENAME, e); } } } @@ -672,7 +661,9 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis @Override public Set<String> getRootClasses() { return alreadyProcessed.stream() - .filter(c -> getDependencies(c).isEmpty()) + .filter(c -> getDependencies(c, DependencyType.USAGE).isEmpty() && + getDependencies(c, DependencyType.INJECT_PAGE).isEmpty() && + getDependencies(c, DependencyType.SUPERCLASS).isEmpty()) .collect(Collectors.toSet()); } @@ -690,13 +681,13 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis @Override public void disableInvalidations() { - INVALIDATIONS_ENABLED.set(false); + INVALIDATIONS_DISABLED.set(INVALIDATIONS_DISABLED.get() + 1); } @Override public void enableInvalidations() { - INVALIDATIONS_ENABLED.set(true); + INVALIDATIONS_DISABLED.set(INVALIDATIONS_DISABLED.get() - 1); } /** @@ -896,4 +887,44 @@ public class ComponentDependencyRegistryImpl implements ComponentDependencyRegis } + private static final class Dependency + { + private final String className; + private final DependencyType type; + + public Dependency(String className, DependencyType dependencyType) + { + super(); + this.className = className; + this.type = dependencyType; + } + + @Override + public int hashCode() { + return Objects.hash(className, type); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (!(obj instanceof Dependency)) + { + return false; + } + Dependency other = (Dependency) obj; + return Objects.equals(className, other.className) && type == other.type; + } + + @Override + public String toString() + { + return "Dependency [className=" + className + ", dependencyType=" + type + "]"; + } + + } + } 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 351b60200..bfafdeeb1 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 @@ -37,6 +37,7 @@ import org.apache.tapestry5.internal.InternalComponentResources; import org.apache.tapestry5.internal.InternalConstants; import org.apache.tapestry5.internal.model.MutableComponentModelImpl; import org.apache.tapestry5.internal.plastic.PlasticInternalUtils; +import org.apache.tapestry5.internal.services.ComponentDependencyRegistry.DependencyType; import org.apache.tapestry5.ioc.Invokable; import org.apache.tapestry5.ioc.LoggerSource; import org.apache.tapestry5.ioc.OperationTracker; @@ -345,8 +346,9 @@ public final class ComponentInstantiatorSourceImpl implements ComponentInstantia // Make sure the dependencies have been processed in case // there was some invalidation going on and they're not there. - - final Set<String> dependencies = componentDependencyRegistry.getDependencies(className); + + // TODO: maybe we need superclasses here too? + final Set<String> dependencies = componentDependencyRegistry.getDependencies(className, DependencyType.USAGE); for (String dependency : dependencies) { if (!OPEN_INSTANTIATORS.get().contains(dependency)) 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 e95056278..5d9dd16e5 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 @@ -15,6 +15,7 @@ package org.apache.tapestry5.internal.services; import java.lang.ref.SoftReference; +import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -27,6 +28,7 @@ import org.apache.tapestry5.commons.services.InvalidationEventHub; import org.apache.tapestry5.commons.util.CollectionFactory; import org.apache.tapestry5.func.F; import org.apache.tapestry5.func.Mapper; +import org.apache.tapestry5.internal.services.ComponentDependencyRegistry.DependencyType; import org.apache.tapestry5.internal.services.assets.ResourceChangeTracker; import org.apache.tapestry5.internal.structure.ComponentPageElement; import org.apache.tapestry5.internal.structure.Page; @@ -128,6 +130,11 @@ public class PageSourceImpl implements PageSource // The while loop looks superfluous, but it helps to ensure that the Page instance, // with all of its mutable construction-time state, is properly published to other // threads (at least, as I understand Brian Goetz's explanation, it should be). + + if (!productionMode && invalidateUnknownContext) + { + componentDependencyRegistry.disableInvalidations(); + } while (true) { @@ -137,6 +144,10 @@ public class PageSourceImpl implements PageSource if (page != null) { + if (invalidateUnknownContext && !productionMode) + { + componentDependencyRegistry.enableInvalidations(); + } return page; } @@ -150,9 +161,7 @@ public class PageSourceImpl implements PageSource // { // getPage(componentClassResolver.resolvePageClassNameToPageName(superclassName), false); // } - final Set<String> pageDependencies = componentDependencyRegistry.getPageDependencies(className); - - preprocessPageClassLoaderContexts(className, pageDependencies); + final List<String> pageDependencies = preprocessPageDependencies(className); for (String pageClassName : pageDependencies) { @@ -169,7 +178,6 @@ public class PageSourceImpl implements PageSource pageCache.put(key, ref); - // TODO: remove this? if (!productionMode) { final ComponentPageElement rootElement = page.getRootElement(); @@ -178,18 +186,39 @@ public class PageSourceImpl implements PageSource PageClassloaderContext context = pageClassloaderContextManager.get(className); if (invalidateUnknownContext && context.isUnknown()) { - componentDependencyRegistry.disableInvalidations(); pageClassloaderContextManager.invalidateAndFireInvalidationEvents(context); - componentDependencyRegistry.enableInvalidations(); pageClassloaderContextManager.get(className); + preprocessPageDependencies(className); return getPage(canonicalPageName, false); } } } + + + } + + private List<String> preprocessPageDependencies(final String className) { + final List<String> pageDependencies = new ArrayList<>(); + pageDependencies.addAll( + new ArrayList<String>(componentDependencyRegistry.getDependencies(className, DependencyType.INJECT_PAGE))); + pageDependencies.addAll( + new ArrayList<String>(componentDependencyRegistry.getDependencies(className, DependencyType.SUPERCLASS))); + + final Iterator<String> iterator = pageDependencies.iterator(); + while (iterator.hasNext()) + { + if (!iterator.next().contains(".pages.")) + { + iterator.remove(); + } + } + + preprocessPageClassLoaderContexts(className, pageDependencies); + return pageDependencies; } - private void preprocessPageClassLoaderContexts(String className, final Set<String> pageDependencies) { + private void preprocessPageClassLoaderContexts(String className, final List<String> pageDependencies) { for (int i = 0; i < 2; i++) { pageClassloaderContextManager.get(className); 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 ba8c3e9f6..553e668de 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 @@ -28,6 +28,7 @@ import org.apache.tapestry5.commons.internal.util.TapestryException; import org.apache.tapestry5.commons.services.InvalidationEventHub; import org.apache.tapestry5.commons.services.PlasticProxyFactory; import org.apache.tapestry5.internal.services.ComponentDependencyRegistry; +import org.apache.tapestry5.internal.services.ComponentDependencyRegistry.DependencyType; import org.apache.tapestry5.internal.services.InternalComponentInvalidationEventHub; import org.apache.tapestry5.ioc.annotations.ComponentClasses; import org.apache.tapestry5.plastic.PlasticUtils; @@ -238,7 +239,7 @@ public class PageClassloaderContextManagerImpl implements PageClassloaderContext { alreadyProcessed.add(className); - + // Sorting dependencies alphabetically so we have consistent results. List<String> dependencies = new ArrayList<>(getDependenciesWithoutPages(className)); Collections.sort(dependencies); @@ -354,11 +355,9 @@ public class PageClassloaderContextManagerImpl implements PageClassloaderContext return context; } - private Set<String> getDependenciesWithoutPages(String className) { - final Set<String> dependencies = componentDependencyRegistry.getDependencies(className); - return dependencies.stream() - .filter(c -> !componentClassResolver.isPage(c)) - .collect(Collectors.toSet()); + private Set<String> getDependenciesWithoutPages(String className) + { + return componentDependencyRegistry.getDependencies(className, DependencyType.USAGE); } private Class<?> loadClass(String className, PageClassloaderContext context) diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistryImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistryImplTest.java index c3dd3ff7a..aea62e595 100644 --- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistryImplTest.java +++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentDependencyRegistryImplTest.java @@ -75,6 +75,7 @@ import org.apache.tapestry5.integration.app1.pages.BlockHolder; import org.apache.tapestry5.integration.app1.pages.EmbeddedComponentTypeConflict; import org.apache.tapestry5.integration.app1.pages.InstanceMixinDependencies; import org.apache.tapestry5.integration.app1.pages.MixinParameterDefault; +import org.apache.tapestry5.internal.services.ComponentDependencyRegistry.DependencyType; import org.apache.tapestry5.internal.services.templates.DefaultTemplateLocator; import org.apache.tapestry5.ioc.internal.QuietOperationTracker; import org.apache.tapestry5.modules.TapestryModule; @@ -214,38 +215,47 @@ public class ComponentDependencyRegistryImplTest result = componentDependencyRegistry.listen(resources); Collections.sort(result); assertEquals(result, Arrays.asList("d", "dd")); - assertEquals("bar", componentDependencyRegistry.getDependencies("foo").iterator().next()); + assertEquals("bar", getDependencies("foo").iterator().next()); assertEquals("foo", componentDependencyRegistry.getDependents("bar").iterator().next()); configurePCCM(false); result = componentDependencyRegistry.listen(Collections.emptyList()); assertEquals(result, Collections.emptyList()); assertEquals(componentDependencyRegistry.getDependents("bar").size(), 0); - assertEquals(componentDependencyRegistry.getDependencies("foo").size(), 0); + assertEquals(getDependencies("foo").size(), 0); } + private Set<String> getDependencies(String className) + { + return componentDependencyRegistry.getDependencies(className, DependencyType.USAGE); + } + @Test public void dependency_methods() { final String foo = "foo"; final String bar = "bar"; - final String something = "d"; - final String other = "dd"; - final String fulano = "a"; - final String beltrano = "aa"; + final String something = "something"; + final String other = "other"; + final String fulano = "fulano"; + final String beltrano = "beltrano"; + final String sicrano = "sicrano"; assertEquals( "getDependents() should never return null", Collections.emptySet(), - componentDependencyRegistry.getDependencies(foo)); + getDependencies(foo)); assertEquals( "getDependents() should never return null", Collections.emptySet(), componentDependencyRegistry.getDependents(foo)); + // In Brazil, Fulano, Beltrano and Sicrano are the most used people + // placeholder names, in that order. + add(foo, bar); add(something, fulano); add(other, beltrano); @@ -254,18 +264,24 @@ public class ComponentDependencyRegistryImplTest add(bar, null); add(fulano, null); add(beltrano, null); + add(beltrano, sicrano); + add(sicrano, null); - assertEquals(new HashSet<>(Arrays.asList(fulano, beltrano)), componentDependencyRegistry.getDependencies(other)); - assertEquals(new HashSet<>(Arrays.asList(fulano)), componentDependencyRegistry.getDependencies(something)); - assertEquals(new HashSet<>(Arrays.asList()), componentDependencyRegistry.getDependencies(fulano)); - assertEquals(new HashSet<>(Arrays.asList(bar)), componentDependencyRegistry.getDependencies(foo)); - assertEquals(new HashSet<>(Arrays.asList()), componentDependencyRegistry.getDependencies(bar)); + assertEquals(new HashSet<>(Arrays.asList(fulano, beltrano)), getDependencies(other)); + assertEquals(new HashSet<>(Arrays.asList(fulano)), getDependencies(something)); + assertEquals(new HashSet<>(Arrays.asList()), getDependencies(fulano)); + assertEquals(new HashSet<>(Arrays.asList(bar)), getDependencies(foo)); + assertEquals(new HashSet<>(Arrays.asList()), getDependencies(bar)); + assertEquals(new HashSet<>(Arrays.asList(sicrano)), getDependencies(beltrano)); assertEquals(new HashSet<>(Arrays.asList(foo)), componentDependencyRegistry.getDependents(bar)); assertEquals(new HashSet<>(Arrays.asList(other, something)), componentDependencyRegistry.getDependents(fulano)); assertEquals(new HashSet<>(Arrays.asList()), componentDependencyRegistry.getDependents(foo)); + assertEquals(new HashSet<>(Arrays.asList(other)), componentDependencyRegistry.getDependents(beltrano)); + assertEquals(new HashSet<>(Arrays.asList(beltrano)), componentDependencyRegistry.getDependents(sicrano)); - assertEquals(new HashSet<>(Arrays.asList(bar, fulano, beltrano)), componentDependencyRegistry.getRootClasses()); + assertEquals(new HashSet<>(Arrays.asList(bar, fulano, sicrano)), + componentDependencyRegistry.getRootClasses()); assertTrue(componentDependencyRegistry.contains(foo)); assertTrue(componentDependencyRegistry.contains(bar)); @@ -273,6 +289,7 @@ public class ComponentDependencyRegistryImplTest assertTrue(componentDependencyRegistry.contains(something)); assertTrue(componentDependencyRegistry.contains(fulano)); assertTrue(componentDependencyRegistry.contains(beltrano)); + assertTrue(componentDependencyRegistry.contains(sicrano)); assertFalse(componentDependencyRegistry.contains("blah")); assertTrue(componentDependencyRegistry.getClassNames().contains(foo)); @@ -281,11 +298,49 @@ public class ComponentDependencyRegistryImplTest assertTrue(componentDependencyRegistry.getClassNames().contains(something)); assertTrue(componentDependencyRegistry.getClassNames().contains(fulano)); assertTrue(componentDependencyRegistry.getClassNames().contains(beltrano)); + assertTrue(componentDependencyRegistry.getClassNames().contains(sicrano)); + assertFalse(componentDependencyRegistry.getClassNames().contains("blah")); + + // Testing the clear method + componentDependencyRegistry.clear(beltrano); + + assertEquals(new HashSet<>(Arrays.asList(fulano)), getDependencies(other)); + assertEquals(new HashSet<>(Arrays.asList(fulano)), getDependencies(something)); + assertEquals(new HashSet<>(Arrays.asList()), getDependencies(fulano)); + assertEquals(new HashSet<>(Arrays.asList(bar)), getDependencies(foo)); + assertEquals(new HashSet<>(Arrays.asList()), getDependencies(bar)); + assertEquals(new HashSet<>(Arrays.asList()), getDependencies(sicrano)); + + assertEquals(new HashSet<>(Arrays.asList(foo)), componentDependencyRegistry.getDependents(bar)); + assertEquals(new HashSet<>(Arrays.asList(other, something)), componentDependencyRegistry.getDependents(fulano)); + assertEquals(new HashSet<>(Arrays.asList()), componentDependencyRegistry.getDependents(foo)); + assertEquals(new HashSet<>(Arrays.asList()), componentDependencyRegistry.getDependents(beltrano)); + assertEquals(new HashSet<>(Arrays.asList()), componentDependencyRegistry.getDependents(sicrano)); + + assertEquals(new HashSet<>(Arrays.asList(bar, fulano, sicrano)), componentDependencyRegistry.getRootClasses()); + + assertTrue(componentDependencyRegistry.contains(foo)); + assertTrue(componentDependencyRegistry.contains(bar)); + assertTrue(componentDependencyRegistry.contains(other)); + assertTrue(componentDependencyRegistry.contains(something)); + assertTrue(componentDependencyRegistry.contains(fulano)); + assertTrue(componentDependencyRegistry.contains(sicrano)); + assertFalse(componentDependencyRegistry.contains(beltrano)); + assertFalse(componentDependencyRegistry.contains("blah")); + + assertTrue(componentDependencyRegistry.getClassNames().contains(foo)); + assertTrue(componentDependencyRegistry.getClassNames().contains(bar)); + assertTrue(componentDependencyRegistry.getClassNames().contains(other)); + assertTrue(componentDependencyRegistry.getClassNames().contains(something)); + assertTrue(componentDependencyRegistry.getClassNames().contains(fulano)); + assertTrue(componentDependencyRegistry.getClassNames().contains(sicrano)); + assertFalse(componentDependencyRegistry.getClassNames().contains(beltrano)); assertFalse(componentDependencyRegistry.getClassNames().contains("blah")); } - @Test + // Tested code isn't being used at the moment + @Test(enabled = false) public void register() { @@ -347,7 +402,7 @@ public class ComponentDependencyRegistryImplTest private void assertDependencies(Class clasz, Class... dependencies) { assertEquals( setOf(dependencies), - componentDependencyRegistry.getDependencies(clasz.getName())); + getDependencies(clasz.getName())); } private static Set<String> setOf(Class ... classes) @@ -364,7 +419,7 @@ public class ComponentDependencyRegistryImplTest private void add(String component, String dependency) { - componentDependencyRegistry.add(component, dependency, true); + componentDependencyRegistry.add(component, dependency, DependencyType.USAGE, true); } private static final class MockMappedConfiguration<String, URL> implements MappedConfiguration<String, URL>