This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push: new 3da7391 CAMEL-15036: Make it easier to switch to use supervising route controller in XML DSL 3da7391 is described below commit 3da7391444e79f806e14097c1a134a8460346e0b Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Mon May 11 14:00:02 2020 +0200 CAMEL-15036: Make it easier to switch to use supervising route controller in XML DSL --- .../camel/cdi/xml/CamelContextFactoryBean.java | 13 ++ .../org/apache/camel/spring/camelContext.json | 1 + .../camel/spring/CamelContextFactoryBean.java | 12 ++ .../spring/handler/CamelNamespaceHandler.java | 3 + .../impl/SpringSupervisingRouteControllerTest.java | 85 ++------- .../impl/SpringSupervisingRouteControllerTest.xml | 50 ++++++ .../engine/DefaultSupervisingRouteController.java | 15 +- .../resources/org/apache/camel/core/xml/jaxb.index | 1 + .../org/apache/camel/core/xml/routeController.json | 26 +++ .../core/xml/AbstractCamelContextFactoryBean.java | 70 +++++++- .../core/xml/CamelRouteControllerDefinition.java | 197 +++++++++++++++++++++ .../DefaultSupervisingRouteControllerTest.java | 1 - 12 files changed, 387 insertions(+), 87 deletions(-) diff --git a/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java b/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java index 18d5c88..4827132 100644 --- a/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java +++ b/components/camel-cdi/src/main/java/org/apache/camel/cdi/xml/CamelContextFactoryBean.java @@ -41,6 +41,7 @@ import org.apache.camel.core.xml.AbstractCamelFactoryBean; import org.apache.camel.core.xml.CamelJMXAgentDefinition; import org.apache.camel.core.xml.CamelPropertyPlaceholderDefinition; import org.apache.camel.core.xml.CamelProxyFactoryDefinition; +import org.apache.camel.core.xml.CamelRouteControllerDefinition; import org.apache.camel.core.xml.CamelServiceExporterDefinition; import org.apache.camel.core.xml.CamelStreamCachingStrategyDefinition; import org.apache.camel.impl.DefaultCamelContext; @@ -177,6 +178,9 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Def @XmlElement(name = "streamCaching", type = CamelStreamCachingStrategyDefinition.class) private CamelStreamCachingStrategyDefinition camelStreamCachingStrategy; + @XmlElement(name = "routeController", type = CamelRouteControllerDefinition.class) + private CamelRouteControllerDefinition camelRouteController; + @XmlElement(name = "jmxAgent", type = CamelJMXAgentDefinition.class) private CamelJMXAgentDefinition camelJMXAgent; @@ -603,6 +607,15 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Def } @Override + public CamelRouteControllerDefinition getCamelRouteController() { + return camelRouteController; + } + + public void setCamelRouteController(CamelRouteControllerDefinition camelRouteController) { + this.camelRouteController = camelRouteController; + } + + @Override public String getTrace() { return trace; } diff --git a/components/camel-spring/src/generated/resources/org/apache/camel/spring/camelContext.json b/components/camel-spring/src/generated/resources/org/apache/camel/spring/camelContext.json index 9e5d43c..4a380f7 100644 --- a/components/camel-spring/src/generated/resources/org/apache/camel/spring/camelContext.json +++ b/components/camel-spring/src/generated/resources/org/apache/camel/spring/camelContext.json @@ -47,6 +47,7 @@ "contextScan": { "kind": "element", "displayName": "Context Scan", "required": false, "type": "object", "javaType": "org.apache.camel.model.ContextScanDefinition", "deprecated": false, "secret": false, "description": "Sets the context scanning (eg Spring's ApplicationContext) information. Context scanning allows for the automatic discovery of Camel routes runtime for inclusion e.g. org.apache.camel.builder.RouteBuilder implementations" }, "streamCaching": { "kind": "element", "displayName": "Stream Caching", "required": false, "type": "object", "javaType": "org.apache.camel.core.xml.CamelStreamCachingStrategyDefinition", "deprecated": false, "secret": false, "description": "Configuration of stream caching." }, "jmxAgent": { "kind": "element", "displayName": "JMX Agent", "required": false, "type": "object", "javaType": "org.apache.camel.core.xml.CamelJMXAgentDefinition", "deprecated": false, "secret": false, "description": "Configuration of JMX Agent." }, + "routeController": { "kind": "element", "displayName": "Route Controller", "required": false, "type": "object", "javaType": "org.apache.camel.core.xml.CamelRouteControllerDefinition", "deprecated": false, "secret": false }, "beansFactory": { "kind": "element", "displayName": "Beans Factory", "required": true, "type": "array", "javaType": "java.util.List<org.apache.camel.core.xml.AbstractCamelFactoryBean<java.lang.Object>>", "oneOf": [ "consumerTemplate", "fluentTemplate", "template" ], "deprecated": false, "secret": false, "description": "Miscellaneous configurations" }, "beans": { "kind": "element", "displayName": "Beans", "required": true, "type": "array", "javaType": "java.util.List<java.lang.Object>", "oneOf": [ "errorHandler", "export", "proxy" ], "deprecated": false, "secret": false, "description": "Miscellaneous configurations" }, "defaultServiceCallConfiguration": { "kind": "element", "displayName": "Default Service Call Configuration", "required": false, "type": "object", "javaType": "org.apache.camel.model.cloud.ServiceCallConfigurationDefinition", "deprecated": false, "secret": false, "description": "ServiceCall EIP default configuration" }, diff --git a/components/camel-spring/src/main/java/org/apache/camel/spring/CamelContextFactoryBean.java b/components/camel-spring/src/main/java/org/apache/camel/spring/CamelContextFactoryBean.java index 99bdc6e..f556e73 100644 --- a/components/camel-spring/src/main/java/org/apache/camel/spring/CamelContextFactoryBean.java +++ b/components/camel-spring/src/main/java/org/apache/camel/spring/CamelContextFactoryBean.java @@ -42,6 +42,7 @@ import org.apache.camel.core.xml.AbstractCamelFactoryBean; import org.apache.camel.core.xml.CamelJMXAgentDefinition; import org.apache.camel.core.xml.CamelPropertyPlaceholderDefinition; import org.apache.camel.core.xml.CamelProxyFactoryDefinition; +import org.apache.camel.core.xml.CamelRouteControllerDefinition; import org.apache.camel.core.xml.CamelServiceExporterDefinition; import org.apache.camel.core.xml.CamelStreamCachingStrategyDefinition; import org.apache.camel.model.ContextScanDefinition; @@ -173,6 +174,8 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Spr private CamelStreamCachingStrategyDefinition camelStreamCachingStrategy; @XmlElement(name = "jmxAgent", type = CamelJMXAgentDefinition.class) @Metadata(displayName = "JMX Agent") private CamelJMXAgentDefinition camelJMXAgent; + @XmlElement(name = "routeController", type = CamelRouteControllerDefinition.class) + private CamelRouteControllerDefinition camelRouteController; @XmlElements({ @XmlElement(name = "template", type = CamelProducerTemplateFactoryBean.class), @XmlElement(name = "fluentTemplate", type = CamelFluentProducerTemplateFactoryBean.class), @@ -679,6 +682,15 @@ public class CamelContextFactoryBean extends AbstractCamelContextFactoryBean<Spr this.camelStreamCachingStrategy = camelStreamCachingStrategy; } + @Override + public CamelRouteControllerDefinition getCamelRouteController() { + return camelRouteController; + } + + public void setCamelRouteController(CamelRouteControllerDefinition camelRouteController) { + this.camelRouteController = camelRouteController; + } + /** * Configuration of JMX Agent. */ diff --git a/components/camel-spring/src/main/java/org/apache/camel/spring/handler/CamelNamespaceHandler.java b/components/camel-spring/src/main/java/org/apache/camel/spring/handler/CamelNamespaceHandler.java index 89fe934..a2e0964 100644 --- a/components/camel-spring/src/main/java/org/apache/camel/spring/handler/CamelNamespaceHandler.java +++ b/components/camel-spring/src/main/java/org/apache/camel/spring/handler/CamelNamespaceHandler.java @@ -26,6 +26,7 @@ import javax.xml.bind.Binder; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; +import org.apache.camel.core.xml.CamelRouteControllerDefinition; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; @@ -149,6 +150,7 @@ public class CamelNamespaceHandler extends NamespaceHandlerSupport { addBeanDefinitionParser("jmxAgent", CamelJMXAgentDefinition.class, false, false); addBeanDefinitionParser("streamCaching", CamelStreamCachingStrategyDefinition.class, false, false); addBeanDefinitionParser("propertyPlaceholder", CamelPropertyPlaceholderDefinition.class, false, false); + addBeanDefinitionParser("routeController", CamelRouteControllerDefinition.class, false, false); // error handler could be the sub element of camelContext or defined outside camelContext BeanDefinitionParser errorHandlerParser = new ErrorHandlerDefinitionParser(); @@ -378,6 +380,7 @@ public class CamelNamespaceHandler extends NamespaceHandlerSupport { builder.addPropertyValue("camelPropertyPlaceholder", factoryBean.getCamelPropertyPlaceholder()); builder.addPropertyValue("camelJMXAgent", factoryBean.getCamelJMXAgent()); builder.addPropertyValue("camelStreamCachingStrategy", factoryBean.getCamelStreamCachingStrategy()); + builder.addPropertyValue("camelRouteController", factoryBean.getCamelRouteController()); builder.addPropertyValue("threadPoolProfiles", factoryBean.getThreadPoolProfiles()); builder.addPropertyValue("beansFactory", factoryBean.getBeansFactory()); builder.addPropertyValue("beans", factoryBean.getBeans()); diff --git a/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultSupervisingRouteControllerTest.java b/components/camel-spring/src/test/java/org/apache/camel/spring/impl/SpringSupervisingRouteControllerTest.java similarity index 57% copy from core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultSupervisingRouteControllerTest.java copy to components/camel-spring/src/test/java/org/apache/camel/spring/impl/SpringSupervisingRouteControllerTest.java index abb7ece..fd5fa54 100644 --- a/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultSupervisingRouteControllerTest.java +++ b/components/camel-spring/src/test/java/org/apache/camel/spring/impl/SpringSupervisingRouteControllerTest.java @@ -14,44 +14,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.impl.engine; +package org.apache.camel.spring.impl; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.camel.Consumer; -import org.apache.camel.ContextTestSupport; import org.apache.camel.Endpoint; import org.apache.camel.Processor; -import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.component.seda.SedaComponent; import org.apache.camel.component.seda.SedaConsumer; import org.apache.camel.component.seda.SedaEndpoint; import org.apache.camel.spi.SupervisingRouteController; +import org.apache.camel.spring.SpringTestSupport; +import org.springframework.context.support.AbstractXmlApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.junit.Test; -public class DefaultSupervisingRouteControllerTest extends ContextTestSupport { +public class SpringSupervisingRouteControllerTest extends SpringTestSupport { @Override - public boolean isUseRouteBuilder() { - return false; + protected AbstractXmlApplicationContext createApplicationContext() { + return new ClassPathXmlApplicationContext("org/apache/camel/spring/impl/SpringSupervisingRouteControllerTest.xml"); } @Test public void testSupervising() throws Exception { - // lets make a simple route - context.addRoutes(new MyRoute()); - - // configure supervising route controller - SupervisingRouteController src = context.getRouteController().supervising(); - src.setBackOffDelay(25); - src.setBackOffMaxAttempts(3); - src.setInitialDelay(100); - src.setThreadPoolSize(2); - - context.start(); - MockEndpoint mock = context.getEndpoint("mock:foo", MockEndpoint.class); mock.expectedMinimumMessageCount(3); @@ -72,6 +61,8 @@ public class DefaultSupervisingRouteControllerTest extends ContextTestSupport { // cake was not able to start assertEquals("Stopped", context.getRouteController().getRouteStatus("cake").toString()); + SupervisingRouteController src = context.getRouteController().adapt(SupervisingRouteController.class); + Throwable e = src.getRestartException("cake"); assertNotNull(e); assertEquals("Cannot start", e.getMessage()); @@ -81,59 +72,7 @@ public class DefaultSupervisingRouteControllerTest extends ContextTestSupport { assertEquals("Stopped", context.getRouteController().getRouteStatus("bar").toString()); } - @Test - public void testSupervisingOk() throws Exception { - // lets make a simple route - context.addRoutes(new MyRoute()); - - // configure supervising - SupervisingRouteController src = context.getRouteController().supervising(); - src.setBackOffDelay(25); - src.setBackOffMaxAttempts(10); - src.setInitialDelay(100); - src.setThreadPoolSize(2); - context.setRouteController(src); - - context.start(); - - MockEndpoint mock = context.getEndpoint("mock:foo", MockEndpoint.class); - mock.expectedMinimumMessageCount(3); - - MockEndpoint mock2 = context.getEndpoint("mock:cheese", MockEndpoint.class); - mock2.expectedMessageCount(0); - - MockEndpoint mock3 = context.getEndpoint("mock:cake", MockEndpoint.class); - mock3.expectedMessageCount(0); - - MockEndpoint mock4 = context.getEndpoint("mock:bar", MockEndpoint.class); - mock4.expectedMessageCount(0); - - MockEndpoint.assertIsSatisfied(5, TimeUnit.SECONDS, mock, mock2, mock3, mock4); - - // these should all start - assertEquals("Started", context.getRouteController().getRouteStatus("foo").toString()); - assertEquals("Started", context.getRouteController().getRouteStatus("cheese").toString()); - assertEquals("Started", context.getRouteController().getRouteStatus("cake").toString()); - // bar is no auto startup - assertEquals("Stopped", context.getRouteController().getRouteStatus("bar").toString()); - } - - private class MyRoute extends RouteBuilder { - @Override - public void configure() throws Exception { - getContext().addComponent("jms", new MyJmsComponent()); - - from("timer:foo").to("mock:foo").routeId("foo"); - - from("jms:cheese").to("mock:cheese").routeId("cheese"); - - from("jms:cake").to("mock:cake").routeId("cake"); - - from("seda:bar").routeId("bar").noAutoStartup().to("mock:bar"); - } - } - - private class MyJmsComponent extends SedaComponent { + public static class MyJmsComponent extends SedaComponent { @Override protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { @@ -141,7 +80,7 @@ public class DefaultSupervisingRouteControllerTest extends ContextTestSupport { } } - private class MyJmsEndpoint extends SedaEndpoint { + public static class MyJmsEndpoint extends SedaEndpoint { private String name; @@ -161,7 +100,7 @@ public class DefaultSupervisingRouteControllerTest extends ContextTestSupport { } } - private class MyJmsConsumer extends SedaConsumer { + public static class MyJmsConsumer extends SedaConsumer { private int counter; diff --git a/components/camel-spring/src/test/resources/org/apache/camel/spring/impl/SpringSupervisingRouteControllerTest.xml b/components/camel-spring/src/test/resources/org/apache/camel/spring/impl/SpringSupervisingRouteControllerTest.xml new file mode 100644 index 0000000..54a1bda --- /dev/null +++ b/components/camel-spring/src/test/resources/org/apache/camel/spring/impl/SpringSupervisingRouteControllerTest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd + "> + + <bean id="jms" class="org.apache.camel.spring.impl.SpringSupervisingRouteControllerTest.MyJmsComponent"/> + + <camelContext xmlns="http://camel.apache.org/schema/spring"> + <routeController id="myController" + supervising="true" initialDelay="100" threadPoolSize="2" backOffDelay="25" backOffMaxAttempts="3"/> + <route id="foo"> + <from uri="timer:foo"/> + <to uri="mock:foo"/> + </route> + <route id="cheese"> + <from uri="jms:cheese"/> + <to uri="mock:cheese"/> + </route> + <route id="cake"> + <from uri="jms:cake"/> + <to uri="mock:cake"/> + </route> + <route id="bar" autoStartup="false"> + <from uri="seda:bar"/> + <to uri="mock:bar"/> + </route> + </camelContext> + +</beans> diff --git a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultSupervisingRouteController.java b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultSupervisingRouteController.java index b62219f..8944856 100644 --- a/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultSupervisingRouteController.java +++ b/core/camel-base/src/main/java/org/apache/camel/impl/engine/DefaultSupervisingRouteController.java @@ -181,16 +181,10 @@ public class DefaultSupervisingRouteController extends DefaultRouteController im @Override protected void doInit() throws Exception { - this.backOff = new BackOff( - Duration.ofMillis(backOffDelay), - backOffMaxDelay > 0 ? Duration.ofMillis(backOffMaxDelay) : Duration.ofMillis(Long.MAX_VALUE), - backOffMaxElapsedTime > 0 ? Duration.ofMillis(backOffMaxElapsedTime) : Duration.ofMillis(Long.MAX_VALUE), - backOffMaxAttempts > 0 ? backOffMaxAttempts : Long.MAX_VALUE, - backOffMultiplier); this.listener = new CamelContextStartupListener(); - CamelContext context = getCamelContext(); // prevent routes from automatic being started by default + CamelContext context = getCamelContext(); context.setAutoStartup(false); context.addRoutePolicyFactory(new ManagedRoutePolicyFactory()); context.addStartupListener(this.listener); @@ -198,6 +192,13 @@ public class DefaultSupervisingRouteController extends DefaultRouteController im @Override protected void doStart() throws Exception { + this.backOff = new BackOff( + Duration.ofMillis(backOffDelay), + backOffMaxDelay > 0 ? Duration.ofMillis(backOffMaxDelay) : Duration.ofMillis(Long.MAX_VALUE), + backOffMaxElapsedTime > 0 ? Duration.ofMillis(backOffMaxElapsedTime) : Duration.ofMillis(Long.MAX_VALUE), + backOffMaxAttempts > 0 ? backOffMaxAttempts : Long.MAX_VALUE, + backOffMultiplier); + CamelContext context = getCamelContext(); if (threadPoolSize == 1) { executorService = context.getExecutorServiceManager().newSingleThreadScheduledExecutor(this, "SupervisingRouteController"); diff --git a/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/jaxb.index b/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/jaxb.index index d0e2b70..97912b3 100644 --- a/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/jaxb.index +++ b/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/jaxb.index @@ -4,5 +4,6 @@ CamelPropertyPlaceholderDefinition CamelPropertyPlaceholderFunctionDefinition CamelPropertyPlaceholderLocationDefinition CamelProxyFactoryDefinition +CamelRouteControllerDefinition CamelServiceExporterDefinition CamelStreamCachingStrategyDefinition diff --git a/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/routeController.json b/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/routeController.json new file mode 100644 index 0000000..1d6a9df --- /dev/null +++ b/core/camel-core-xml/src/generated/resources/org/apache/camel/core/xml/routeController.json @@ -0,0 +1,26 @@ +{ + "model": { + "kind": "model", + "name": "routeController", + "title": "Route Controller", + "description": "Route controller configuration.", + "deprecated": false, + "label": "spring,configuration", + "javaType": "org.apache.camel.core.xml.CamelRouteControllerDefinition", + "input": false, + "output": false + }, + "properties": { + "supervising": { "kind": "attribute", "displayName": "Supervising", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "defaultValue": "false", "description": "To enable using supervising route controller which allows Camel to startup and then the controller takes care of starting the routes in a safe manner. This can be used when you want to startup Camel despite a route may otherwise fail fast during startup and cause Camel to [...] + "includeRoutes": { "kind": "attribute", "displayName": "Include Routes", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Pattern for filtering routes to be included as supervised. The pattern is matching on route id, and endpoint uri for the route. Multiple patterns can be separated by comma. For example to include all kafka routes, you can say kafka:. And to include routes with specific route ids myRoute,myOt [...] + "excludeRoutes": { "kind": "attribute", "displayName": "Exclude Routes", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Pattern for filtering routes to be excluded as supervised. The pattern is matching on route id, and endpoint uri for the route. Multiple patterns can be separated by comma. For example to exclude all JMS routes, you can say jms:. And to exclude routes with specific route ids mySpecialRoute,m [...] + "threadPoolSize": { "kind": "attribute", "displayName": "Thread Pool Size", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "defaultValue": "1", "description": "The number of threads used by the scheduled thread pool that are used for restarting routes. The pool uses 1 thread by default, but you can increase this to allow the controller to concurrently attempt to restart multiple routes in case more than one route has problem [...] + "initialDelay": { "kind": "attribute", "displayName": "Initial Delay", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Initial delay in milli seconds before the route controller starts, after CamelContext has been started." }, + "backOffDelay": { "kind": "attribute", "displayName": "Back Off Delay", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "defaultValue": "2000", "description": "Backoff delay in millis when restarting a route that failed to startup." }, + "backOffMaxDelay": { "kind": "attribute", "displayName": "Back Off Max Delay", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Backoff maximum delay in millis when restarting a route that failed to startup." }, + "backOffMaxElapsedTime": { "kind": "attribute", "displayName": "Back Off Max Elapsed Time", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Backoff maximum elapsed time in millis, after which the backoff should be considered exhausted and no more attempts should be made." }, + "backOffMaxAttempts": { "kind": "attribute", "displayName": "Back Off Max Attempts", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "Backoff maximum number of attempts to restart a route that failed to startup. When this threshold has been exceeded then the controller will give up attempting to restart the route, and the route will remain as stopped." }, + "backOffMultiplier": { "kind": "attribute", "displayName": "Back Off Multiplier", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "defaultValue": "1.0", "description": "Backoff multiplier to use for exponential backoff. This is used to extend the delay between restart attempts." }, + "id": { "kind": "attribute", "displayName": "Id", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "secret": false, "description": "The id of this node" } + } +} diff --git a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java index cec0b69..ba4457c 100644 --- a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java +++ b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBean.java @@ -117,6 +117,7 @@ import org.apache.camel.spi.RoutePolicyFactory; import org.apache.camel.spi.RuntimeEndpointRegistry; import org.apache.camel.spi.ShutdownStrategy; import org.apache.camel.spi.StreamCachingStrategy; +import org.apache.camel.spi.SupervisingRouteController; import org.apache.camel.spi.ThreadPoolFactory; import org.apache.camel.spi.ThreadPoolProfile; import org.apache.camel.spi.Transformer; @@ -382,12 +383,6 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex LOG.info("Using HealthCheckService: {}", healthCheckService); getContext().addService(healthCheckService); } - // Route controller - RouteController routeController = getBeanForType(RouteController.class); - if (routeController != null) { - LOG.info("Using RouteController: {}", routeController); - getContext().setRouteController(routeController); - } // UuidGenerator UuidGenerator uuidGenerator = getBeanForType(UuidGenerator.class); if (uuidGenerator != null) { @@ -417,6 +412,9 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex // init stream caching strategy initStreamCachingStrategy(); + + // init route controller + initRouteController(); } //CHECKSTYLE:ON @@ -647,6 +645,64 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex } } + protected void initRouteController() throws Exception { + // Route controller + RouteController routeController = getBeanForType(RouteController.class); + if (routeController != null) { + LOG.info("Using RouteController: {}", routeController); + getContext().setRouteController(routeController); + // we are using custom so dont attempt to use the default below + return; + } + + CamelRouteControllerDefinition rc = getCamelRouteController(); + if (rc == null) { + return; + } + + SupervisingRouteController src = null; + Boolean enabled = CamelContextHelper.parseBoolean(getContext(), rc.getSupervising()); + if (enabled != null) { + src = getContext().getRouteController().supervising(); + } + String includeRoutes = CamelContextHelper.parseText(getContext(), rc.getIncludeRoutes()); + if (includeRoutes != null && src != null) { + src.setIncludeRoutes(includeRoutes); + } + String excludeRoutes = CamelContextHelper.parseText(getContext(), rc.getExcludeRoutes()); + if (excludeRoutes != null && src != null) { + src.setExcludeRoutes(excludeRoutes); + } + Integer threadPoolSize = CamelContextHelper.parseInteger(getContext(), rc.getThreadPoolSize()); + if (threadPoolSize != null && src != null) { + src.setThreadPoolSize(threadPoolSize); + } + Long initialDelay = CamelContextHelper.parseLong(getContext(), rc.getInitialDelay()); + if (initialDelay != null && src != null) { + src.setInitialDelay(initialDelay); + } + Long backoffDelay = CamelContextHelper.parseLong(getContext(), rc.getBackOffDelay()); + if (backoffDelay != null && src != null) { + src.setBackOffDelay(backoffDelay); + } + Long backOffMaxDelay = CamelContextHelper.parseLong(getContext(), rc.getBackOffMaxDelay()); + if (backOffMaxDelay != null && src != null) { + src.setBackOffMaxDelay(backOffMaxDelay); + } + Long backOffMaxElapsedTime = CamelContextHelper.parseLong(getContext(), rc.getBackOffMaxElapsedTime()); + if (backOffMaxElapsedTime != null && src != null) { + src.setBackOffMaxElapsedTime(backOffMaxElapsedTime); + } + Long backOffMaxAttempts = CamelContextHelper.parseLong(getContext(), rc.getBackOffMaxAttempts()); + if (backOffMaxAttempts != null && src != null) { + src.setBackOffMaxAttempts(backOffMaxAttempts); + } + Double backOffMultiplier = CamelContextHelper.parseDouble(getContext(), rc.getBackOffMultiplier()); + if (backOffMultiplier != null && src != null) { + src.setBackOffMultiplier(backOffMultiplier); + } + } + protected void initPropertyPlaceholder() throws Exception { if (getCamelPropertyPlaceholder() != null) { CamelPropertyPlaceholderDefinition def = getCamelPropertyPlaceholder(); @@ -825,6 +881,8 @@ public abstract class AbstractCamelContextFactoryBean<T extends ModelCamelContex public abstract CamelStreamCachingStrategyDefinition getCamelStreamCachingStrategy(); + public abstract CamelRouteControllerDefinition getCamelRouteController(); + public abstract List<RouteBuilderDefinition> getBuilderRefs(); public abstract List<RouteContextRefDefinition> getRouteRefs(); diff --git a/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/CamelRouteControllerDefinition.java b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/CamelRouteControllerDefinition.java new file mode 100644 index 0000000..c79142a --- /dev/null +++ b/core/camel-core-xml/src/main/java/org/apache/camel/core/xml/CamelRouteControllerDefinition.java @@ -0,0 +1,197 @@ +/* + * 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.core.xml; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +import org.apache.camel.model.IdentifiedType; +import org.apache.camel.spi.Metadata; + +/** + * Route controller configuration. + */ +@Metadata(label = "spring,configuration") +@XmlRootElement(name = "routeController") +@XmlAccessorType(XmlAccessType.FIELD) +public class CamelRouteControllerDefinition extends IdentifiedType { + + @XmlAttribute @Metadata(defaultValue = "false") + private String supervising; + @XmlAttribute + private String includeRoutes; + @XmlAttribute + private String excludeRoutes; + @XmlAttribute @Metadata(defaultValue = "1") + private String threadPoolSize; + @XmlAttribute + private String initialDelay; + @XmlAttribute @Metadata(defaultValue = "2000") + private String backOffDelay; + @XmlAttribute + private String backOffMaxDelay; + @XmlAttribute + private String backOffMaxElapsedTime; + @XmlAttribute + private String backOffMaxAttempts; + @XmlAttribute @Metadata(defaultValue = "1.0") + private String backOffMultiplier; + + public String getSupervising() { + return supervising; + } + + /** + * To enable using supervising route controller which allows Camel to startup + * and then the controller takes care of starting the routes in a safe manner. + * + * This can be used when you want to startup Camel despite a route may otherwise + * fail fast during startup and cause Camel to fail to startup as well. By delegating + * the route startup to the supervising route controller then its manages the startup + * using a background thread. The controller allows to be configured with various + * settings to attempt to restart failing routes. + */ + public void setSupervising(String supervising) { + this.supervising = supervising; + } + + public String getIncludeRoutes() { + return includeRoutes; + } + + /** + * Pattern for filtering routes to be included as supervised. + * + * The pattern is matching on route id, and endpoint uri for the route. + * Multiple patterns can be separated by comma. + * + * For example to include all kafka routes, you can say <tt>kafka:*</tt>. + * And to include routes with specific route ids <tt>myRoute,myOtherRoute</tt>. + * The pattern supports wildcards and uses the matcher from + * org.apache.camel.support.PatternHelper#matchPattern. + */ + public void setIncludeRoutes(String includeRoutes) { + this.includeRoutes = includeRoutes; + } + + public String getExcludeRoutes() { + return excludeRoutes; + } + + /** + * Pattern for filtering routes to be excluded as supervised. + * + * The pattern is matching on route id, and endpoint uri for the route. + * Multiple patterns can be separated by comma. + * + * For example to exclude all JMS routes, you can say <tt>jms:*</tt>. + * And to exclude routes with specific route ids <tt>mySpecialRoute,myOtherSpecialRoute</tt>. + * The pattern supports wildcards and uses the matcher from + * org.apache.camel.support.PatternHelper#matchPattern. + */ + public void setExcludeRoutes(String excludeRoutes) { + this.excludeRoutes = excludeRoutes; + } + + public String getThreadPoolSize() { + return threadPoolSize; + } + + /** + * The number of threads used by the scheduled thread pool that are used for restarting + * routes. The pool uses 1 thread by default, but you can increase this to allow the controller + * to concurrently attempt to restart multiple routes in case more than one route has problems + * starting. + */ + public void setThreadPoolSize(String threadPoolSize) { + this.threadPoolSize = threadPoolSize; + } + + public String getInitialDelay() { + return initialDelay; + } + + /** + * Initial delay in milli seconds before the route controller starts, after + * CamelContext has been started. + */ + public void setInitialDelay(String initialDelay) { + this.initialDelay = initialDelay; + } + + public String getBackOffDelay() { + return backOffDelay; + } + + /** + * Backoff delay in millis when restarting a route that failed to startup. + */ + public void setBackOffDelay(String backOffDelay) { + this.backOffDelay = backOffDelay; + } + + public String getBackOffMaxDelay() { + return backOffMaxDelay; + } + + /** + * Backoff maximum delay in millis when restarting a route that failed to startup. + */ + public void setBackOffMaxDelay(String backOffMaxDelay) { + this.backOffMaxDelay = backOffMaxDelay; + } + + public String getBackOffMaxElapsedTime() { + return backOffMaxElapsedTime; + } + + /** + * Backoff maximum elapsed time in millis, after which the backoff should be considered + * exhausted and no more attempts should be made. + */ + public void setBackOffMaxElapsedTime(String backOffMaxElapsedTime) { + this.backOffMaxElapsedTime = backOffMaxElapsedTime; + } + + public String getBackOffMaxAttempts() { + return backOffMaxAttempts; + } + + /** + * Backoff maximum number of attempts to restart a route that failed to startup. + * When this threshold has been exceeded then the controller will give up + * attempting to restart the route, and the route will remain as stopped. + */ + public void setBackOffMaxAttempts(String backOffMaxAttempts) { + this.backOffMaxAttempts = backOffMaxAttempts; + } + + public String getBackOffMultiplier() { + return backOffMultiplier; + } + + /** + * Backoff multiplier to use for exponential backoff. This is used to extend the delay + * between restart attempts. + */ + public void setBackOffMultiplier(String backOffMultiplier) { + this.backOffMultiplier = backOffMultiplier; + } + +} diff --git a/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultSupervisingRouteControllerTest.java b/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultSupervisingRouteControllerTest.java index abb7ece..e3ddb86 100644 --- a/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultSupervisingRouteControllerTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/impl/engine/DefaultSupervisingRouteControllerTest.java @@ -92,7 +92,6 @@ public class DefaultSupervisingRouteControllerTest extends ContextTestSupport { src.setBackOffMaxAttempts(10); src.setInitialDelay(100); src.setThreadPoolSize(2); - context.setRouteController(src); context.start();