This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new 52bbd0e CAMEL-16819 (#5901) 52bbd0e is described below commit 52bbd0e83f1b723d0b4cda51a84152ca5fafed09 Author: Jose Montoya <ja...@users.noreply.github.com> AuthorDate: Sun Aug 1 08:09:14 2021 -0500 CAMEL-16819 (#5901) * exploratory support for configurable ExceptionPolicyStrategy * refactor to find strategy from context * add custom exception policy test --- .../DefaultExceptionPolicyStrategy.java | 10 +- .../errorhandler/ErrorHandlerSupport.java | 2 +- .../errorhandler/RedeliveryErrorHandler.java | 6 ++ .../CustomExceptionPolicyStrategyTest.java | 115 +++++++++++++++++++++ 4 files changed, 127 insertions(+), 6 deletions(-) diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/DefaultExceptionPolicyStrategy.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/DefaultExceptionPolicyStrategy.java index 9eb210b..45aab86 100644 --- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/DefaultExceptionPolicyStrategy.java +++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/DefaultExceptionPolicyStrategy.java @@ -51,7 +51,7 @@ import org.slf4j.LoggerFactory; * for more fine grained matching, so you can e.g. define multiple sets of exception types with the same exception * class(es) but have a predicate attached to select which to select at runtime. */ -public final class DefaultExceptionPolicyStrategy implements ExceptionPolicyStrategy { +public class DefaultExceptionPolicyStrategy implements ExceptionPolicyStrategy { // thread safe so we can use a shared instance public static final DefaultExceptionPolicyStrategy INSTANCE = new DefaultExceptionPolicyStrategy(); @@ -114,7 +114,7 @@ public final class DefaultExceptionPolicyStrategy implements ExceptionPolicyStra } } - private static boolean findMatchedExceptionPolicy( + private boolean findMatchedExceptionPolicy( Iterable<ExceptionPolicyKey> exceptionPolicies, Exchange exchange, Throwable exception, Map<Integer, ExceptionPolicyKey> candidates) { @@ -204,7 +204,7 @@ public final class DefaultExceptionPolicyStrategy implements ExceptionPolicyStra * @param exception the thrown exception * @return <tt>true</tt> if the to current exception class is a candidate, <tt>false</tt> to skip it. */ - protected static boolean filter(ExceptionPolicyKey type, Class<?> exceptionClass, Throwable exception) { + protected boolean filter(ExceptionPolicyKey type, Class<?> exceptionClass, Throwable exception) { // must be instance of check to ensure that the exceptionClass is one type of the thrown exception return exceptionClass.isInstance(exception); } @@ -222,7 +222,7 @@ public final class DefaultExceptionPolicyStrategy implements ExceptionPolicyStra * @param exchange the current {@link Exchange} * @return <tt>true</tt> if matched, <tt>false</tt> otherwise. */ - protected static boolean matchesWhen(ExceptionPolicyKey definition, Exchange exchange) { + protected boolean matchesWhen(ExceptionPolicyKey definition, Exchange exchange) { if (definition.getWhen() == null) { // if no predicate then it's always a match return true; @@ -239,7 +239,7 @@ public final class DefaultExceptionPolicyStrategy implements ExceptionPolicyStra * @param exception the exception * @return the list to iterate */ - protected static Iterable<Throwable> createExceptionIterable(Throwable exception) { + protected Iterable<Throwable> createExceptionIterable(Throwable exception) { return ObjectHelper.createExceptionIterable(exception); } diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/ErrorHandlerSupport.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/ErrorHandlerSupport.java index acec522..460bc75 100644 --- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/ErrorHandlerSupport.java +++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/ErrorHandlerSupport.java @@ -39,7 +39,7 @@ public abstract class ErrorHandlerSupport extends ChildServiceSupport implements DEFAULT_EXCHANGE_FORMATTER.setStyle(DefaultExchangeFormatter.OutputStyle.Fixed); } // optimize to use a shared instance - protected final ExceptionPolicyStrategy exceptionPolicy = DefaultExceptionPolicyStrategy.INSTANCE; + protected ExceptionPolicyStrategy exceptionPolicy = DefaultExceptionPolicyStrategy.INSTANCE; protected Map<ExceptionPolicyKey, ExceptionPolicy> exceptionPolicies; public void addErrorHandler(Processor errorHandler) { diff --git a/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/RedeliveryErrorHandler.java b/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/RedeliveryErrorHandler.java index 7de06ee..eddc219 100644 --- a/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/RedeliveryErrorHandler.java +++ b/core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/RedeliveryErrorHandler.java @@ -18,6 +18,7 @@ package org.apache.camel.processor.errorhandler; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; @@ -160,6 +161,11 @@ public abstract class RedeliveryErrorHandler extends ErrorHandlerSupport throw RuntimeCamelException.wrapRuntimeCamelException(e); } } + + Set<ExceptionPolicyStrategy> policies = this.camelContext.getRegistry().findByType(ExceptionPolicyStrategy.class); + if (policies.size() == 1) { + exceptionPolicy = policies.iterator().next(); + } } RedeliveryErrorHandler(Logger log) { diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/exceptionpolicy/CustomExceptionPolicyStrategyTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/exceptionpolicy/CustomExceptionPolicyStrategyTest.java new file mode 100644 index 0000000..378cb59 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/processor/exceptionpolicy/CustomExceptionPolicyStrategyTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.camel.processor.exceptionpolicy; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.processor.errorhandler.DefaultExceptionPolicyStrategy; +import org.apache.camel.processor.errorhandler.ExceptionPolicyStrategy; +import org.apache.camel.spi.Registry; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.fail; + +/** + * Unit test for a custom ExceptionPolicy + */ +public class CustomExceptionPolicyStrategyTest extends ContextTestSupport { + + private static final String ERROR_QUEUE = "mock:error"; + private static final String ERROR_USER_QUEUE = "mock:usererror"; + + public static class MyUserException extends IllegalStateException { + private static final long serialVersionUID = 1L; + + public MyUserException(String message) { + super(message); + } + + protected MyUserException(String message, Throwable cause) { + super(message, cause); + } + } + + @Override + protected Registry createRegistry() throws Exception { + Registry answer = super.createRegistry(); + answer.bind("reverse-strategy", ExceptionPolicyStrategy.class, new DefaultExceptionPolicyStrategy() { + @Override + public Iterable<Throwable> createExceptionIterable(Throwable exception) { + List<Throwable> answer = new ArrayList<>(); + // reversing default implementation order + for (Throwable throwable : super.createExceptionIterable(exception)) { + answer.add(0, throwable); + } + return answer; + } + }); + return answer; + } + + /** + * With the default implementation throwing MyUserException is matched to the generic Exception.class policy because + * of its cause. Whereas when reversing the order to check, MyUserException is matched to its superclass + * IllegalStateException.class policy first + */ + @Test + public void testReverseBehavior() throws Exception { + MockEndpoint mock = getMockEndpoint(ERROR_USER_QUEUE); + mock.expectedMessageCount(1); + getMockEndpoint("mock:result").expectedMessageCount(0); + + try { + template.sendBody("direct:a", "Hello Camel"); + fail("Should have thrown an Exception"); + } catch (Exception e) { + // expected + } + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + public void configure() throws Exception { + onException(IllegalStateException.class).maximumRedeliveries(1).redeliveryDelay(0) + .to(ERROR_USER_QUEUE); + + onException(Exception.class).maximumRedeliveries(1).redeliveryDelay(0) + .to(ERROR_QUEUE); + + from("direct:a").process(new Processor() { + public void process(Exchange exchange) throws Exception { + String s = exchange.getIn().getBody(String.class); + if ("Hello Camel".equals(s)) { + throw new MyUserException("Forced for testing", new IOException("Uh oh!")); + } + exchange.getMessage().setBody("Hello World"); + } + }).to("mock:result"); + } + }; + } +}