This is an automated email from the ASF dual-hosted git repository. thiagohp pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tapestry-5.git
The following commit(s) were added to refs/heads/master by this push: new f58f36a99 TAP5-2770: trying to recover when a ClassCastException happens f58f36a99 is described below commit f58f36a99fb0600f91880a3e136ba4e98963da26 Author: Thiago H. de Paula Figueiredo <thi...@arsmachina.com.br> AuthorDate: Mon Mar 11 23:50:41 2024 -0300 TAP5-2770: trying to recover when a ClassCastException happens by clearing the generated classes caches and trying to handle the request again --- .../services/PropertyConduitSourceImpl.java | 2 +- .../ComponentRequestHandlerTerminator.java | 91 ++++++++++++++++++++-- .../internal/services/RequestPageCacheImpl.java | 8 +- 3 files changed, 92 insertions(+), 9 deletions(-) diff --git a/beanmodel/src/main/java/org/apache/tapestry5/beanmodel/internal/services/PropertyConduitSourceImpl.java b/beanmodel/src/main/java/org/apache/tapestry5/beanmodel/internal/services/PropertyConduitSourceImpl.java index 5ac0aac72..ad26631df 100644 --- a/beanmodel/src/main/java/org/apache/tapestry5/beanmodel/internal/services/PropertyConduitSourceImpl.java +++ b/beanmodel/src/main/java/org/apache/tapestry5/beanmodel/internal/services/PropertyConduitSourceImpl.java @@ -1,4 +1,4 @@ -// Copyright 2007-2013 The Apache Software Foundation +// Copyright 2007-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. diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentRequestHandlerTerminator.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentRequestHandlerTerminator.java index 0ab7b9ffd..91e276d18 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentRequestHandlerTerminator.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentRequestHandlerTerminator.java @@ -1,4 +1,4 @@ -// Copyright 2009, 2011 Apache Software Foundation +// Copyright 2009, 2024 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. @@ -15,14 +15,20 @@ package org.apache.tapestry5.internal.services; import java.io.IOException; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; -import org.apache.tapestry5.beanmodel.services.*; +import org.apache.tapestry5.commons.services.InvalidationEventHub; +import org.apache.tapestry5.ioc.annotations.ComponentClasses; import org.apache.tapestry5.services.ComponentEventRequestHandler; import org.apache.tapestry5.services.ComponentEventRequestParameters; import org.apache.tapestry5.services.ComponentRequestHandler; import org.apache.tapestry5.services.PageRenderRequestHandler; import org.apache.tapestry5.services.PageRenderRequestParameters; import org.apache.tapestry5.services.Traditional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Terminator for the {@link org.apache.tapestry5.services.ComponentRequestHandler} pipeline, that feeds out into the @@ -33,24 +39,99 @@ import org.apache.tapestry5.services.Traditional; */ public class ComponentRequestHandlerTerminator implements ComponentRequestHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ComponentRequestHandlerTerminator.class); + private final ComponentEventRequestHandler componentEventRequestHandler; private final PageRenderRequestHandler pageRenderRequestHandler; + + private final InvalidationEventHub invalidationEventHub; + + private final ComponentDependencyRegistry componentDependencyRegistry; public ComponentRequestHandlerTerminator(@Traditional ComponentEventRequestHandler componentEventRequestHandler, - PageRenderRequestHandler pageRenderRequestHandler) + PageRenderRequestHandler pageRenderRequestHandler, + final @ComponentClasses InvalidationEventHub invalidationEventHub, + final ComponentDependencyRegistry componentDependencyRegistry) { this.componentEventRequestHandler = componentEventRequestHandler; this.pageRenderRequestHandler = pageRenderRequestHandler; + this.invalidationEventHub = invalidationEventHub; + this.componentDependencyRegistry = componentDependencyRegistry; } public void handleComponentEvent(ComponentEventRequestParameters parameters) throws IOException { - componentEventRequestHandler.handle(parameters); + boolean retry = run(() -> componentEventRequestHandler.handle(parameters)); + if (retry) + { + componentEventRequestHandler.handle(parameters); + } } public void handlePageRender(PageRenderRequestParameters parameters) throws IOException { - pageRenderRequestHandler.handle(parameters); + boolean retry = run(() -> pageRenderRequestHandler.handle(parameters)); + if (retry) + { + pageRenderRequestHandler.handle(parameters); + } } + + private static final Pattern PATTERN = Pattern.compile( + "class (\\S+) cannot be cast to class (\\S+).*"); + + private static interface RunnableWithIOException + { + public void run() throws IOException; + } + + private boolean run(RunnableWithIOException runnable) throws IOException + { + boolean retry = false; + try { + runnable.run(); + } + catch (RuntimeException e) + { + Throwable throwable = e; + while (!(throwable instanceof ClassCastException) && throwable.getCause() != null) + { + throwable = throwable.getCause(); + } + if (throwable instanceof ClassCastException && throwable != null) + { + Matcher matcher = PATTERN.matcher(throwable.getMessage()); + if (matcher.matches() && matcher.groupCount() >= 2 && + isTransformed(matcher.group(1)) && + isTransformed(matcher.group(2))) + { + LOGGER.warn("Caught exception and trying to recover by invalidating generated classes caches: {}", + throwable.getMessage()); + componentDependencyRegistry.disableInvalidations(); + invalidationEventHub.fireInvalidationEvent(Collections.emptyList()); + componentDependencyRegistry.enableInvalidations(); + retry = true; + } + } + else + { + throw e; + } + } + return retry; + } + + /** + * Very simple, but fast, implementation. + */ + private boolean isTransformed(String className) + { + return className != null && ( + className.contains(".pages.") || + className.contains(".components.") || + className.contains(".base.")); + + } + } diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestPageCacheImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestPageCacheImpl.java index 99563acb0..b75d1521c 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestPageCacheImpl.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestPageCacheImpl.java @@ -1,4 +1,4 @@ -// Copyright 2010-2013 The Apache Software Foundation +// Copyright 2010-2024 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. @@ -40,8 +40,6 @@ import org.slf4j.Logger; */ @Scope(ScopeConstants.PERTHREAD) public class RequestPageCacheImpl implements RequestPageCache, Runnable - -/// This should have a listener too! { private final Logger logger; @@ -117,6 +115,10 @@ public class RequestPageCacheImpl implements RequestPageCache, Runnable private List<String> listen(List<String> resources) { + if (resources.isEmpty()) + { + cache.clear(); + } // TODO: we probably don't need this anymore for (String resource : resources) {