From c0228d4f12c6a34859e69337bd0017a855fe195c Mon Sep 17 00:00:00 2001 From: Christoph Deppisch Date: Fri, 15 Mar 2024 09:21:43 +0100 Subject: [PATCH] CAMEL-20568: Set error handler on route level in YAML DSL (#13489) Allow to set error handler on route level in Camel YAML DSL --- .../yaml/deserializers/CustomResolver.java | 2 + .../ErrorHandlerBuilderDeserializer.java | 33 ++----- .../ErrorHandlerDeserializer.java | 71 +++++++++++++++ .../RouteDefinitionDeserializer.java | 9 ++ .../resources/schema/camelYamlDsl.json | 89 ++++++++++++++++++- .../apache/camel/dsl/yaml/RoutesTest.groovy | 88 +++++++++++++++++- 6 files changed, 264 insertions(+), 28 deletions(-) create mode 100644 dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/ErrorHandlerDeserializer.java diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/CustomResolver.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/CustomResolver.java index c0ccc80eac775..ab9415ece5d7d 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/CustomResolver.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/CustomResolver.java @@ -87,6 +87,8 @@ public ConstructNode resolve(String id) { case "beans": return beansDeserializer; case "errorHandler": + return new ErrorHandlerDeserializer(); + case "org.apache.camel.ErrorHandlerFactory": return new ErrorHandlerBuilderDeserializer(); case "org.apache.camel.model.ProcessorDefinition": return new ProcessorDefinitionDeserializer(); diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/ErrorHandlerBuilderDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/ErrorHandlerBuilderDeserializer.java index 545fc9e300e5e..daf18aa1d3aa0 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/ErrorHandlerBuilderDeserializer.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/ErrorHandlerBuilderDeserializer.java @@ -16,7 +16,6 @@ */ package org.apache.camel.dsl.yaml.deserializers; -import org.apache.camel.CamelContext; import org.apache.camel.ErrorHandlerFactory; import org.apache.camel.dsl.yaml.common.YamlDeserializationContext; import org.apache.camel.dsl.yaml.common.YamlDeserializerResolver; @@ -27,7 +26,6 @@ import org.apache.camel.model.errorhandler.JtaTransactionErrorHandlerDefinition; import org.apache.camel.model.errorhandler.NoErrorHandlerDefinition; import org.apache.camel.model.errorhandler.RefErrorHandlerDefinition; -import org.apache.camel.spi.CamelContextCustomizer; import org.apache.camel.spi.annotations.YamlIn; import org.apache.camel.spi.annotations.YamlProperty; import org.apache.camel.spi.annotations.YamlType; @@ -36,16 +34,11 @@ import org.snakeyaml.engine.v2.nodes.Node; import org.snakeyaml.engine.v2.nodes.NodeTuple; -import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.asMappingNode; -import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.asText; -import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.asType; -import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.getDeserializationContext; -import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.setDeserializationContext; +import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.*; @YamlIn @YamlType( - inline = false, - nodes = { "error-handler", "errorHandler" }, + types = ErrorHandlerFactory.class, order = YamlDeserializerResolver.ORDER_DEFAULT, properties = { @YamlProperty(name = "deadLetterChannel", @@ -69,15 +62,6 @@ }) public class ErrorHandlerBuilderDeserializer implements ConstructNode { - private static CamelContextCustomizer customizer(ErrorHandlerFactory builder) { - return new CamelContextCustomizer() { - @Override - public void configure(CamelContext camelContext) { - camelContext.getCamelContextExtension().setErrorHandlerFactory(builder); - } - }; - } - @Override public Object construct(Node node) { final MappingNode bn = asMappingNode(node); @@ -92,17 +76,16 @@ public Object construct(Node node) { key = org.apache.camel.util.StringHelper.dashToCamelCase(key); switch (key) { case "deadLetterChannel": - return customizer(asType(val, DeadLetterChannelDefinition.class)); + return asType(val, DeadLetterChannelDefinition.class); case "defaultErrorHandler": - return customizer(asType(val, DefaultErrorHandlerDefinition.class)); + return asType(val, DefaultErrorHandlerDefinition.class); case "jtaTransactionErrorHandler": - return customizer(asType(val, JtaTransactionErrorHandlerDefinition.class)); + case "springTransactionErrorHandler": + return asType(val, JtaTransactionErrorHandlerDefinition.class); case "noErrorHandler": - return customizer(asType(val, NoErrorHandlerDefinition.class)); + return asType(val, NoErrorHandlerDefinition.class); case "refErrorHandler": - return customizer(asType(val, RefErrorHandlerDefinition.class)); - case "springTransactionErrorHandler": - return customizer(asType(val, JtaTransactionErrorHandlerDefinition.class)); + return asType(val, RefErrorHandlerDefinition.class); default: throw new UnsupportedFieldException(val, key); } diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/ErrorHandlerDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/ErrorHandlerDeserializer.java new file mode 100644 index 0000000000000..d9a89cd5f5fbf --- /dev/null +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/ErrorHandlerDeserializer.java @@ -0,0 +1,71 @@ +/* + * 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.dsl.yaml.deserializers; + +import org.apache.camel.CamelContext; +import org.apache.camel.ErrorHandlerFactory; +import org.apache.camel.dsl.yaml.common.YamlDeserializerResolver; +import org.apache.camel.spi.CamelContextCustomizer; +import org.apache.camel.spi.annotations.YamlIn; +import org.apache.camel.spi.annotations.YamlProperty; +import org.apache.camel.spi.annotations.YamlType; +import org.snakeyaml.engine.v2.api.ConstructNode; +import org.snakeyaml.engine.v2.nodes.Node; + +@YamlIn +@YamlType( + inline = false, + nodes = { "error-handler", "errorHandler" }, + order = YamlDeserializerResolver.ORDER_DEFAULT, + properties = { + @YamlProperty(name = "deadLetterChannel", + type = "object:org.apache.camel.model.errorhandler.DeadLetterChannelDefinition", + oneOf = "errorHandler"), + @YamlProperty(name = "defaultErrorHandler", + type = "object:org.apache.camel.model.errorhandler.DefaultErrorHandlerDefinition", + oneOf = "errorHandler"), + @YamlProperty(name = "jtaTransactionErrorHandler", + type = "object:org.apache.camel.model.errorhandler.JtaTransactionErrorHandlerDefinition", + oneOf = "errorHandler"), + @YamlProperty(name = "noErrorHandler", + type = "object:org.apache.camel.model.errorhandler.NoErrorHandlerDefinition", + oneOf = "errorHandler"), + @YamlProperty(name = "refErrorHandler", + type = "object:org.apache.camel.model.errorhandler.RefErrorHandlerDefinition", + oneOf = "errorHandler"), + @YamlProperty(name = "springTransactionErrorHandler", + type = "object:org.apache.camel.model.errorhandler.SpringTransactionErrorHandlerDefinition", + oneOf = "errorHandler"), + }) +public class ErrorHandlerDeserializer implements ConstructNode { + + private final ErrorHandlerBuilderDeserializer delegate = new ErrorHandlerBuilderDeserializer(); + + private static CamelContextCustomizer customizer(ErrorHandlerFactory builder) { + return new CamelContextCustomizer() { + @Override + public void configure(CamelContext camelContext) { + camelContext.getCamelContextExtension().setErrorHandlerFactory(builder); + } + }; + } + + @Override + public Object construct(Node node) { + return customizer((ErrorHandlerFactory) delegate.construct(node)); + } +} diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java index eed0d6a2e08c2..413065acf3961 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java @@ -16,6 +16,7 @@ */ package org.apache.camel.dsl.yaml.deserializers; +import org.apache.camel.ErrorHandlerFactory; import org.apache.camel.dsl.yaml.common.YamlDeserializationContext; import org.apache.camel.dsl.yaml.common.YamlDeserializerBase; import org.apache.camel.dsl.yaml.common.YamlDeserializerResolver; @@ -50,6 +51,8 @@ @YamlProperty(name = "messageHistory", type = "boolean"), @YamlProperty(name = "logMask", type = "boolean"), @YamlProperty(name = "trace", type = "boolean"), + @YamlProperty(name = "errorHandlerRef", type = "string"), + @YamlProperty(name = "errorHandler", type = "object:org.apache.camel.ErrorHandlerFactory"), @YamlProperty(name = "shutdownRoute", type = "enum:Default,Defer", defaultValue = "Default", description = "To control how to shut down the route."), @@ -128,6 +131,12 @@ protected void setProperties(RouteDefinition target, MappingNode node) { case "trace": target.setTrace(asText(val)); break; + case "errorHandlerRef": + target.setErrorHandlerRef(asText(val)); + break; + case "errorHandler": + target.setErrorHandlerFactory(asType(val, ErrorHandlerFactory.class)); + break; case "inputType": target.setInputType(asType(val, InputTypeDefinition.class)); break; diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json index ed68cdb827830..7f5cb52893114 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json @@ -207,6 +207,85 @@ } } }, + "org.apache.camel.ErrorHandlerFactory" : { + "type" : "object", + "additionalProperties" : false, + "anyOf" : [ { + "oneOf" : [ { + "type" : "object", + "required" : [ "deadLetterChannel" ], + "properties" : { + "deadLetterChannel" : { + "$ref" : "#/items/definitions/org.apache.camel.model.errorhandler.DeadLetterChannelDefinition" + } + } + }, { + "not" : { + "anyOf" : [ { + "required" : [ "deadLetterChannel" ] + }, { + "required" : [ "defaultErrorHandler" ] + }, { + "required" : [ "jtaTransactionErrorHandler" ] + }, { + "required" : [ "noErrorHandler" ] + }, { + "required" : [ "refErrorHandler" ] + }, { + "required" : [ "springTransactionErrorHandler" ] + } ] + } + }, { + "type" : "object", + "required" : [ "defaultErrorHandler" ], + "properties" : { + "defaultErrorHandler" : { + "$ref" : "#/items/definitions/org.apache.camel.model.errorhandler.DefaultErrorHandlerDefinition" + } + } + }, { + "type" : "object", + "required" : [ "jtaTransactionErrorHandler" ], + "properties" : { + "jtaTransactionErrorHandler" : { + "$ref" : "#/items/definitions/org.apache.camel.model.errorhandler.JtaTransactionErrorHandlerDefinition" + } + } + }, { + "type" : "object", + "required" : [ "noErrorHandler" ], + "properties" : { + "noErrorHandler" : { + "$ref" : "#/items/definitions/org.apache.camel.model.errorhandler.NoErrorHandlerDefinition" + } + } + }, { + "type" : "object", + "required" : [ "refErrorHandler" ], + "properties" : { + "refErrorHandler" : { + "$ref" : "#/items/definitions/org.apache.camel.model.errorhandler.RefErrorHandlerDefinition" + } + } + }, { + "type" : "object", + "required" : [ "springTransactionErrorHandler" ], + "properties" : { + "springTransactionErrorHandler" : { + "$ref" : "#/items/definitions/org.apache.camel.model.errorhandler.SpringTransactionErrorHandlerDefinition" + } + } + } ] + } ], + "properties" : { + "deadLetterChannel" : { }, + "defaultErrorHandler" : { }, + "jtaTransactionErrorHandler" : { }, + "noErrorHandler" : { }, + "refErrorHandler" : { }, + "springTransactionErrorHandler" : { } + } + }, "org.apache.camel.dsl.yaml.deserializers.BeansDeserializer" : { "type" : "array", "additionalProperties" : false, @@ -214,7 +293,7 @@ "$ref" : "#/items/definitions/org.apache.camel.model.app.RegistryBeanDefinition" } }, - "org.apache.camel.dsl.yaml.deserializers.ErrorHandlerBuilderDeserializer" : { + "org.apache.camel.dsl.yaml.deserializers.ErrorHandlerDeserializer" : { "type" : "object", "additionalProperties" : false, "anyOf" : [ { @@ -5094,6 +5173,12 @@ "description" : { "type" : "string" }, + "errorHandler" : { + "$ref" : "#/items/definitions/org.apache.camel.ErrorHandlerFactory" + }, + "errorHandlerRef" : { + "type" : "string" + }, "from" : { "$ref" : "#/items/definitions/org.apache.camel.model.FromDefinition" }, @@ -16206,7 +16291,7 @@ "$ref" : "#/items/definitions/org.apache.camel.dsl.yaml.deserializers.BeansDeserializer" }, "errorHandler" : { - "$ref" : "#/items/definitions/org.apache.camel.dsl.yaml.deserializers.ErrorHandlerBuilderDeserializer" + "$ref" : "#/items/definitions/org.apache.camel.dsl.yaml.deserializers.ErrorHandlerDeserializer" }, "from" : { "$ref" : "#/items/definitions/org.apache.camel.dsl.yaml.deserializers.RouteFromDefinitionDeserializer" diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy index 4e4a2e5b4c623..ee19ea61da652 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy @@ -19,6 +19,8 @@ package org.apache.camel.dsl.yaml import org.apache.camel.dsl.yaml.support.YamlTestSupport import org.apache.camel.model.LogDefinition import org.apache.camel.model.RouteDefinition +import org.apache.camel.model.errorhandler.DeadLetterChannelDefinition +import org.apache.camel.model.errorhandler.RefErrorHandlerDefinition import org.junit.jupiter.api.Assertions class RoutesTest extends YamlTestSupport { @@ -164,6 +166,91 @@ class RoutesTest extends YamlTestSupport { } } + def "load route with error handler ref"() { + when: + loadRoutes ''' + - route: + errorHandlerRef: "myErrorHandler" + from: + uri: "direct:info" + steps: + - log: "message" + ''' + then: + context.routeDefinitions.size() == 1 + + with(context.routeDefinitions[0], RouteDefinition) { + input.endpointUri == 'direct:info' + with (outputs[0], LogDefinition) { + message == 'message' + } + errorHandlerFactorySet + errorHandlerFactory.class == RefErrorHandlerDefinition.class + with (errorHandlerFactory, RefErrorHandlerDefinition) { + ref == "myErrorHandler" + } + } + } + + def "load route with error handler"() { + when: + loadRoutes ''' + - route: + errorHandler: + refErrorHandler: + ref: "myErrorHandler" + from: + uri: "direct:info" + steps: + - log: "message" + ''' + then: + context.routeDefinitions.size() == 1 + + with(context.routeDefinitions[0], RouteDefinition) { + input.endpointUri == 'direct:info' + with (outputs[0], LogDefinition) { + message == 'message' + } + errorHandlerFactorySet + errorHandlerFactory.class == RefErrorHandlerDefinition.class + with (errorHandlerFactory, RefErrorHandlerDefinition) { + ref == "myErrorHandler" + } + } + } + + def "load route with error handler properties"() { + when: + loadRoutes ''' + - route: + errorHandler: + deadLetterChannel: + deadLetterUri: "mock:on-error" + redeliveryPolicy: + maximumRedeliveries: 3 + from: + uri: "direct:info" + steps: + - log: "message" + ''' + then: + context.routeDefinitions.size() == 1 + + with(context.routeDefinitions[0], RouteDefinition) { + input.endpointUri == 'direct:info' + with (outputs[0], LogDefinition) { + message == 'message' + } + errorHandlerFactorySet + errorHandlerFactory.class == DeadLetterChannelDefinition.class + with (errorHandlerFactory, DeadLetterChannelDefinition) { + deadLetterUri == 'mock:on-error' + redeliveryPolicy.maximumRedeliveries == "3" + } + } + } + def "load route with input/output types"() { when: loadRoutes ''' @@ -191,7 +278,6 @@ class RoutesTest extends YamlTestSupport { } } - def "load route inlined camelCase"() { when: loadRoutes '''