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 8f142df68a6859f2c78b79369da58874ab09e31e Author: Claus Ibsen <[email protected]> AuthorDate: Mon Mar 28 11:57:10 2022 +0200 CAMEL-17784: ExtendedRoutesBuilderLoader SPI which allows routes loader to load multiple routes in the same unit. In preparation for camel-java-joor-dsl to compile all java sources together. --- .../camel/spi/ExtendedRoutesBuilderLoader.java | 21 ++++++++ .../java/org/apache/camel/spi/ResourceAware.java | 44 +++++++++++++++++ .../camel/impl/engine/DefaultRoutesLoader.java | 50 ++++++++++++------- .../org/apache/camel/builder/RouteBuilder.java | 3 +- .../org/apache/camel/model/RouteDefinition.java | 4 +- .../org/apache/camel/model/RoutesDefinition.java | 3 +- .../support/ExtendedRouteBuilderLoaderSupport.java | 51 ++++++++++++++++++++ .../dsl/java/joor/JavaRoutesBuilderLoader.java | 56 ++++++++++++++-------- 8 files changed, 193 insertions(+), 39 deletions(-) diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ExtendedRoutesBuilderLoader.java b/core/camel-api/src/main/java/org/apache/camel/spi/ExtendedRoutesBuilderLoader.java new file mode 100644 index 0000000..3c3a016 --- /dev/null +++ b/core/camel-api/src/main/java/org/apache/camel/spi/ExtendedRoutesBuilderLoader.java @@ -0,0 +1,21 @@ +package org.apache.camel.spi; + +import java.util.Collection; + +import org.apache.camel.RoutesBuilder; + +/** + * An extended {@link RoutesBuilderLoader} that is capable of loading from multiple resources in one unit (such as + * compiling them together). + */ +public interface ExtendedRoutesBuilderLoader extends RoutesBuilderLoader { + + /** + * Loads {@link RoutesBuilder} from multiple {@link Resource}s. + * + * @param resources the resources to be loaded. + * @return a set of loaded {@link RoutesBuilder}s + */ + Collection<RoutesBuilder> loadRoutesBuilders(Collection<Resource> resources) throws Exception; + +} diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ResourceAware.java b/core/camel-api/src/main/java/org/apache/camel/spi/ResourceAware.java new file mode 100644 index 0000000..f02b8dc --- /dev/null +++ b/core/camel-api/src/main/java/org/apache/camel/spi/ResourceAware.java @@ -0,0 +1,44 @@ +/* + * 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.spi; + +/** + * An interface to represent an object which wishes to be injected with the {@link Resource} + */ +public interface ResourceAware { + + /** + * Set the {@link Resource} resource if the object is an instance of {@link ResourceAware}. + */ + static <T> T trySetResource(T object, Resource resource) { + if (resource != null && object instanceof ResourceAware) { + ((ResourceAware) object).setResource(resource); + } + + return object; + } + + /** + * Gets the {@link Resource}. + */ + Resource getResource(); + + /** + * Sets the {@link Resource}. + */ + void setResource(Resource resource); +} 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 0f4737f..4ff9f39 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 @@ -18,6 +18,7 @@ package org.apache.camel.impl.engine; import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -29,6 +30,7 @@ import org.apache.camel.CamelContextAware; import org.apache.camel.ExtendedCamelContext; import org.apache.camel.RoutesBuilder; import org.apache.camel.StaticService; +import org.apache.camel.spi.ExtendedRoutesBuilderLoader; import org.apache.camel.spi.FactoryFinder; import org.apache.camel.spi.ModelineFactory; import org.apache.camel.spi.Resource; @@ -86,20 +88,44 @@ public class DefaultRoutesLoader extends ServiceSupport implements RoutesLoader, public Collection<RoutesBuilder> findRoutesBuilders(Collection<Resource> resources) throws Exception { List<RoutesBuilder> answer = new ArrayList<>(resources.size()); - for (Resource resource : resources) { - RoutesBuilderLoader loader = resolveRoutesBuilderLoader(resource); - - if (camelContext.isModeline()) { - ModelineFactory factory = camelContext.adapt(ExtendedCamelContext.class).getModelineFactory(); + // first we need to parse for modeline to gather all the configurations + if (camelContext.isModeline()) { + ModelineFactory factory = camelContext.adapt(ExtendedCamelContext.class).getModelineFactory(); + for (Resource resource : resources) { + RoutesBuilderLoader loader = resolveRoutesBuilderLoader(resource); // gather resources for modeline factory.parseModeline(resource); // pre-parse before loading loader.preParseRoute(resource); } + } + + // now group resources by loader + Map<RoutesBuilderLoader, List<Resource>> groups = new LinkedHashMap<>(); + for (Resource resource : resources) { + RoutesBuilderLoader loader = resolveRoutesBuilderLoader(resource); + List<Resource> list = groups.getOrDefault(loader, new ArrayList<Resource>()); + list.add(resource); + groups.put(loader, list); + } - RoutesBuilder builder = loader.loadRoutesBuilder(resource); - if (builder != null) { - answer.add(builder); + // now load all the same resources for each loader + for (Map.Entry<RoutesBuilderLoader, List<Resource>> entry : groups.entrySet()) { + RoutesBuilderLoader loader = entry.getKey(); + if (loader instanceof ExtendedRoutesBuilderLoader) { + // extended loader can load all resources ine one unit + ExtendedRoutesBuilderLoader extLoader = (ExtendedRoutesBuilderLoader) loader; + Collection<RoutesBuilder> builders = extLoader.loadRoutesBuilders(entry.getValue()); + if (builders != null) { + answer.addAll(builders); + } + } else { + for (Resource resource : entry.getValue()) { + RoutesBuilder builder = loader.loadRoutesBuilder(resource); + if (builder != null) { + answer.add(builder); + } + } } } @@ -156,14 +182,6 @@ public class DefaultRoutesLoader extends ServiceSupport implements RoutesLoader, Set<String> answer = new LinkedHashSet<>(); Collection<RoutesBuilder> builders = findRoutesBuilders(resources); - if (camelContext.isModeline()) { - ModelineFactory factory = camelContext.adapt(ExtendedCamelContext.class).getModelineFactory(); - // gather resources for modeline - for (Resource resource : resources) { - factory.parseModeline(resource); - } - } - for (RoutesBuilder builder : builders) { // update any existing routes Set<String> ids = builder.updateRoutesToCamelContext(getCamelContext()); diff --git a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java index 26411e97..d661d53 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/builder/RouteBuilder.java @@ -51,6 +51,7 @@ import org.apache.camel.model.rest.RestsDefinition; import org.apache.camel.spi.OnCamelContextEvent; import org.apache.camel.spi.PropertiesComponent; import org.apache.camel.spi.Resource; +import org.apache.camel.spi.ResourceAware; import org.apache.camel.spi.RestConfiguration; import org.apache.camel.support.LifecycleStrategySupport; import org.apache.camel.util.ObjectHelper; @@ -64,7 +65,7 @@ import org.slf4j.LoggerFactory; * A <a href="http://camel.apache.org/dsl.html">Java DSL</a> which is used to build {@link Route} instances in a * {@link CamelContext} for smart routing. */ -public abstract class RouteBuilder extends BuilderSupport implements RoutesBuilder, Ordered { +public abstract class RouteBuilder extends BuilderSupport implements RoutesBuilder, Ordered, ResourceAware { protected Logger log = LoggerFactory.getLogger(getClass()); private Resource resource; diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java index c7e2d3e..a0bd0c2 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java @@ -48,6 +48,7 @@ import org.apache.camel.model.rest.RestDefinition; import org.apache.camel.spi.AsEndpointUri; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.Resource; +import org.apache.camel.spi.ResourceAware; import org.apache.camel.spi.RoutePolicy; /** @@ -58,7 +59,8 @@ import org.apache.camel.spi.RoutePolicy; @XmlType(propOrder = { "input", "inputType", "outputType", "outputs", "routeProperties" }) @XmlAccessorType(XmlAccessType.PROPERTY) // must use XmlAccessType.PROPERTY as there is some custom logic needed to be executed in the setter methods -public class RouteDefinition extends OutputDefinition<RouteDefinition> implements NamedRoute, PreconditionContainer { +public class RouteDefinition extends OutputDefinition<RouteDefinition> + implements NamedRoute, PreconditionContainer, ResourceAware { private final AtomicBoolean prepared = new AtomicBoolean(); private FromDefinition input; private String routeConfigurationId; diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java index 2ce18c6..f53235d 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RoutesDefinition.java @@ -33,6 +33,7 @@ import org.apache.camel.builder.EndpointConsumerBuilder; import org.apache.camel.spi.AsEndpointUri; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.Resource; +import org.apache.camel.spi.ResourceAware; import org.apache.camel.support.OrderedComparator; import org.apache.camel.support.PatternHelper; import org.slf4j.Logger; @@ -45,7 +46,7 @@ import org.slf4j.LoggerFactory; @XmlRootElement(name = "routes") @XmlAccessorType(XmlAccessType.FIELD) public class RoutesDefinition extends OptionalIdentifiedDefinition<RoutesDefinition> - implements RouteContainer, CamelContextAware { + implements RouteContainer, CamelContextAware, ResourceAware { private static final Logger LOG = LoggerFactory.getLogger(RoutesDefinition.class); diff --git a/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/ExtendedRouteBuilderLoaderSupport.java b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/ExtendedRouteBuilderLoaderSupport.java new file mode 100644 index 0000000..870df61 --- /dev/null +++ b/dsl/camel-dsl-support/src/main/java/org/apache/camel/dsl/support/ExtendedRouteBuilderLoaderSupport.java @@ -0,0 +1,51 @@ +/* + * 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.support; + +import java.util.Collection; + +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spi.ExtendedRoutesBuilderLoader; +import org.apache.camel.spi.Resource; +import org.apache.camel.spi.RoutesBuilderLoader; + +/** + * Base class for {@link RoutesBuilderLoader} implementations. + */ +public abstract class ExtendedRouteBuilderLoaderSupport extends RouteBuilderLoaderSupport + implements ExtendedRoutesBuilderLoader { + + protected ExtendedRouteBuilderLoaderSupport(String extension) { + super(extension); + } + + @Override + protected RouteBuilder doLoadRouteBuilder(Resource resource) throws Exception { + // noop + return null; + } + + @Override + public Collection<RoutesBuilder> loadRoutesBuilders(Collection<Resource> resources) throws Exception { + Collection<RoutesBuilder> answer = doLoadRoutesBuilders(resources); + return answer; + } + + protected abstract Collection<RoutesBuilder> doLoadRoutesBuilders(Collection<Resource> resources) throws Exception; + +} diff --git a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java index c29e54d..a5d1d26 100644 --- a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java +++ b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java @@ -18,14 +18,19 @@ package org.apache.camel.dsl.java.joor; import java.io.FileNotFoundException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.camel.CamelContextAware; +import org.apache.camel.RoutesBuilder; import org.apache.camel.api.management.ManagedResource; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.dsl.support.CompilePostProcessor; -import org.apache.camel.dsl.support.RouteBuilderLoaderSupport; +import org.apache.camel.dsl.support.ExtendedRouteBuilderLoaderSupport; import org.apache.camel.spi.Resource; +import org.apache.camel.spi.ResourceAware; import org.apache.camel.spi.annotations.RoutesLoader; import org.apache.camel.support.ResourceHelper; import org.apache.camel.util.FileUtil; @@ -34,7 +39,7 @@ import org.joor.Reflect; @ManagedResource(description = "Managed JavaRoutesBuilderLoader") @RoutesLoader(JavaRoutesBuilderLoader.EXTENSION) -public class JavaRoutesBuilderLoader extends RouteBuilderLoaderSupport { +public class JavaRoutesBuilderLoader extends ExtendedRouteBuilderLoaderSupport { public static final String EXTENSION = "java"; public static final Pattern PACKAGE_PATTERN = Pattern.compile( "^\\s*package\\s+([a-zA-Z][\\.\\w]*)\\s*;.*$", Pattern.MULTILINE); @@ -44,29 +49,40 @@ public class JavaRoutesBuilderLoader extends RouteBuilderLoaderSupport { } @Override - public RouteBuilder doLoadRouteBuilder(Resource resource) throws Exception { - try (InputStream is = resource.getInputStream()) { - if (is == null) { - throw new FileNotFoundException(resource.getLocation()); - } - String content = IOHelper.loadText(is); - String name = determineName(resource, content); + protected Collection<RoutesBuilder> doLoadRoutesBuilders(Collection<Resource> resources) throws Exception { + Collection<RoutesBuilder> answer = new ArrayList<>(); - Reflect ref = Reflect.compile(name, content).create(); - Class<?> clazz = ref.type(); - Object obj = ref.get(); + // TODO: when joor supports compiling in one unit + for (Resource resource : resources) { + try (InputStream is = resource.getInputStream()) { + if (is == null) { + throw new FileNotFoundException(resource.getLocation()); + } + String content = IOHelper.loadText(is); + String name = determineName(resource, content); - // support custom annotation scanning post compilation - // such as to register custom beans, type converters, etc. - for (CompilePostProcessor pre : getCompilePostProcessors()) { - pre.postCompile(getCamelContext(), name, clazz, obj); - } + Reflect ref = Reflect.compile(name, content).create(); + Class<?> clazz = ref.type(); + Object obj = ref.get(); + + // inject context and resource + CamelContextAware.trySetCamelContext(obj, getCamelContext()); + ResourceAware.trySetResource(obj, resource); - if (obj instanceof RouteBuilder) { - return (RouteBuilder) obj; + // support custom annotation scanning post compilation + // such as to register custom beans, type converters, etc. + for (CompilePostProcessor pre : getCompilePostProcessors()) { + pre.postCompile(getCamelContext(), name, clazz, obj); + } + + if (obj instanceof RouteBuilder) { + RouteBuilder builder = (RouteBuilder) obj; + answer.add(builder); + } } - return null; } + + return answer; } private static String determineName(Resource resource, String content) {
