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
commit 5c6b2d3c3212757182b1b9e728ec3c1dcd2848aa Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Fri Oct 28 09:39:50 2022 +0200 CAMEL-18614: camel-core - Updating route configuration via routes loader --- .../apache/camel/RouteConfigurationsBuilder.java | 8 ++ .../camel/impl/engine/DefaultRoutesLoader.java | 9 ++ .../org/apache/camel/impl/DefaultCamelContext.java | 16 +++ .../java/org/apache/camel/impl/DefaultModel.java | 17 +++ .../camel/impl/lw/LightweightCamelContext.java | 10 ++ .../camel/builder/RouteConfigurationBuilder.java | 18 +++ .../main/java/org/apache/camel/model/Model.java | 16 +++ .../camel/model/RoutesConfigurationUpdateTest.java | 149 +++++++++++++++++++++ 8 files changed, 243 insertions(+) diff --git a/core/camel-api/src/main/java/org/apache/camel/RouteConfigurationsBuilder.java b/core/camel-api/src/main/java/org/apache/camel/RouteConfigurationsBuilder.java index d9e95d599b0..b9761480791 100644 --- a/core/camel-api/src/main/java/org/apache/camel/RouteConfigurationsBuilder.java +++ b/core/camel-api/src/main/java/org/apache/camel/RouteConfigurationsBuilder.java @@ -29,4 +29,12 @@ public interface RouteConfigurationsBuilder { */ void addRouteConfigurationsToCamelContext(CamelContext context) throws Exception; + /** + * Adds or updates the route configurations from this builder to the CamelContext. + * + * @param context the Camel context + * @throws Exception is thrown if initialization of route configurations failed + */ + void updateRouteConfigurationsToCamelContext(CamelContext context) throws Exception; + } diff --git a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java index 22864c346dd..32e0c46c419 100644 --- a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java @@ -28,6 +28,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.camel.CamelContext; import org.apache.camel.CamelContextAware; import org.apache.camel.ExtendedCamelContext; +import org.apache.camel.RouteConfigurationsBuilder; import org.apache.camel.RoutesBuilder; import org.apache.camel.StaticService; import org.apache.camel.spi.ExtendedRoutesBuilderLoader; @@ -178,6 +179,14 @@ public class DefaultRoutesLoader extends ServiceSupport implements RoutesLoader, Set<String> answer = new LinkedHashSet<>(); Collection<RoutesBuilder> builders = findRoutesBuilders(resources); + for (RoutesBuilder builder : builders) { + // update any existing route configurations first + if (builder instanceof RouteConfigurationsBuilder) { + RouteConfigurationsBuilder rcb = (RouteConfigurationsBuilder) builder; + rcb.updateRouteConfigurationsToCamelContext(getCamelContext()); + } + } + for (RoutesBuilder builder : builders) { // update any existing routes Set<String> ids = builder.updateRoutesToCamelContext(getCamelContext()); diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java index 253f1d6c322..e9c2b30bc0e 100644 --- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java +++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java @@ -327,6 +327,22 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame return model.getRouteConfigurationDefinitions(); } + @Override + public RouteConfigurationDefinition getRouteConfigurationDefinition(String id) { + if (model == null && isLightweight()) { + throw new IllegalStateException("Access to model not supported in lightweight mode"); + } + return model.getRouteConfigurationDefinition(id); + } + + @Override + public void removeRouteConfiguration(RouteConfigurationDefinition routeConfigurationDefinition) throws Exception { + if (model == null && isLightweight()) { + throw new IllegalStateException("Access to model not supported in lightweight mode"); + } + model.removeRouteConfiguration(routeConfigurationDefinition); + } + @Override public List<RouteDefinition> getRouteDefinitions() { if (model == null && isLightweight()) { diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java index 380af8b5b8f..be04596304b 100644 --- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java +++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java @@ -160,6 +160,23 @@ public class DefaultModel implements Model { return routesConfigurations; } + @Override + public synchronized RouteConfigurationDefinition getRouteConfigurationDefinition(String id) { + for (RouteConfigurationDefinition def : routesConfigurations) { + if (def.idOrCreate(camelContext.adapt(ExtendedCamelContext.class).getNodeIdFactory()).equals(id)) { + return def; + } + } + // you can have a global route configuration that has no ID assigned + return routesConfigurations.stream().filter(c -> c.getId() == null).findFirst().orElse(null); + } + + @Override + public void removeRouteConfiguration(RouteConfigurationDefinition routeConfigurationDefinition) throws Exception { + RouteConfigurationDefinition toBeRemoved = getRouteConfigurationDefinition(routeConfigurationDefinition.getId()); + this.routesConfigurations.remove(toBeRemoved); + } + @Override public synchronized void addRouteDefinitions(Collection<RouteDefinition> routeDefinitions) throws Exception { if (routeDefinitions == null || routeDefinitions.isEmpty()) { diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java index 30e22f79638..53a5f2f2116 100644 --- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java +++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/lw/LightweightCamelContext.java @@ -1875,6 +1875,16 @@ public class LightweightCamelContext implements ExtendedCamelContext, CatalogCam return getModelCamelContext().getRouteConfigurationDefinitions(); } + @Override + public void removeRouteConfiguration(RouteConfigurationDefinition routeConfigurationDefinition) throws Exception { + getModelCamelContext().removeRouteConfiguration(routeConfigurationDefinition); + } + + @Override + public RouteConfigurationDefinition getRouteConfigurationDefinition(String id) { + return getModelCamelContext().getRouteConfigurationDefinition(id); + } + @Override public List<RouteDefinition> getRouteDefinitions() { return getModelCamelContext().getRouteDefinitions(); diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteConfigurationBuilder.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteConfigurationBuilder.java index 9b7b43e70c6..0b22d14404f 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteConfigurationBuilder.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteConfigurationBuilder.java @@ -16,6 +16,7 @@ */ package org.apache.camel.builder; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.camel.CamelContext; @@ -79,6 +80,23 @@ public abstract class RouteConfigurationBuilder extends RouteBuilder implements populateRoutesConfiguration(); } + @Override + public void updateRouteConfigurationsToCamelContext(CamelContext context) throws Exception { + setCamelContext(context); + routeConfigurationCollection.setCamelContext(context); + if (initializedConfiguration.compareAndSet(false, true)) { + configuration(); + } + List<RouteConfigurationDefinition> list = getRouteConfigurationCollection().getRouteConfigurations(); + if (!list.isEmpty()) { + // remove existing before updating + for (RouteConfigurationDefinition def : list) { + context.getExtension(Model.class).removeRouteConfiguration(def); + } + populateRoutesConfiguration(); + } + } + protected void populateRoutesConfiguration() throws Exception { CamelContext camelContext = getContext(); if (camelContext == null) { diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/Model.java b/core/camel-core-model/src/main/java/org/apache/camel/model/Model.java index 84d0792d95e..ae3528bd3de 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/Model.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/Model.java @@ -71,6 +71,22 @@ public interface Model { */ List<RouteConfigurationDefinition> getRouteConfigurationDefinitions(); + /** + * Removes a route configuration from the context + * + * @param routeConfigurationDefinition route configuration to remove + * @throws Exception if the route configuration could not be removed for whatever reason + */ + void removeRouteConfiguration(RouteConfigurationDefinition routeConfigurationDefinition) throws Exception; + + /** + * Gets the route configuration definition with the given id + * + * @param id id of the route configuration + * @return the route configuration definition or <tt>null</tt> if not found + */ + RouteConfigurationDefinition getRouteConfigurationDefinition(String id); + /** * Returns a list of the current route definitions * diff --git a/core/camel-core/src/test/java/org/apache/camel/model/RoutesConfigurationUpdateTest.java b/core/camel-core/src/test/java/org/apache/camel/model/RoutesConfigurationUpdateTest.java new file mode 100644 index 00000000000..d1ecbed63dc --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/model/RoutesConfigurationUpdateTest.java @@ -0,0 +1,149 @@ +/* + * 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.model; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.builder.RouteConfigurationBuilder; +import org.junit.jupiter.api.Test; + +public class RoutesConfigurationUpdateTest extends ContextTestSupport { + + @Override + public boolean isUseRouteBuilder() { + return false; + } + + @Test + public void testRoutesConfigurationUpdate() throws Exception { + context.start(); + + RouteConfigurationBuilder rcb = new RouteConfigurationBuilder() { + @Override + public void configuration() throws Exception { + routeConfiguration("myConfig").onException(Exception.class).handled(true).to("mock:error"); + } + }; + rcb.addRouteConfigurationsToCamelContext(context); + RouteBuilder rb = new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start").routeId("start").routeConfigurationId("myConfig") + .throwException(new IllegalArgumentException("Foo")); + + from("direct:start2").routeId("start2").routeConfigurationId("myConfig") + .throwException(new IllegalArgumentException("Foo2")); + } + }; + rb.addRoutesToCamelContext(context); + + getMockEndpoint("mock:error").expectedBodiesReceived("Hello World", "Bye World"); + getMockEndpoint("mock:error2").expectedMessageCount(0); + template.sendBody("direct:start", "Hello World"); + template.sendBody("direct:start2", "Bye World"); + assertMockEndpointsSatisfied(); + + // update route configuration and routes (remove routes first) + context.getRouteController().removeAllRoutes(); + RouteConfigurationBuilder rcb2 = new RouteConfigurationBuilder() { + @Override + public void configuration() throws Exception { + routeConfiguration("myConfig").onException(Exception.class).handled(true).to("mock:error2"); + } + }; + rcb2.updateRouteConfigurationsToCamelContext(context); + RouteBuilder rb2 = new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start").routeId("start").routeConfigurationId("myConfig") + .throwException(new IllegalArgumentException("Foo")); + + from("direct:start2").routeId("start2").routeConfigurationId("myConfig") + .throwException(new IllegalArgumentException("Foo2")); + } + }; + rb2.updateRoutesToCamelContext(context); + + resetMocks(); + + getMockEndpoint("mock:error").expectedMessageCount(0); + getMockEndpoint("mock:error2").expectedBodiesReceived("Hello World2", "Bye World2"); + template.sendBody("direct:start", "Hello World2"); + template.sendBody("direct:start2", "Bye World2"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testRoutesConfigurationGlobalUpdate() throws Exception { + context.start(); + + RouteConfigurationBuilder rcb = new RouteConfigurationBuilder() { + @Override + public void configuration() throws Exception { + routeConfiguration().onException(Exception.class).handled(true).to("mock:error"); + } + }; + rcb.addRouteConfigurationsToCamelContext(context); + RouteBuilder rb = new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start").routeId("start") + .throwException(new IllegalArgumentException("Foo")); + + from("direct:start2").routeId("start2") + .throwException(new IllegalArgumentException("Foo2")); + } + }; + rb.addRoutesToCamelContext(context); + + getMockEndpoint("mock:error").expectedBodiesReceived("Hello World", "Bye World"); + getMockEndpoint("mock:error2").expectedMessageCount(0); + template.sendBody("direct:start", "Hello World"); + template.sendBody("direct:start2", "Bye World"); + assertMockEndpointsSatisfied(); + + // update route configuration and routes (remove routes first) + context.getRouteController().removeAllRoutes(); + RouteConfigurationBuilder rcb2 = new RouteConfigurationBuilder() { + @Override + public void configuration() throws Exception { + routeConfiguration().onException(Exception.class).handled(true).to("mock:error2"); + } + }; + rcb2.updateRouteConfigurationsToCamelContext(context); + RouteBuilder rb2 = new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start").routeId("start") + .throwException(new IllegalArgumentException("Foo")); + + from("direct:start2").routeId("start2") + .throwException(new IllegalArgumentException("Foo2")); + } + }; + rb2.updateRoutesToCamelContext(context); + + resetMocks(); + + getMockEndpoint("mock:error").expectedMessageCount(0); + getMockEndpoint("mock:error2").expectedBodiesReceived("Hello World2", "Bye World2"); + template.sendBody("direct:start", "Hello World2"); + template.sendBody("direct:start2", "Bye World2"); + assertMockEndpointsSatisfied(); + } + +}