From 7a5dbdaccab3be52362268b6fb953291fb54ea12 Mon Sep 17 00:00:00 2001 From: lburgazzoli Date: Tue, 26 May 2020 19:59:26 +0200 Subject: [PATCH] yaml-loader: add support for OnException --- .../k/loader/yaml/parser/FromStepParser.java | 2 +- .../yaml/parser/OnExceptionStepParser.java | 173 ++++++++++++++++++ .../k/loader/yaml/parser/RestStepParser.java | 2 +- .../k/loader/yaml/spi/StartStepParser.java | 1 - .../camel/k/loader/yaml/spi/StepParser.java | 17 +- .../camel/k/loader/yaml/YamlSourceLoader.java | 42 +---- .../camel/k/loader/yaml/RoutesTest.groovy | 61 +++--- .../camel/k/loader/yaml/TestSupport.groovy | 17 +- .../loader/yaml/parser/OnExceptionTest.groovy | 138 ++++++++++++++ .../k/loader/yaml/support/MyException.groovy | 23 +++ .../yaml/support/MyFailingProcessor.groovy | 27 +++ .../routes/RoutesTest_onExceptionHandled.yaml | 28 +++ .../yaml/parser/KnativeStepParser.java | 2 +- 13 files changed, 457 insertions(+), 76 deletions(-) create mode 100644 camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/OnExceptionStepParser.java create mode 100644 camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/parser/OnExceptionTest.groovy create mode 100644 camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/support/MyException.groovy create mode 100644 camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/support/MyFailingProcessor.groovy create mode 100644 camel-k-loader-yaml/camel-k-loader-yaml/src/test/resources/routes/RoutesTest_onExceptionHandled.yaml diff --git a/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/FromStepParser.java b/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/FromStepParser.java index 22f846fde..47e95fc75 100644 --- a/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/FromStepParser.java +++ b/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/FromStepParser.java @@ -37,7 +37,7 @@ public class FromStepParser implements StartStepParser { public ProcessorDefinition toStartProcessor(Context context) { final FromStepDefinition definition = context.node(FromStepDefinition.class); final String uri = definition.getEndpointUri(); - final RouteDefinition route = new RouteDefinition().from(uri); + final RouteDefinition route = context.builder().from(uri); // as this is a start converter, steps are mandatory StepParserSupport.notNull(definition.steps, "steps"); diff --git a/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/OnExceptionStepParser.java b/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/OnExceptionStepParser.java new file mode 100644 index 000000000..6e1264345 --- /dev/null +++ b/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/OnExceptionStepParser.java @@ -0,0 +1,173 @@ +/* + * 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.k.loader.yaml.parser; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonAlias; +import org.apache.camel.k.annotation.yaml.YAMLNodeDefinition; +import org.apache.camel.k.annotation.yaml.YAMLStepParser; +import org.apache.camel.k.loader.yaml.model.Step; +import org.apache.camel.k.loader.yaml.spi.ProcessorStepParser; +import org.apache.camel.k.loader.yaml.spi.StartStepParser; +import org.apache.camel.k.loader.yaml.spi.StepParserSupport; +import org.apache.camel.model.ExpressionSubElementDefinition; +import org.apache.camel.model.OnExceptionDefinition; +import org.apache.camel.model.ProcessorDefinition; +import org.apache.camel.model.RedeliveryPolicyDefinition; +import org.apache.camel.model.WhenDefinition; +import org.apache.camel.model.language.ConstantExpression; +import org.apache.camel.model.language.ExpressionDefinition; +import org.apache.camel.reifier.OnExceptionReifier; + +import static org.apache.camel.util.ObjectHelper.ifNotEmpty; + +@YAMLStepParser("on-exception") +public class OnExceptionStepParser implements StartStepParser, ProcessorStepParser { + @SuppressWarnings("unchecked") + @Override + public ProcessorDefinition toStartProcessor(Context context) { + final Definition definition = context.node(Definition.class); + final OnExceptionDefinition onException = context.builder().onException(); + + if (definition.exceptions == null) { + definition.exceptions = List.of(Exception.class.getName()); + } + + onException.setExceptions(definition.exceptions); + onException.setRouteScoped(false); + + mapToOnException(context, definition, onException); + + return StepParserSupport.convertSteps( + context, + onException, + definition.steps); + } + + @Override + public ProcessorDefinition toProcessor(Context context) { + final Definition definition = context.node(Definition.class); + final OnExceptionDefinition onException = new OnExceptionDefinition(); + + if (definition.exceptions == null) { + definition.exceptions = List.of(Exception.class.getName()); + } + + onException.setExceptions(definition.exceptions); + onException.setRouteScoped(true); + + mapToOnException(context, definition, onException); + + return StepParserSupport.convertSteps( + context, + onException, + definition.steps); + } + + private static void mapToOnException(Context context, Definition definition, OnExceptionDefinition onException) { + ifNotEmpty(definition.retryWhile, onException::setRetryWhile); + ifNotEmpty(definition.handled, onException::setHandled); + ifNotEmpty(definition.continued, onException::setContinued); + ifNotEmpty(definition.continued, onException::setContinued); + ifNotEmpty(definition.redeliveryPolicyType, onException::setRedeliveryPolicyType); + ifNotEmpty(definition.redeliveryPolicyRef, onException::setRedeliveryPolicyRef); + ifNotEmpty(definition.onRedeliveryRef, onException::setOnRedeliveryRef); + ifNotEmpty(definition.onExceptionOccurredRef, onException::setOnExceptionOccurredRef); + ifNotEmpty(definition.useOriginalMessage, val -> onException.setUseOriginalMessage(Boolean.toString(val))); + ifNotEmpty(definition.useOriginalBody, val -> onException.setUseOriginalBody(Boolean.toString(val))); + + if (definition.onWhen != null) { + StepParserSupport.notNull(definition.onWhen.steps, "onWhen.steps"); + + StepParserSupport.convertSteps( + context, + definition.onWhen, + definition.onWhen.steps + ); + + onException.setOnWhen(definition.onWhen); + } + } + + @YAMLNodeDefinition(reifiers = OnExceptionReifier.class) + public static final class Definition { + public List steps; + + @JsonAlias("exceptions") + public List exceptions; + + @JsonAlias({"when", "on-when"}) + public When onWhen; + @JsonAlias("retry-while") + public ExpressionElement retryWhile; + @JsonAlias("handled") + public MaybeBooleanExpressionElement handled; + @JsonAlias("continued") + public MaybeBooleanExpressionElement continued; + + @JsonAlias("redelivery-policy") + public RedeliveryPolicyDefinition redeliveryPolicyType; + @JsonAlias("redelivery-policy-ref") + public String redeliveryPolicyRef; + + @JsonAlias("on-redelivery-ref") + public String onRedeliveryRef; + @JsonAlias("on-exception-occurred-ref") + public String onExceptionOccurredRef; + @JsonAlias("use-original-message") + public boolean useOriginalMessage; + @JsonAlias("use-original-body") + public boolean useOriginalBody; + + public static final class When extends WhenDefinition implements HasExpression { + public List steps; + } + + public static final class ExpressionElement extends ExpressionSubElementDefinition implements HasExpression { + @Override + public void setExpression(ExpressionDefinition expressionDefinition) { + super.setExpressionType(expressionDefinition); + } + + @Override + public ExpressionDefinition getExpression() { + return super.getExpressionType(); + } + } + + public static final class MaybeBooleanExpressionElement extends ExpressionSubElementDefinition implements HasExpression { + public MaybeBooleanExpressionElement() { + } + + public MaybeBooleanExpressionElement(boolean argument) { + setExpression(new ConstantExpression(Boolean.toString(argument))); + } + + @Override + public void setExpression(ExpressionDefinition expressionDefinition) { + super.setExpressionType(expressionDefinition); + } + + @Override + public ExpressionDefinition getExpression() { + return super.getExpressionType(); + } + } + } +} + diff --git a/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/RestStepParser.java b/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/RestStepParser.java index 99cb057a0..936f5567a 100644 --- a/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/RestStepParser.java +++ b/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/parser/RestStepParser.java @@ -41,7 +41,7 @@ public ProcessorDefinition toStartProcessor(Context context) { StepParserSupport.notNull(definition.verb, "verb"); StepParserSupport.notNull(definition.steps, "steps"); - RestDefinition rest = new RestDefinition().verb(definition.verb, definition.uri); + RestDefinition rest = context.builder().rest().verb(definition.verb, definition.uri); ObjectHelper.ifNotEmpty(definition.apiDocs, rest::apiDocs); ObjectHelper.ifNotEmpty(definition.enableCORS, rest::enableCORS); diff --git a/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/spi/StartStepParser.java b/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/spi/StartStepParser.java index f80bcdbb5..10426a218 100644 --- a/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/spi/StartStepParser.java +++ b/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/spi/StartStepParser.java @@ -22,7 +22,6 @@ public interface StartStepParser extends StepParser { /** * @param context - * @return */ ProcessorDefinition toStartProcessor(Context context); diff --git a/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/spi/StepParser.java b/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/spi/StepParser.java index 2fab5f246..42099ee2d 100644 --- a/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/spi/StepParser.java +++ b/camel-k-loader-yaml/camel-k-loader-yaml-common/src/main/java/org/apache/camel/k/loader/yaml/spi/StepParser.java @@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.camel.CamelContext; import org.apache.camel.ExtendedCamelContext; +import org.apache.camel.builder.RouteBuilder; import org.apache.camel.spi.HasCamelContext; import org.apache.camel.util.ObjectHelper; @@ -35,12 +36,12 @@ public interface StepParser { */ class Context implements HasCamelContext { private final ObjectMapper mapper; - private final CamelContext camelContext; + private final RouteBuilder builder; private final JsonNode node; private final Resolver resolver; - public Context(CamelContext camelContext, ObjectMapper mapper, JsonNode node, Resolver resolver) { - this.camelContext = camelContext; + public Context(RouteBuilder builder, ObjectMapper mapper, JsonNode node, Resolver resolver) { + this.builder = builder; this.mapper = mapper; this.node = node; this.resolver = ObjectHelper.notNull(resolver, "resolver"); @@ -48,7 +49,11 @@ public Context(CamelContext camelContext, ObjectMapper mapper, JsonNode node, Re @Override public CamelContext getCamelContext() { - return camelContext; + return builder.getContext(); + } + + public RouteBuilder builder() { + return builder; } public JsonNode node() { @@ -75,7 +80,7 @@ public T node(Class type) { } public T lookup(Class type, String stepId) { - StepParser parser = resolver.resolve(camelContext, stepId); + StepParser parser = resolver.resolve(builder.getContext(), stepId); if (type.isInstance(parser)) { return type.cast(parser); } @@ -85,7 +90,7 @@ public T lookup(Class type, String stepId) { public static Context of(Context context, JsonNode step) { return new Context( - context.camelContext, + context.builder, context.mapper, step, context.resolver diff --git a/camel-k-loader-yaml/camel-k-loader-yaml/src/main/java/org/apache/camel/k/loader/yaml/YamlSourceLoader.java b/camel-k-loader-yaml/camel-k-loader-yaml/src/main/java/org/apache/camel/k/loader/yaml/YamlSourceLoader.java index 1c1b4cd06..a300fe10a 100644 --- a/camel-k-loader-yaml/camel-k-loader-yaml/src/main/java/org/apache/camel/k/loader/yaml/YamlSourceLoader.java +++ b/camel-k-loader-yaml/camel-k-loader-yaml/src/main/java/org/apache/camel/k/loader/yaml/YamlSourceLoader.java @@ -19,7 +19,6 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -31,7 +30,6 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; -import org.apache.camel.CamelContext; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.k.Runtime; import org.apache.camel.k.Source; @@ -40,11 +38,6 @@ import org.apache.camel.k.loader.yaml.model.Step; import org.apache.camel.k.loader.yaml.spi.StartStepParser; import org.apache.camel.k.loader.yaml.spi.StepParser; -import org.apache.camel.model.ProcessorDefinition; -import org.apache.camel.model.RouteDefinition; -import org.apache.camel.model.RoutesDefinition; -import org.apache.camel.model.rest.RestDefinition; -import org.apache.camel.model.rest.RestsDefinition; @Loader("yaml") public class YamlSourceLoader implements SourceLoader { @@ -94,41 +87,12 @@ final RouteBuilder builder(InputStream is) { @Override public void configure() throws Exception { final StepParser.Resolver resolver = StepParser.Resolver.caching(new YamlStepResolver()); - final CamelContext camelContext = getContext(); - final List routes = new ArrayList<>(); - final List rests = new ArrayList<>(); try (is) { for (Step step : mapper.readValue(is, Step[].class)) { - final StepParser.Context context = new StepParser.Context(camelContext, mapper, step.node, resolver); - final ProcessorDefinition root = StartStepParser.invoke(context, step.id); - - if (root == null) { - throw new IllegalStateException("No route definition"); - } - if (!(root instanceof RouteDefinition)) { - throw new IllegalStateException("Root definition should be of type RouteDefinition"); - } - - RouteDefinition r = (RouteDefinition) root; - if (r.getRestDefinition() == null) { - routes.add(r); - } else { - rests.add(r.getRestDefinition()); - } - } - - if (!routes.isEmpty()) { - RoutesDefinition definition = new RoutesDefinition(); - definition.setRoutes(routes); - - setRouteCollection(definition); - } - if (!rests.isEmpty()) { - RestsDefinition definition = new RestsDefinition(); - definition.setRests(rests); - - setRestCollection(definition); + StartStepParser.invoke( + new StepParser.Context(this, mapper, step.node, resolver), + step.id); } } } diff --git a/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/RoutesTest.groovy b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/RoutesTest.groovy index 3fc24b00d..cde4540ae 100644 --- a/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/RoutesTest.groovy +++ b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/RoutesTest.groovy @@ -17,6 +17,7 @@ package org.apache.camel.k.loader.yaml import org.apache.camel.component.mock.MockEndpoint +import org.apache.camel.k.loader.yaml.support.MyFailingProcessor import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy import org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository @@ -26,15 +27,15 @@ class RoutesTest extends TestSupport { setup: def context = startContext() - org.apache.camel.k.loader.yaml.TestSupport.mockEndpoint(context,'mock:split') { + mockEndpoint(context,'mock:split') { expectedMessageCount = 3 expectedBodiesReceived 'a', 'b', 'c' } - org.apache.camel.k.loader.yaml.TestSupport.mockEndpoint(context,'mock:route') { + mockEndpoint(context,'mock:route') { expectedMessageCount = 1 expectedBodiesReceived 'a,b,c' } - org.apache.camel.k.loader.yaml.TestSupport.mockEndpoint(context,'mock:flow') { + mockEndpoint(context,'mock:flow') { expectedMessageCount = 3 expectedBodiesReceived 'a', 'b', 'c' } @@ -53,15 +54,15 @@ class RoutesTest extends TestSupport { setup: def context = startContext() - org.apache.camel.k.loader.yaml.TestSupport.mockEndpoint(context, 'mock:route') { + mockEndpoint(context, 'mock:route') { expectedMessageCount 2 expectedBodiesReceived 'a', 'b' } - org.apache.camel.k.loader.yaml.TestSupport.mockEndpoint(context, 'mock:filter') { + mockEndpoint(context, 'mock:filter') { expectedMessageCount 1 expectedBodiesReceived 'a' } - org.apache.camel.k.loader.yaml.TestSupport.mockEndpoint(context,'mock:flow') { + mockEndpoint(context,'mock:flow') { expectedMessageCount 1 expectedBodiesReceived 'a' } @@ -84,7 +85,7 @@ class RoutesTest extends TestSupport { 'aggregatorStrategy': new UseLatestAggregationStrategy() ]) - org.apache.camel.k.loader.yaml.TestSupport.mockEndpoint(context, 'mock:route') { + mockEndpoint(context, 'mock:route') { expectedMessageCount 2 expectedBodiesReceived '2', '4' } @@ -103,30 +104,42 @@ class RoutesTest extends TestSupport { def 'idempotentConsumer'() { setup: - def context = startContext([ + def context = startContext([ 'myRepo': new MemoryIdempotentRepository() - ]) + ]) - org.apache.camel.k.loader.yaml.TestSupport.mockEndpoint(context,'mock:idempotent') { - expectedMessageCount = 3 - expectedBodiesReceived 'a', 'b', 'c' - } - org.apache.camel.k.loader.yaml.TestSupport.mockEndpoint(context,'mock:route') { - expectedMessageCount = 5 - expectedBodiesReceived 'a', 'b', 'a2', 'b2', 'c' - } + mockEndpoint(context,'mock:idempotent') { + expectedMessageCount = 3 + expectedBodiesReceived 'a', 'b', 'c' + } + mockEndpoint(context,'mock:route') { + expectedMessageCount = 5 + expectedBodiesReceived 'a', 'b', 'a2', 'b2', 'c' + } when: - context.createProducerTemplate().with { - sendBodyAndHeader('direct:route', 'a', 'id', '1') - sendBodyAndHeader('direct:route', 'b', 'id', '2') - sendBodyAndHeader('direct:route', 'a2', 'id', '1') - sendBodyAndHeader('direct:route', 'b2', 'id', '2') - sendBodyAndHeader('direct:route', 'c', 'id', '3') - } + context.createProducerTemplate().with { + sendBodyAndHeader('direct:route', 'a', 'id', '1') + sendBodyAndHeader('direct:route', 'b', 'id', '2') + sendBodyAndHeader('direct:route', 'a2', 'id', '1') + sendBodyAndHeader('direct:route', 'b2', 'id', '2') + sendBodyAndHeader('direct:route', 'c', 'id', '3') + } then: MockEndpoint.assertIsSatisfied(context) cleanup: context?.stop() } + def 'onExceptionHandled'() { + setup: + def context = startContext([ + 'myFailingProcessor' : new MyFailingProcessor() + ]) + when: + def out = context.createProducerTemplate().requestBody('direct:start', 'Hello World'); + then: + out == 'Sorry' + cleanup: + context?.stop() + } } diff --git a/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/TestSupport.groovy b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/TestSupport.groovy index 597d4296c..7a0f17a60 100644 --- a/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/TestSupport.groovy +++ b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/TestSupport.groovy @@ -19,6 +19,7 @@ package org.apache.camel.k.loader.yaml import com.fasterxml.jackson.databind.JsonNode import groovy.util.logging.Slf4j import org.apache.camel.CamelContext +import org.apache.camel.builder.RouteBuilder import org.apache.camel.component.mock.MockEndpoint import org.apache.camel.impl.DefaultCamelContext import org.apache.camel.k.loader.yaml.spi.ProcessorStepParser @@ -36,13 +37,23 @@ class TestSupport extends Specification { static StepParser.Context stepContext(String content) { def node = MAPPER.readTree(content.stripMargin()) - def cctx = new DefaultCamelContext() + def builder = new RouteBuilder(new DefaultCamelContext()) { + @Override + void configure() throws Exception { + } + } - return new StepParser.Context(cctx, MAPPER, node, RESOLVER) + return new StepParser.Context(builder, MAPPER, node, RESOLVER) } static StepParser.Context stepContext(JsonNode content) { - return new StepParser.Context(new DefaultCamelContext(), MAPPER, content, RESOLVER) + def builder = new RouteBuilder(new DefaultCamelContext()) { + @Override + void configure() throws Exception { + } + } + + return new StepParser.Context(builder, MAPPER, content, RESOLVER) } static CamelContext startContext(String content) { diff --git a/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/parser/OnExceptionTest.groovy b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/parser/OnExceptionTest.groovy new file mode 100644 index 000000000..4f286f873 --- /dev/null +++ b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/parser/OnExceptionTest.groovy @@ -0,0 +1,138 @@ +/* + * 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.k.loader.yaml.parser + +import org.apache.camel.k.loader.yaml.TestSupport +import org.apache.camel.model.OnExceptionDefinition +import org.apache.camel.model.ToDefinition +import org.apache.camel.model.language.ConstantExpression + +class OnExceptionTest extends TestSupport { + def "definition (route)"() { + given: + def stepContext = stepContext(''' + exceptions: + - java.lang.Exception + - java.io.IOException + when: + constant: "when" + steps: + - to: 'log:when' + retry-while: + constant: "while" + handled: + constant: "handled" + continued: + constant: "continued" + + ''') + when: + def processor = new OnExceptionStepParser().toProcessor(stepContext) + then: + with(processor, OnExceptionDefinition) { + exceptions.contains('java.lang.Exception') + exceptions.contains('java.io.IOException') + + with(onWhen) { + outputs.size() == 1 + + with(outputs[0], ToDefinition) { + endpointUri == 'log:when' + } + with(expression, ConstantExpression) { + expression == 'when' + } + } + with(retryWhile.expressionType, ConstantExpression) { + expression == 'while' + } + with(handled.expressionType, ConstantExpression) { + expression == 'handled' + } + with(continued.expressionType, ConstantExpression) { + expression == 'continued' + } + } + } + + def "definition with maybe-booleans"() { + when: + def processor = toProcessor(OnExceptionStepParser, ''' + handled: true + continued: false + ''') + then: + with(processor, OnExceptionDefinition) { + with(handled.expressionType, ConstantExpression) { + expression == 'true' + } + with(continued.expressionType, ConstantExpression) { + expression == 'false' + } + } + } + + def "definition (global)"() { + given: + def stepContext = stepContext(''' + exceptions: + - java.lang.Exception + - java.io.IOException + when: + constant: "when" + steps: + - to: 'log:when' + retry-while: + constant: "while" + handled: + constant: "handled" + continued: + constant: "continued" + ''') + when: + def processor = new OnExceptionStepParser().toStartProcessor(stepContext) + then: + stepContext.builder().routeCollection.onExceptions.size() == 1 + stepContext.builder().routeCollection.onExceptions[0].exceptions.contains("java.lang.Exception") + stepContext.builder().routeCollection.onExceptions[0].exceptions.contains("java.io.IOException") + + with(processor, OnExceptionDefinition) { + exceptions.contains('java.lang.Exception') + exceptions.contains('java.io.IOException') + + with(onWhen) { + outputs.size() == 1 + + with(outputs[0], ToDefinition) { + endpointUri == 'log:when' + } + with(expression, ConstantExpression) { + expression == 'when' + } + } + with(retryWhile.expressionType, ConstantExpression) { + expression == 'while' + } + with(handled.expressionType, ConstantExpression) { + expression == 'handled' + } + with(continued.expressionType, ConstantExpression) { + expression == 'continued' + } + } + } +} diff --git a/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/support/MyException.groovy b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/support/MyException.groovy new file mode 100644 index 000000000..5b573b36f --- /dev/null +++ b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/support/MyException.groovy @@ -0,0 +1,23 @@ +/* + * 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.k.loader.yaml.support + +class MyException extends Exception { + MyException(String message) { + super(message) + } +} diff --git a/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/support/MyFailingProcessor.groovy b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/support/MyFailingProcessor.groovy new file mode 100644 index 000000000..39043518b --- /dev/null +++ b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/groovy/org/apache/camel/k/loader/yaml/support/MyFailingProcessor.groovy @@ -0,0 +1,27 @@ +/* + * 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.k.loader.yaml.support + +import org.apache.camel.Exchange +import org.apache.camel.Processor + +class MyFailingProcessor implements Processor { + @Override + void process(Exchange exchange) throws Exception { + throw new MyException('Sorry you cannot do this') + } +} diff --git a/camel-k-loader-yaml/camel-k-loader-yaml/src/test/resources/routes/RoutesTest_onExceptionHandled.yaml b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/resources/routes/RoutesTest_onExceptionHandled.yaml new file mode 100644 index 000000000..3bd3bcb7e --- /dev/null +++ b/camel-k-loader-yaml/camel-k-loader-yaml/src/test/resources/routes/RoutesTest_onExceptionHandled.yaml @@ -0,0 +1,28 @@ +# +# 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. +# +- on-exception: + exceptioons: + - org.apache.camel.k.loader.yaml.support.MyException + handled: true + steps: + - transform: + constant: "Sorry" +- from: + uri: "direct:start" + steps: + - process: + ref: "myFailingProcessor" \ No newline at end of file diff --git a/camel-k-runtime-knative/src/main/java/org/apache/camel/k/knative/yaml/parser/KnativeStepParser.java b/camel-k-runtime-knative/src/main/java/org/apache/camel/k/knative/yaml/parser/KnativeStepParser.java index 48c07f8ac..b1d4c750b 100644 --- a/camel-k-runtime-knative/src/main/java/org/apache/camel/k/knative/yaml/parser/KnativeStepParser.java +++ b/camel-k-runtime-knative/src/main/java/org/apache/camel/k/knative/yaml/parser/KnativeStepParser.java @@ -37,7 +37,7 @@ public class KnativeStepParser implements ProcessorStepParser, StartStepParser { public ProcessorDefinition toStartProcessor(Context context) { final Definition definition = context.node(Definition.class); final String uri = definition.getEndpointUri(); - final RouteDefinition route = new RouteDefinition().from(uri); + final RouteDefinition route = context.builder().from(uri); // steps are mandatory ObjectHelper.notNull(definition.steps, "from steps");