Skip to content

Commit

Permalink
TAP5-2742: smarter page cache invalidation
Browse files Browse the repository at this point in the history
TAP5-2742: Initial work on smarter page cache invalidation

TAP5-2742: more tests and code improvements

TAP5-2742: page cache invalidation on template change working

plus some additional logging for when invalidations are done and page
instances created

TAP5-2742: refactoring URLChangeTracker to make it parameterized

TAP5-2742: work in progress for page invalidation on message file change

TAP5-2742: smart page cache invalidation for message (i18n) files

TAP5-2742: fixing ComponentMessagesSourceImplTest

TAP5-2742: JavaDoc fixes

TAP5-2744: upgrading Selenium and fixing integration tests

TAP5-2744: upgrading Selenium

TAP5-2744: upgrading Selenium again

TAP5-2744: explicitly choosing Firefox for Selenium

TAP5-2744: fixing tapestry-webresources build

TAP5-2742: fixing NPE when production mode is off

TAP5-2742: manual invalidation of cached pages and some error handling

TAP5-2742: smarter @import asset caching invalidation

TAP5-2742: fixing changes in PageLoaderImpl, plus other changes

in ComponentClassCache and PropertyConduitSourceImpl

TAP5-2742: handling @InjectComponent, @InjectPage and @component

as proper dependencies between components

TAP5-2742: fixing JavaDoc and NPE in ResourceChangeTrackerImpl

TAP5-2742: fixing NPE in ResourceChangeTrackerImpl

Trying to fix failing tests where Selenium doesn't find a link

which is actually there.

Attempt to get Firefox tests run on Linux with and without Snap

TAP5-2742: fixing CoreBehaviorsTest.reload_from_nested_page

Adding a bit of test code for our tests

Adding a bit of test code for our tests (again)

TAP5-2744: bug fix in ComponentClassCacheImpl

TAP5-2744: better exception message parsing in RequestErrorFilter

TAP5-2744: last commit before introducing a classloader tree

TAP5-2744: first pass at page dependency graph

TAP5-2744: eliminating a infinite recursion

TAP5-2744: finished Graphviz component and PageDependencyGraph page

TAP5-2744: storing component dependency information

TAP5-2742: first pass at multiple classloader support

for live class reloading

Partial work commit for better page invalidation part 2

TAP-2742: live class reloading works, production mode doesn'r

TAP-2742: production mode fixed, PageCatalog too

TAP-2742: fixing JavaDoc errors

Trying to fix build failure

Temporarily removing tapestry-breanvalidator from the build

Fixing syntax error on settings.gradle

TAP-2742: fixing PageSourceImpl

Reinstating tapestry-beanvalidator.

TAP-2742: fixing tests in ComponentDependencyRegistryImplTest

TAP-2742: code cleanup

Fixing syntax error on settings.gradle again (:facepalm:)

TAP-2742: fixing better class invalidation

TAP-2742: fixing some Tapestry-IoC tests

Trying to fix TapestryBeanValidationIntegrationTests.client_validation()

TAP5-2742: fixes service implementation live class reloading

Disabling WebDriver trace debugging level

Turning off PageClassLoaderContextManager log at debug level

TAP5-2742: fixes problems with page classes with subclasses

Temporary change to a tapestry-beanvalidation

TAP5-2742: forces pages to never rendered from the unknwon context

TAP5-2742: fixing NestedBeanEditor test

TAP5-2742: introducingtypes of dependencies

Fixing a couple of tests in FormsTests

Temporarily disabling the tapestry-beanvalidation build

More test code improving (or workaround-ing)

TAP5-2742: making method public so it works with smarter page
invalidation

TAP5-2742: fixing possible NPE when PCLC depends on the root context

TAP5-2742: handling page dependencies better (or at least trying)

TAP5-2742: specific stylig for superclass and inject page dependencies

TAP5-2742: showing all dependencies, not just the already loaded pages

TAP5-2742: fixing dependency file saving and loading

TAP5-2742: removing commented-out code

TAP5-2742: preprocessing page classloader context when possible

TAP5-2742: PageClassloaderContext -> PageClassLoaderContext

TAP5-2742: creating multiple classloader mode

TAP5-2742: reenabling resource -> class tracking

TAP5-2742: reenabling tapestry-beanvalidator

TAP5-2742: component dependency and page classloader context preloading
  • Loading branch information
thiagohp committed Jun 16, 2023
1 parent 0b1437f commit 48938d9
Show file tree
Hide file tree
Showing 84 changed files with 5,802 additions and 338 deletions.
25 changes: 25 additions & 0 deletions 583_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Scratch pad for changes destined for the 5.8.3 release notes page.

# New configuration symbol

* SymbolConstants.MULTIPLE_CLASSLOADERS: when set to true (default false), enables
multiple classloaders for smarter page cache invalidation.

# Added methods

* add(URL url, String memo) to URLChangeTracker
* getChangeResourcesMemos() to URLChangeTracker
* getValues() to MultiKey
* New getLogicalName() method in ComponentClassResolver.
* New getPlasticManager() method in PlasticProxyFactory
* New getPageNames() method in BeanBlockOverrideSource

# Non-backward-compatible changes (but that probably won't cause problems)

* New addInvalidationCallback(Function<List<String>, List<String>> callback) method in InvalidationEventHub
* New getEmbeddedElementIds() method in ComponentPageElement (internal service)
* New registerClassName() method in ResourceChangeTracker (internal service)
* New clearClassName() method in ResourceChangeTracker (internal service)

# Overall notes
* Before
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.CommonTokenStream;
Expand Down Expand Up @@ -1387,9 +1390,42 @@ public PropertyConduitSourceImpl(PropertyAccess access, @ComponentLayer
@PostInjection
public void listenForInvalidations(@ComponentClasses InvalidationEventHub hub)
{
hub.clearOnInvalidation(cache);
hub.addInvalidationCallback(this::listen);
}

private List<String> listen(List<String> resources)
{

if (resources.isEmpty())
{
cache.clear();
}
else
{

final Iterator<Entry<MultiKey, PropertyConduit>> iterator = cache.entrySet().iterator();

while (iterator.hasNext())
{

final Entry<MultiKey, PropertyConduit> entry = iterator.next();

for (String resource : resources) {
@SuppressWarnings("rawtypes")
final Class clasz = (Class) entry.getKey().getValues()[0];
if (clasz.getName().equals(resource))
{
iterator.remove();
}
}

}

}

return Collections.emptyList();

}


public PropertyConduit create(Class rootClass, String expression)
{
Expand Down Expand Up @@ -1488,7 +1524,7 @@ private PropertyConduit build(final Class rootClass, String expression)
break;
}

return proxyFactory.createProxy(InternalPropertyConduit.class,
return proxyFactory.getProxyFactory(rootClass.getName()).createProxy(InternalPropertyConduit.class,
new PropertyConduitBuilder(rootClass, expression, tree)).newInstance();
} catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,4 +297,10 @@ public void removePlasticClassListener(PlasticClassListener listener)
manager.removePlasticClassListener(listener);
}

@Override
public PlasticManager getPlasticManager()
{
return manager;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@

package org.apache.tapestry5.commons.services;

import java.util.List;
import java.util.Map;
import java.util.function.Function;

import org.apache.tapestry5.ioc.annotations.IncompatibleChange;

/**
* An object which manages a list of {@link org.apache.tapestry5.commons.services.InvalidationListener}s. There are multiple
Expand Down Expand Up @@ -55,4 +59,24 @@ public interface InvalidationEventHub
* @since 5.4
*/
void clearOnInvalidation(Map<?,?> map);

/**
* Adds a callback, as a function that receives a list of strings and also returns a list of strings,
* that is invoked when one or more listed underlying tracked resource have changed.
* An empty list should be considered as all resources being changed and any caches needing to be cleared.
* The return value of the function should be a non-null, but possibly empty, list of other resources that also
* need to be invalidated in a recursive fashion.
* This method does nothing in production mode.
* @since 5.8.3
*/
@IncompatibleChange(release = "5.8.3", details = "Added method")
void addInvalidationCallback(Function<List<String>, List<String>> function);

/**
* Notify resource-specific invalidations to listeners.
* @since 5.8.3
*/
@IncompatibleChange(release = "5.8.3", details = "Added method")
void fireInvalidationEvent(List<String> resources);

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2011, 2012 The Apache Software Foundation
// Copyright 2011, 2012, 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.
Expand All @@ -24,6 +24,7 @@
import org.apache.tapestry5.plastic.PlasticClassListenerHub;
import org.apache.tapestry5.plastic.PlasticClassTransformation;
import org.apache.tapestry5.plastic.PlasticClassTransformer;
import org.apache.tapestry5.plastic.PlasticManager;

/**
* A service used to create proxies of varying types. As a secondary concern, manages to identify the
Expand Down Expand Up @@ -169,6 +170,24 @@ public interface PlasticProxyFactory extends PlasticClassListenerHub
*
* @since 5.3.3
*/
@IncompatibleChange(release = "5.3.3", details = "Added method")
void clearCache();

/**
* Returns the {@linkplain PlasticManager} instance used by this PlasticProxyFactory.
* @since 5.8.3
*/
@IncompatibleChange(release = "5.8.3", details = "Added method")
PlasticManager getPlasticManager();

/**
* Returns the {@linkplain PlasticProxyFactory} instance to be used for a given class.
* Default implementation returns <code>this</code>.
* @since 5.8.3
*/
default PlasticProxyFactory getProxyFactory(String className)
{
return this;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2021 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.commons.util;

import org.apache.tapestry5.commons.internal.util.TapestryException;

/**
* Exception used when trying to assemble a page but different versions of the same class are found.
*
* @since 5.8.3
*/
public class DifferentClassVersionsException extends TapestryException
{

private static final long serialVersionUID = 1L;

private final String className;

private final ClassLoader classLoader1;

private final ClassLoader classLoader2;

public DifferentClassVersionsException(String message, String className, ClassLoader classLoader1, ClassLoader classLoader2)
{
super(message, null);
this.className = className;
this.classLoader1 = classLoader1;
this.classLoader2 = classLoader2;
}

public String getClassName()
{
return className;
}

public ClassLoader getClassLoader1()
{
return classLoader1;
}

public ClassLoader getClassLoader2()
{
return classLoader2;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,13 @@ public String toString()

return builder.toString();
}


/**
* Returns a copy of the values array.
* @since 5.8.3
*/
public Object[] getValues() {
return values.clone();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

package org.apache.tapestry5.internal.plastic;

import java.util.function.Function;
import java.util.function.Predicate;

import org.apache.tapestry5.plastic.PlasticUtils;

public class PlasticClassLoader extends ClassLoader
{
static
Expand All @@ -23,11 +28,16 @@ public class PlasticClassLoader extends ClassLoader
}

private final ClassLoaderDelegate delegate;

public PlasticClassLoader(ClassLoader parent, ClassLoaderDelegate delegate)

private Predicate<String> filter;

private Function<String, Class<?>> alternativeClassloading;

private String tag;

public PlasticClassLoader(ClassLoader parent, ClassLoaderDelegate delegate)
{
super(parent);

this.delegate = delegate;
}

Expand All @@ -41,10 +51,23 @@ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundE
if (loadedClass != null)
return loadedClass;

if (delegate.shouldInterceptClassLoading(name))
if (shouldInterceptClassLoading(name))
{
Class<?> c = delegate.loadAndTransformClass(name);

Class<?> c = null;
if ((filter != null && filter.test(name)) || (filter == null && delegate.shouldInterceptClassLoading(name)))
{
c = delegate.loadAndTransformClass(name);
}
else if (alternativeClassloading != null)
{
c = alternativeClassloading.apply(name);
}

if (c == null)
{
return super.loadClass(name, resolve);
}

if (resolve)
resolveClass(c);

Expand All @@ -56,11 +79,56 @@ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundE
}
}

private boolean shouldInterceptClassLoading(String name) {
return delegate.shouldInterceptClassLoading(
PlasticUtils.getEnclosingClassName(name));
}

public synchronized Class<?> defineClassWithBytecode(String className, byte[] bytecode)
{
synchronized(getClassLoadingLock(className))
{
return defineClass(className, bytecode, 0, bytecode.length);
}
}

/**
* When alternatingClassloader is set, this classloader delegates to it the
* call to {@linkplain ClassLoader#loadClass(String)}. If it returns a non-null object,
* it's returned by <code>loadClass(String)</code>. Otherwise, it returns
* <code>super.loadClass(name)</code>.
* @since 5.8.3
*/
public void setAlternativeClassloading(Function<String, Class<?>> alternateClassloading)
{
this.alternativeClassloading = alternateClassloading;
}

/**
* @since 5.8.3
*/
public void setTag(String tag)
{
this.tag = tag;
}

/**
* When a filter is set, only classes accepted by it will be loaded by this classloader.
* Instead, it will be delegated to alternate classloading first and the parent classloader
* in case the alternate doesn't handle it.
* @since 5.8.3
*/
public void setFilter(Predicate<String> filter)
{
this.filter = filter;
}

@Override
public String toString()
{
final String superToString = super.toString();
final String id = superToString.substring(superToString.indexOf('@')).trim();
return String.format("PlasticClassLoader[%s, tag=%s, parent=%s]", id, tag, getParent());
}

}
Loading

0 comments on commit 48938d9

Please sign in to comment.