This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch blueprint in repository https://gitbox.apache.org/repos/asf/camel.git
commit c3813a64eb575cff7cbaca0abbcb5c568f09f152 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Wed Aug 30 13:31:57 2023 +0200 camel-jbang - Allow to load OSGi <blueprint> with embedded <camelContext> --- .../java/org/apache/camel/main/util/XmlHelper.java | 12 ++ .../xml/blueprint/BlueprintXmlBeansHandler.java | 129 +++++++++++++++++++++ .../main/xml/spring/SpringXmlBeansHandler.java | 8 +- 3 files changed, 145 insertions(+), 4 deletions(-) diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java index 52b843abfcf..0a362f18334 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/util/XmlHelper.java @@ -20,6 +20,8 @@ import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.Node; + import org.apache.camel.util.ObjectHelper; public final class XmlHelper { @@ -60,4 +62,14 @@ public final class XmlHelper { return factory; } + public static String getAttribute(Node node, String key) { + if (node != null && node.hasAttributes()) { + Node attr = node.getAttributes().getNamedItem(key); + if (attr != null) { + return attr.getNodeValue(); + } + } + return null; + } + } diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java index 959dd09a610..9316a5df26a 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java @@ -16,13 +16,25 @@ */ package org.apache.camel.main.xml.blueprint; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.StringJoiner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import org.apache.camel.CamelContext; import org.apache.camel.main.MainConfigurationProperties; +import org.apache.camel.main.util.XmlHelper; import org.apache.camel.main.xml.spring.SpringXmlBeansHandler; +import org.apache.camel.model.Model; +import org.apache.camel.model.app.RegistryBeanDefinition; +import org.apache.camel.spi.Resource; +import org.apache.camel.spi.ResourceLoader; +import org.apache.camel.util.StringHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +45,12 @@ import org.slf4j.LoggerFactory; public class BlueprintXmlBeansHandler { private static final Logger LOG = LoggerFactory.getLogger(SpringXmlBeansHandler.class); + private static final Pattern BLUEPRINT_PATTERN = Pattern.compile("(\\$\\{.*?})"); // non-greedy mode + + // when preparing blueprint-based beans, we may have problems loading classes which are provided with Java DSL + // that's why some beans should be processed later + private final Map<String, Node> delayedBeans = new LinkedHashMap<>(); + private final Map<String, Resource> resources = new LinkedHashMap<>(); /** * Parses the XML documents and discovers blueprint beans, which will be created manually via Camel. @@ -41,13 +59,124 @@ public class BlueprintXmlBeansHandler { CamelContext camelContext, MainConfigurationProperties config, final Map<String, Document> xmls) { LOG.debug("Loading beans from classic OSGi <blueprint> XML"); + + xmls.forEach((id, doc) -> { + if (id.startsWith("camel-xml-io-dsl-blueprint-xml:")) { + // this is a camel bean via camel-xml-io-dsl + String fileName = StringHelper.afterLast(id, ":"); + discoverBeans(camelContext, fileName, doc); + } + }); } /** * Invoked at later stage to create and register Blueprint beans into Camel {@link org.apache.camel.spi.Registry}. */ public void createAndRegisterBeans(CamelContext camelContext) { + LOG.info("Discovered {} OSGi <blueprint> XML beans", delayedBeans.size()); + + for (Map.Entry<String, Node> entry : delayedBeans.entrySet()) { + String id = entry.getKey(); + Node n = entry.getValue(); + RegistryBeanDefinition bean = createBeanModel(camelContext, id, n); + // create bean and register to camel + + LOG.info("Creating bean: {}", bean); + // TODO: create bean from the model + + // register bean into model (as a BeanRegistry that allows Camel DSL to know about these beans) + Model model = camelContext.getCamelContextExtension().getContextPlugin(Model.class); + model.addRegistryBean(bean); + } + } + + private RegistryBeanDefinition createBeanModel(CamelContext camelContext, String name, Node node) { + RegistryBeanDefinition rrd = new RegistryBeanDefinition(); + rrd.setResource(resources.get(name)); + rrd.setType(XmlHelper.getAttribute(node, "class")); + rrd.setName(name); + + // constructor arguments + StringJoiner sj = new StringJoiner(", "); + NodeList props = node.getChildNodes(); + for (int i = 0; i < props.getLength(); i++) { + Node child = props.item(i); + // assume the args are in order (1, 2) + if ("argument".equals(child.getNodeName())) { + String val = XmlHelper.getAttribute(child, "value"); + String ref = XmlHelper.getAttribute(child, "ref"); + if (val != null) { + sj.add("'" + extractValue(camelContext, val, false) + "'"); + } else if (ref != null) { + sj.add("'#bean:" + extractValue(camelContext, ref, false) + "'"); + } + } + } + if (sj.length() > 0) { + rrd.setType("#class:" + rrd.getType() + "(" + sj + ")"); + } + + // property values + Map<String, Object> properties = new LinkedHashMap<>(); + props = node.getChildNodes(); + for (int i = 0; i < props.getLength(); i++) { + Node child = props.item(i); + // assume the args are in order (1, 2) + if ("property".equals(child.getNodeName())) { + String key = XmlHelper.getAttribute(child, "name"); + String val = XmlHelper.getAttribute(child, "value"); + String ref = XmlHelper.getAttribute(child, "ref"); + + // TODO: List/Map properties + if (key != null && val != null) { + properties.put(key, extractValue(camelContext, val, false)); + } else if (key != null && ref != null) { + properties.put(key, extractValue(camelContext, "#bean:" + ref, false)); + } + } + } + if (!properties.isEmpty()) { + rrd.setProperties(properties); + } + + return rrd; + } + + private void discoverBeans(CamelContext camelContext, String fileName, Document dom) { + Resource resource = camelContext.getCamelContextExtension().getContextPlugin(ResourceLoader.class) + .resolveResource("file:" + fileName); + + NodeList beans = dom.getElementsByTagName("bean"); + for (int i = 0; i < beans.getLength(); i++) { + Node n = beans.item(i); + if (n.hasAttributes()) { + String id = XmlHelper.getAttribute(n, "id"); + if (id != null) { + delayedBeans.put(id, n); + resources.put(id, resource); + } + } + } + } + + protected String extractValue(CamelContext camelContext, String val, boolean resolve) { + // blueprint placeholder prefix + if (val != null && val.contains("${")) { + Matcher matcher = BLUEPRINT_PATTERN.matcher(val); + while (matcher.find()) { + String group = matcher.group(1); + String replace = "{{" + group.substring(2, group.length() - 1) + "}}"; + val = matcher.replaceFirst(replace); + // we changed so reset matcher so it can find more + matcher.reset(val); + } + } + if (resolve && camelContext != null) { + // if running camel then resolve property placeholders from beans + val = camelContext.resolvePropertyPlaceholders(val); + } + return val; } } diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java index f84ae377082..4473c26a98a 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java @@ -245,7 +245,7 @@ public class SpringXmlBeansHandler { if (val instanceof TypedStringValue tsv) { sj.add("'" + extractValue(camelContext, tsv.getValue(), false) + "'"); } else if (val instanceof BeanReference br) { - sj.add("'#bean:" + br.getBeanName() + "'"); + sj.add("'#bean:" + extractValue(camelContext, br.getBeanName(), false) + "'"); } } if (sj.length() > 0) { @@ -264,7 +264,7 @@ public class SpringXmlBeansHandler { if (val instanceof TypedStringValue tsv) { properties.put(key, extractValue(camelContext, tsv.getValue(), false)); } else if (val instanceof BeanReference br) { - properties.put(key, "#bean:" + br.getBeanName()); + properties.put(key, "#bean:" + extractValue(camelContext, br.getBeanName(), false)); } else if (val instanceof List) { int i = 0; Iterator<?> it = ObjectHelper.createIterator(val); @@ -274,7 +274,7 @@ public class SpringXmlBeansHandler { if (val instanceof TypedStringValue tsv) { properties.put(k, extractValue(camelContext, tsv.getValue(), false)); } else if (val instanceof BeanReference br) { - properties.put(k, "#bean:" + br.getBeanName()); + properties.put(k, "#bean:" + extractValue(camelContext, br.getBeanName(), false)); } i++; } @@ -286,7 +286,7 @@ public class SpringXmlBeansHandler { if (val instanceof TypedStringValue tsv) { properties.put(k, extractValue(camelContext, tsv.getValue(), false)); } else if (val instanceof BeanReference br) { - properties.put(k, "#bean:" + br.getBeanName()); + properties.put(k, "#bean:" + extractValue(camelContext, br.getBeanName(), false)); } } }