You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2022/03/19 14:15:24 UTC
[camel] 03/03: CAMEL-17815: camel-jbang - In modeline mode then eager discover routes and pre-load spec/* from camel-k integration yaml files to allow properties to be used during bootstrap.
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 6832fc27c6648d7757255ee0fd75cd889997d20e
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sat Mar 19 13:59:13 2022 +0100
CAMEL-17815: camel-jbang - In modeline mode then eager discover routes and pre-load spec/* from camel-k integration yaml files to allow properties to be used during bootstrap.
---
.../org/apache/camel/spi/RoutesBuilderLoader.java | 12 +++
.../java/org/apache/camel/spi/RoutesLoader.java | 4 +
.../camel/impl/engine/DefaultRoutesLoader.java | 38 ++++++---
.../org/apache/camel/main/RoutesConfigurer.java | 8 ++
.../camel/dsl/yaml/YamlRoutesBuilderLoader.java | 99 +++++++++++++++++-----
.../dsl/yaml/YamlRoutesBuilderLoaderSupport.java | 6 +-
6 files changed, 132 insertions(+), 35 deletions(-)
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/RoutesBuilderLoader.java b/core/camel-api/src/main/java/org/apache/camel/spi/RoutesBuilderLoader.java
index f55da10..7492e03 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/RoutesBuilderLoader.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/RoutesBuilderLoader.java
@@ -49,4 +49,16 @@ public interface RoutesBuilderLoader extends StaticService, CamelContextAware {
* @return a {@link RoutesBuilder}
*/
RoutesBuilder loadRoutesBuilder(Resource resource) throws Exception;
+
+ /**
+ * Pre-parses the {@link RoutesBuilder} from {@link Resource}.
+ *
+ * This is used during bootstrap, to eager detect configurations from route DSL resources which makes it possible to
+ * specify configurations that affect the bootstrap, such as by camel-jbang and camel-yaml-dsl.
+ *
+ * @param resource the resource to be pre parsed.
+ */
+ default void preParseRoute(Resource resource) throws Exception {
+ // noop
+ }
}
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/RoutesLoader.java b/core/camel-api/src/main/java/org/apache/camel/spi/RoutesLoader.java
index 4ac5c35..e00720e 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/RoutesLoader.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/RoutesLoader.java
@@ -115,4 +115,8 @@ public interface RoutesLoader extends CamelContextAware {
* @return a collection {@link RoutesBuilder}
*/
Collection<RoutesBuilder> findRoutesBuilders(Collection<Resource> resources) throws Exception;
+
+ default void preParseRoute(Resource resource) throws Exception {
+ // noop
+ }
}
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 1d134e3..0f4737f 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
@@ -87,24 +87,14 @@ public class DefaultRoutesLoader extends ServiceSupport implements RoutesLoader,
List<RoutesBuilder> answer = new ArrayList<>(resources.size());
for (Resource resource : resources) {
- // the loader to use is derived from the file extension
- final String extension = FileUtil.onlyExt(resource.getLocation(), false);
-
- if (ObjectHelper.isEmpty(extension)) {
- throw new IllegalArgumentException(
- "Unable to determine file extension for resource: " + resource.getLocation());
- }
-
- RoutesBuilderLoader loader = getRoutesLoader(extension);
- if (loader == null) {
- throw new IllegalArgumentException(
- "Cannot find RoutesBuilderLoader in classpath supporting file extension: " + extension);
- }
+ RoutesBuilderLoader loader = resolveRoutesBuilderLoader(resource);
if (camelContext.isModeline()) {
ModelineFactory factory = camelContext.adapt(ExtendedCamelContext.class).getModelineFactory();
// gather resources for modeline
factory.parseModeline(resource);
+ // pre-parse before loading
+ loader.preParseRoute(resource);
}
RoutesBuilder builder = loader.loadRoutesBuilder(resource);
@@ -116,6 +106,11 @@ public class DefaultRoutesLoader extends ServiceSupport implements RoutesLoader,
return answer;
}
+ @Override
+ public void preParseRoute(Resource resource) throws Exception {
+ resolveRoutesBuilderLoader(resource).preParseRoute(resource);
+ }
+
/**
* Looks up a {@link RoutesBuilderLoader} in the registry or fallback to a factory finder mechanism if none found.
*
@@ -178,4 +173,21 @@ public class DefaultRoutesLoader extends ServiceSupport implements RoutesLoader,
return answer;
}
+ protected RoutesBuilderLoader resolveRoutesBuilderLoader(Resource resource) throws Exception {
+ // the loader to use is derived from the file extension
+ final String extension = FileUtil.onlyExt(resource.getLocation(), false);
+
+ if (ObjectHelper.isEmpty(extension)) {
+ throw new IllegalArgumentException(
+ "Unable to determine file extension for resource: " + resource.getLocation());
+ }
+
+ RoutesBuilderLoader loader = getRoutesLoader(extension);
+ if (loader == null) {
+ throw new IllegalArgumentException(
+ "Cannot find RoutesBuilderLoader in classpath supporting file extension: " + extension);
+ }
+ return loader;
+ }
+
}
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/RoutesConfigurer.java b/core/camel-main/src/main/java/org/apache/camel/main/RoutesConfigurer.java
index e680485..e05a0af 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/RoutesConfigurer.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/RoutesConfigurer.java
@@ -30,6 +30,7 @@ import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.spi.CamelBeanPostProcessor;
import org.apache.camel.spi.ModelineFactory;
import org.apache.camel.spi.Resource;
+import org.apache.camel.spi.RoutesLoader;
import org.apache.camel.support.OrderedComparator;
import org.apache.camel.util.StopWatch;
import org.apache.camel.util.TimeUtils;
@@ -282,6 +283,13 @@ public class RoutesConfigurer {
LOG.debug("Parsing modeline: {}", resource);
factory.parseModeline(resource);
}
+ // the resource may also have additional configurations which we need to detect via pre-parsing
+ for (Resource resource : resources) {
+ LOG.debug("Pre-parsing: {}", resource);
+ RoutesLoader loader = camelContext.adapt(ExtendedCamelContext.class).getRoutesLoader();
+ loader.preParseRoute(resource);
+ }
+
}
}
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java
index f43e373..3d6d141 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java
@@ -16,6 +16,8 @@
*/
package org.apache.camel.dsl.yaml;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -59,11 +61,18 @@ import org.apache.camel.support.ObjectHelper;
import org.apache.camel.support.PropertyBindingSupport;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.URISupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.snakeyaml.engine.v2.api.YamlUnicodeReader;
+import org.snakeyaml.engine.v2.composer.Composer;
import org.snakeyaml.engine.v2.nodes.MappingNode;
import org.snakeyaml.engine.v2.nodes.Node;
import org.snakeyaml.engine.v2.nodes.NodeTuple;
import org.snakeyaml.engine.v2.nodes.NodeType;
import org.snakeyaml.engine.v2.nodes.SequenceNode;
+import org.snakeyaml.engine.v2.parser.Parser;
+import org.snakeyaml.engine.v2.parser.ParserImpl;
+import org.snakeyaml.engine.v2.scanner.StreamReader;
import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.asMap;
import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.asMappingNode;
@@ -79,6 +88,8 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
public static final String EXTENSION = "yaml";
+ private static final Logger LOG = LoggerFactory.getLogger(YamlRoutesBuilderLoader.class);
+
// API versions for Camel-K Integration and Kamelet Binding
// we are lenient so lets just assume we can work with any of the v1 even if they evolve
private static final String INTEGRATION_VERSION = "camel.apache.org/v1";
@@ -104,7 +115,7 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
ctx.setResource(resource);
setDeserializationContext(root, ctx);
- Object target = preConfigureNode(root, ctx);
+ Object target = preConfigureNode(root, ctx, false);
if (target == null) {
return;
}
@@ -196,7 +207,7 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
ctx.setResource(resource);
setDeserializationContext(root, ctx);
- Object target = preConfigureNode(root, ctx);
+ Object target = preConfigureNode(root, ctx, false);
if (target == null) {
return;
}
@@ -236,7 +247,7 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
};
}
- private Object preConfigureNode(Node root, YamlDeserializationContext ctx) throws Exception {
+ private Object preConfigureNode(Node root, YamlDeserializationContext ctx, boolean preParse) throws Exception {
Object target = root;
// check if the yaml is a camel-k yaml with embedded binding/routes (called flow(s))
@@ -249,8 +260,9 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
boolean binding = anyTupleMatches(mn.getValue(), "apiVersion", v -> v.startsWith(BINDING_VERSION)) &&
anyTupleMatches(mn.getValue(), "kind", "KameletBinding");
if (integration) {
- target = preConfigureIntegration(root, ctx, target);
- } else if (binding) {
+ target = preConfigureIntegration(root, ctx, target, preParse);
+ } else if (binding && !preParse) {
+ // kamelet binding does not take part in pre-parse phase
target = preConfigureKameletBinding(root, ctx, target);
}
}
@@ -261,7 +273,9 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
/**
* Camel K Integration file
*/
- private Object preConfigureIntegration(Node root, YamlDeserializationContext ctx, Object target) {
+ private Object preConfigureIntegration(Node root, YamlDeserializationContext ctx, Object target, boolean preParse) {
+ // when in pre-parse phase then we only want to gather spec/dependencies,spec/configuration,spec/traits
+
List<Object> answer = new ArrayList<>();
// if there are dependencies then include them first
@@ -270,6 +284,7 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
var dep = preConfigureDependencies(deps);
answer.add(dep);
}
+
// if there are configurations then include them early
Node configuration = nodeAt(root, "/spec/configuration");
if (configuration != null) {
@@ -288,20 +303,24 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
var list = preConfigureTraitEnvironment(configuration);
answer.addAll(list);
}
- // if there are sources then include them before routes
- Node sources = nodeAt(root, "/spec/sources");
- if (sources != null) {
- var list = preConfigureSources(sources);
- answer.addAll(list);
- }
- // add routes last
- Node routes = nodeAt(root, "/spec/flows");
- if (routes == null) {
- routes = nodeAt(root, "/spec/flow");
- }
- if (routes != null) {
- answer.add(routes);
+
+ if (!preParse) {
+ // if there are sources then include them before routes
+ Node sources = nodeAt(root, "/spec/sources");
+ if (sources != null) {
+ var list = preConfigureSources(sources);
+ answer.addAll(list);
+ }
+ // add routes last
+ Node routes = nodeAt(root, "/spec/flows");
+ if (routes == null) {
+ routes = nodeAt(root, "/spec/flow");
+ }
+ if (routes != null) {
+ answer.add(routes);
+ }
}
+
return answer;
}
@@ -624,4 +643,46 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
}
}
+ @Override
+ public void preParseRoute(Resource resource) throws Exception {
+ LOG.trace("Pre-parsing: {}", resource.getLocation());
+
+ if (!resource.exists()) {
+ throw new FileNotFoundException("Resource not found: " + resource.getLocation());
+ }
+
+ try (InputStream is = resource.getInputStream()) {
+ final StreamReader reader = new StreamReader(settings, new YamlUnicodeReader(is));
+ final Parser parser = new ParserImpl(settings, reader);
+ final Composer composer = new Composer(settings, parser);
+
+ composer.getSingleNode()
+ .map(node -> preParseNode(node, resource));
+ }
+ }
+
+ private Object preParseNode(Node root, Resource resource) {
+ LOG.trace("Pre-parsing node: {}", root);
+
+ YamlDeserializationContext ctx = getDeserializationContext();
+ ctx.setResource(resource);
+ setDeserializationContext(root, ctx);
+
+ try {
+ Object target = preConfigureNode(root, ctx, true);
+ Iterator<?> it = ObjectHelper.createIterator(target);
+ while (it.hasNext()) {
+ target = it.next();
+ if (target instanceof CamelContextCustomizer) {
+ CamelContextCustomizer customizer = (CamelContextCustomizer) target;
+ customizer.configure(getCamelContext());
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeCamelException("Error pre-parsing resource: " + resource.getLocation(), e);
+ }
+
+ return null;
+ }
+
}
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java
index 6102197..04429d2 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java
@@ -50,9 +50,9 @@ import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.asText;
public abstract class YamlRoutesBuilderLoaderSupport extends RouteBuilderLoaderSupport {
public static final String DESERIALIZATION_MODE = "CamelYamlDslDeserializationMode";
- private LoadSettings settings;
- private YamlDeserializationContext deserializationContext;
- private YamlDeserializationMode deserializationMode;
+ LoadSettings settings;
+ YamlDeserializationContext deserializationContext;
+ YamlDeserializationMode deserializationMode;
public YamlRoutesBuilderLoaderSupport(String extension) {
super(extension);