You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pp...@apache.org on 2020/12/10 12:37:45 UTC

[camel-quarkus] branch camel-master updated (0b4216a -> ec44edb)

This is an automated email from the ASF dual-hosted git repository.

ppalaga pushed a change to branch camel-master
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git.


 discard 0b4216a  CSimple language support #2036
     new ec44edb  CSimple language support #2036

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (0b4216a)
            \
             N -- N -- N   refs/heads/camel-master (ec44edb)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../main/src/test/java/org/apache/camel/quarkus/main/CoreMainTest.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)


[camel-quarkus] 01/01: CSimple language support #2036

Posted by pp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ppalaga pushed a commit to branch camel-master
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git

commit ec44edb7056a343b38ee5ccf54be6e3a09a961f0
Author: Peter Palaga <pp...@redhat.com>
AuthorDate: Thu Dec 3 12:45:19 2020 +0100

    CSimple language support #2036
---
 .../ROOT/pages/reference/extensions/core.adoc      |   6 +
 .../partials/reference/components/vertx-kafka.adoc |   1 +
 extensions-core/core/deployment/pom.xml            |  16 +
 .../CSimpleRouteDefinitionProcessor.java           | 354 +++++++++++++++++++++
 .../LanguageExpressionContentHandler.java          | 122 +++++++
 .../spi/CSimpleExpressionSourceBuildItem.java      |  58 ++++
 .../spi/CompiledCSimpleExpressionBuildItem.java    |  57 ++++
 .../core/runtime/src/main/adoc/limitations.adoc    |  17 +
 .../quarkus/core/CSimpleLanguageRecorder.java      |  24 +-
 .../org/apache/camel/quarkus/core/CamelConfig.java |  18 ++
 .../main/deployment/CSimpleXmlProcessor.java       | 127 ++++++++
 integration-tests/core/pom.xml                     |  42 +++
 .../apache/camel/quarkus/core/CoreResource.java    |  14 +
 .../org/apache/camel/quarkus/core/CoreRoutes.java  |   3 +
 .../org/apache/camel/quarkus/core/CoreTest.java    |  12 +
 .../camel/quarkus/main/CoreMainXmlIoResource.java  |   8 +
 .../src/main/resources/routes/my-routes.xml        |   7 +
 .../camel/quarkus/main/CoreMainXmlIoTest.java      |  13 +-
 .../apache/camel/quarkus/main/CoreMainTest.java    |   3 +-
 19 files changed, 892 insertions(+), 10 deletions(-)

diff --git a/docs/modules/ROOT/pages/reference/extensions/core.adoc b/docs/modules/ROOT/pages/reference/extensions/core.adoc
index cd8e0a5..381b7b6 100644
--- a/docs/modules/ROOT/pages/reference/extensions/core.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/core.adoc
@@ -160,6 +160,12 @@ A comma separated list of Ant-path style patterns to match class names that shou
  For this option to work properly, the artifacts containing the selected classes must either contain a Jandex index (`META-INF/jandex.idx`) or they must be registered for indexing using the `quarkus.index-dependency.++*++` family of options in `application.properties` - e.g. quarkus.index-dependency.my-dep.group-id = org.my-group quarkus.index-dependency.my-dep.artifact-id = my-artifact  where `my-dep` is a label of your choice to tell Quarkus that `org.my-group` and with `my-artifact` b [...]
 | `string`
 | 
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.csimple.on-build-time-analysis-failure]]`link:#quarkus.camel.csimple.on-build-time-analysis-failure[quarkus.camel.csimple.on-build-time-analysis-failure]`
+
+What to do if it is not possible to extract CSimple expressions from a route definition at build time.
+| `org.apache.camel.quarkus.core.CamelConfig.FailureRemedy`
+| `warn`
 |===
 
 [.configuration-legend]
diff --git a/docs/modules/ROOT/partials/reference/components/vertx-kafka.adoc b/docs/modules/ROOT/partials/reference/components/vertx-kafka.adoc
new file mode 100644
index 0000000..a509c1d
--- /dev/null
+++ b/docs/modules/ROOT/partials/reference/components/vertx-kafka.adoc
@@ -0,0 +1 @@
+// Empty partial for a Camel bit unsupported by Camel Quarkus to avoid warnings when this file is included from a Camel page
diff --git a/extensions-core/core/deployment/pom.xml b/extensions-core/core/deployment/pom.xml
index 37b0c2d..bf0822b 100644
--- a/extensions-core/core/deployment/pom.xml
+++ b/extensions-core/core/deployment/pom.xml
@@ -49,6 +49,22 @@
             <artifactId>camel-quarkus-core</artifactId>
         </dependency>
 
+        <!-- JAXB is needed for the build time routes introspection -->
+        <dependency>
+            <groupId>org.glassfish.jaxb</groupId>
+            <artifactId>jaxb-runtime</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>jakarta.xml.bind</groupId>
+                    <artifactId>jakarta.xml.bind-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.spec.javax.xml.bind</groupId>
+            <artifactId>jboss-jaxb-api_2.3_spec</artifactId>
+        </dependency>
+
         <!-- test dependencies -->
         <dependency>
             <groupId>org.apache.camel</groupId>
diff --git a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CSimpleRouteDefinitionProcessor.java b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CSimpleRouteDefinitionProcessor.java
new file mode 100644
index 0000000..c9878ab
--- /dev/null
+++ b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CSimpleRouteDefinitionProcessor.java
@@ -0,0 +1,354 @@
+/*
+ * 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.quarkus.core.deployment;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Marshaller.Listener;
+
+import io.quarkus.bootstrap.classloading.ClassPathElement;
+import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
+import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
+import io.quarkus.deployment.dev.CompilationProvider;
+import io.quarkus.deployment.dev.CompilationProvider.Context;
+import io.quarkus.deployment.dev.JavaCompilationProvider;
+import io.quarkus.deployment.recording.RecorderContext;
+import io.quarkus.runtime.RuntimeValue;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.NamedNode;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.language.csimple.CSimpleCodeGenerator;
+import org.apache.camel.language.csimple.CSimpleGeneratedCode;
+import org.apache.camel.language.csimple.CSimpleHelper;
+import org.apache.camel.language.csimple.CSimpleLanguage;
+import org.apache.camel.language.csimple.CSimpleLanguage.Builder;
+import org.apache.camel.model.Constants;
+import org.apache.camel.model.ExpressionNode;
+import org.apache.camel.quarkus.core.CSimpleLanguageRecorder;
+import org.apache.camel.quarkus.core.CamelConfig;
+import org.apache.camel.quarkus.core.CamelConfig.FailureRemedy;
+import org.apache.camel.quarkus.core.deployment.spi.CSimpleExpressionSourceBuildItem;
+import org.apache.camel.quarkus.core.deployment.spi.CamelBeanBuildItem;
+import org.apache.camel.quarkus.core.deployment.spi.CamelContextBuildItem;
+import org.apache.camel.quarkus.core.deployment.spi.CamelRoutesBuilderClassBuildItem;
+import org.apache.camel.quarkus.core.deployment.spi.CompiledCSimpleExpressionBuildItem;
+import org.apache.camel.util.PropertiesHelper;
+import org.jboss.logging.Logger;
+
+class CSimpleRouteDefinitionProcessor {
+    private static final Logger LOG = Logger.getLogger(CSimpleRouteDefinitionProcessor.class);
+    static final String CLASS_EXT = ".class";
+
+    @BuildStep
+    void collectCSimpleExpresions(
+            CamelConfig config,
+            List<CamelRoutesBuilderClassBuildItem> routesBuilderClasses,
+            BuildProducer<CSimpleExpressionSourceBuildItem> csimpleExpressions)
+            throws IOException, ClassNotFoundException, URISyntaxException, JAXBException {
+
+        if (!routesBuilderClasses.isEmpty()) {
+            final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+            if (!(loader instanceof QuarkusClassLoader)) {
+                throw new IllegalStateException(
+                        QuarkusClassLoader.class.getSimpleName() + " expected as the context class loader");
+            }
+
+            final ExpressionCollector collector = new ExpressionCollector(loader);
+
+            final CamelContext ctx = new DefaultCamelContext();
+            for (CamelRoutesBuilderClassBuildItem routesBuilderClass : routesBuilderClasses) {
+                final String className = routesBuilderClass.getDotName().toString();
+                final Class<?> cl = loader.loadClass(className);
+
+                if (!RouteBuilder.class.isAssignableFrom(cl)) {
+                    LOG.warnf("CSimple language expressions ocurring in %s won't be compiled at build time", cl);
+                } else {
+                    try {
+                        final RouteBuilder rb = (RouteBuilder) cl.newInstance();
+                        rb.setContext(ctx);
+                        try {
+                            rb.configure();
+                            collector.collect(
+                                    "csimple",
+                                    (script, isPredicate) -> csimpleExpressions.produce(
+                                            new CSimpleExpressionSourceBuildItem(
+                                                    script,
+                                                    isPredicate,
+                                                    className)),
+                                    rb.getRouteCollection(),
+                                    rb.getRestCollection());
+
+                        } catch (Exception e) {
+                            switch (config.csimple.onBuildTimeAnalysisFailure) {
+                            case fail:
+                                throw new RuntimeException(
+                                        "Could not extract CSimple expressions from " + className
+                                                + ". You may want to set quarkus.camel.csimple.on-build-time-analysis-failure to warn or ignore if you do not use CSimple language in your routes",
+                                        e);
+                            case warn:
+                                LOG.warnf(e,
+                                        "Could not extract CSimple language expressions from the route definition %s in class %s.",
+                                        rb, cl);
+                                break;
+                            case ignore:
+                                LOG.debugf(e,
+                                        "Could not extract CSimple language expressions from the route definition %s in class %s",
+                                        rb, cl);
+                                break;
+                            default:
+                                throw new IllegalStateException("Unexpected " + FailureRemedy.class.getSimpleName() + ": "
+                                        + config.csimple.onBuildTimeAnalysisFailure);
+                            }
+                        }
+
+                    } catch (InstantiationException | IllegalAccessException e) {
+                        throw new RuntimeException("Could not instantiate " + className, e);
+                    }
+                }
+            }
+        }
+    }
+
+    @BuildStep
+    void compileCSimpleExpresions(
+            List<CSimpleExpressionSourceBuildItem> expressionSources,
+            BuildProducer<CompiledCSimpleExpressionBuildItem> compiledCSimpleExpression,
+            BuildProducer<GeneratedClassBuildItem> generatedClasses) throws IOException {
+
+        if (!expressionSources.isEmpty()) {
+            final Set<String> imports = new TreeSet<>();
+            final Map<String, String> aliases = new LinkedHashMap<>();
+            final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+            if (!(loader instanceof QuarkusClassLoader)) {
+                throw new IllegalStateException(
+                        QuarkusClassLoader.class.getSimpleName() + " expected as the context class loader");
+            }
+            final QuarkusClassLoader quarkusClassLoader = (QuarkusClassLoader) loader;
+            readConfig(imports, aliases, loader);
+            final CSimpleCodeGenerator generator = new CSimpleCodeGenerator();
+            generator.setAliases(aliases);
+            generator.setImports(imports);
+
+            final Path projectDir = Paths.get(".").toAbsolutePath().normalize();
+            final Path csimpleGeneratedSourceDir = projectDir.resolve("target/generated/csimple");
+            Files.createDirectories(csimpleGeneratedSourceDir);
+
+            final Set<File> filesToCompile = new LinkedHashSet<>();
+
+            /* We do not want to compile the same source twice, so we store here what we have compiled already */
+            final Map<Boolean, Set<String>> compiledExpressions = new HashMap<>();
+            compiledExpressions.put(true, new HashSet<>());
+            compiledExpressions.put(false, new HashSet<>());
+
+            /* Generate Java classes for the language expressions */
+            for (CSimpleExpressionSourceBuildItem expr : expressionSources) {
+                final boolean predicate = expr.isPredicate();
+                final String script = expr.getSourceCode();
+                if (!compiledExpressions.get(predicate).contains(script)) {
+                    final CSimpleGeneratedCode code = predicate
+                            ? generator.generatePredicate(expr.getClassNameBase(), script)
+                            : generator.generateExpression(expr.getClassNameBase(), script);
+
+                    compiledCSimpleExpression
+                            .produce(new CompiledCSimpleExpressionBuildItem(code.getCode(), predicate, code.getFqn()));
+
+                    final Path javaCsimpleFile = csimpleGeneratedSourceDir
+                            .resolve(code.getFqn().replace('.', '/') + ".java");
+                    Files.createDirectories(javaCsimpleFile.getParent());
+                    Files.write(javaCsimpleFile, code.getCode().getBytes(StandardCharsets.UTF_8));
+                    filesToCompile.add(javaCsimpleFile.toFile());
+                    compiledExpressions.get(predicate).add(script);
+                }
+            }
+
+            final Path csimpleClassesDir = projectDir.resolve("target/csimple-classes");
+            Files.createDirectories(csimpleClassesDir);
+
+            /* Compile the generated sources */
+            try (JavaCompilationProvider compiler = new JavaCompilationProvider()) {
+                final Context context = compilationContext(projectDir, csimpleClassesDir, quarkusClassLoader);
+                compiler.compile(filesToCompile, context);
+            }
+
+            /* Register the compiled classes via Quarkus GeneratedClassBuildItem */
+            try (Stream<Path> classFiles = Files.walk(csimpleClassesDir)) {
+                classFiles
+                        .filter(Files::isRegularFile)
+                        .filter(p -> p.getFileName().toString().endsWith(CLASS_EXT))
+                        .forEach(p -> {
+                            final Path relPath = csimpleClassesDir.relativize(p);
+                            String className = relPath.toString();
+                            className = className.substring(0, className.length() - CLASS_EXT.length());
+                            try {
+                                final GeneratedClassBuildItem item = new GeneratedClassBuildItem(true, className,
+                                        Files.readAllBytes(p));
+                                generatedClasses.produce(item);
+                            } catch (IOException e) {
+                                throw new RuntimeException("Could not read " + p);
+                            }
+                        });
+            }
+
+        }
+    }
+
+    @Record(ExecutionTime.STATIC_INIT)
+    @BuildStep
+    CamelBeanBuildItem configureCSimpleLanguage(
+            RecorderContext recorderContext,
+            CSimpleLanguageRecorder recorder,
+            CamelContextBuildItem camelContext,
+            List<CompiledCSimpleExpressionBuildItem> compiledCSimpleExpressions) {
+
+        final RuntimeValue<Builder> builder = recorder.csimpleLanguageBuilder();
+        for (CompiledCSimpleExpressionBuildItem expr : compiledCSimpleExpressions) {
+            recorder.addExpression(builder, recorderContext.newInstance(expr.getClassName()));
+        }
+
+        final RuntimeValue<?> csimpleLanguage = recorder.buildCSimpleLanguage(builder);
+        return new CamelBeanBuildItem("csimple", CSimpleLanguage.class.getName(), csimpleLanguage);
+    }
+
+    static void readConfig(Set<String> imports, Map<String, String> aliases, ClassLoader cl) throws IOException {
+        Enumeration<URL> confiUrls = cl.getResources("camel-csimple.properties");
+        while (confiUrls.hasMoreElements()) {
+            final URL configUrl = confiUrls.nextElement();
+            try (BufferedReader r = new BufferedReader(new InputStreamReader(configUrl.openStream(), StandardCharsets.UTF_8))) {
+                String line = null;
+                while ((line = r.readLine()) != null) {
+                    line = line.trim();
+                    // skip comments
+                    if (line.startsWith("#")) {
+                        continue;
+                    }
+                    // imports
+                    if (line.startsWith("import ")) {
+                        imports.add(line);
+                        continue;
+                    }
+                    // aliases as key=value
+                    final int eqPos = line.indexOf('=');
+                    final String key = line.substring(0, eqPos).trim();
+                    final String value = line.substring(eqPos + 1).trim();
+                    aliases.put(key, value);
+
+                }
+            } catch (IOException e) {
+                throw new RuntimeException("Could not read from " + configUrl);
+            }
+        }
+    }
+
+    private Context compilationContext(final Path projectDir, final Path csimpleClassesDir,
+            QuarkusClassLoader quarkusClassLoader) {
+        Set<File> classPathElements = Stream.of(CSimpleHelper.class, Exchange.class, PropertiesHelper.class)
+                .map(clazz -> clazz.getName().replace('.', '/') + CLASS_EXT)
+                .flatMap(className -> (Stream<ClassPathElement>) quarkusClassLoader.getElementsWithResource(className).stream())
+                .map(cpe -> cpe.getRoot())
+                .filter(p -> p != null)
+                .map(Path::toFile)
+                .collect(Collectors.toSet());
+
+        return new CompilationProvider.Context(
+                "csimple-project",
+                classPathElements,
+                projectDir.toFile(),
+                projectDir.resolve("src/main/java").toFile(),
+                csimpleClassesDir.toFile(),
+                StandardCharsets.UTF_8.name(),
+                Collections.emptyList(),
+                "1.8",
+                "1.8",
+                Collections.emptyList(),
+                Collections.emptyList());
+    }
+
+    /**
+     * Collects expressions of a given language.
+     */
+    static class ExpressionCollector {
+        private final JAXBContext jaxbContext;
+        private final Marshaller marshaler;
+
+        ExpressionCollector(ClassLoader loader) {
+            try {
+                jaxbContext = JAXBContext.newInstance(Constants.JAXB_CONTEXT_PACKAGES, loader);
+                Marshaller m = jaxbContext.createMarshaller();
+                m.setListener(new RouteDefinitionNormalizer());
+                marshaler = m;
+            } catch (JAXBException e) {
+                throw new RuntimeException("Could not creat a JAXB marshaler", e);
+            }
+        }
+
+        public void collect(String languageName, BiConsumer<String, Boolean> expressionConsumer, NamedNode... nodes) {
+            final LanguageExpressionContentHandler handler = new LanguageExpressionContentHandler(languageName,
+                    expressionConsumer);
+            for (NamedNode node : nodes) {
+                try {
+                    marshaler.marshal(node, handler);
+                } catch (JAXBException e) {
+                    throw new RuntimeException("Could not collect '" + languageName + "' expressions from node " + node, e);
+                }
+            }
+        }
+
+        /**
+         * Inlines all fancy expression builders so that JAXB can serialize the model properly.
+         */
+        private static class RouteDefinitionNormalizer extends Listener {
+            public void beforeMarshal(Object source) {
+                if (source instanceof ExpressionNode) {
+                    ((ExpressionNode) source).preCreateProcessor();
+                }
+            }
+        }
+
+    }
+
+}
diff --git a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/LanguageExpressionContentHandler.java b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/LanguageExpressionContentHandler.java
new file mode 100644
index 0000000..51d6abb
--- /dev/null
+++ b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/LanguageExpressionContentHandler.java
@@ -0,0 +1,122 @@
+/*
+ * 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.quarkus.core.deployment;
+
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class LanguageExpressionContentHandler extends DefaultHandler {
+
+    private final String languageName;
+
+    private final BiConsumer<String, Boolean> expressionConsumer;
+    private boolean inExpression = false;
+    private final StringBuilder expressionBuilder = new StringBuilder();
+    private final Deque<Map.Entry<String, Attributes>> path = new ArrayDeque<>();
+
+    public LanguageExpressionContentHandler(String languageName, BiConsumer<String, Boolean> expressionConsumer) {
+        super();
+        this.languageName = languageName;
+        this.expressionConsumer = expressionConsumer;
+    }
+
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes atts)
+            throws SAXException {
+        if (inExpression) {
+            throw new IllegalStateException("Unexpected element '" + localName + "' under '" + languageName
+                    + "'; only text content is expected");
+        }
+        if (languageName.equals(localName)) {
+            inExpression = true;
+        } else {
+            path.push(new SimpleImmutableEntry<String, Attributes>(localName, atts));
+        }
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        if (languageName.equals(localName)) {
+            final String expressionText = expressionBuilder.toString();
+            final boolean predicate = isPredicate();
+            expressionConsumer.accept(expressionText, predicate);
+            expressionBuilder.setLength(0);
+            inExpression = false;
+        } else {
+            path.pop();
+        }
+    }
+
+    private boolean isPredicate() {
+        Entry<String, Attributes> parent = path.peek();
+        if (parent != null) {
+            return hasSimplePredicateChild(parent.getKey(), attributeName -> {
+                final Attributes attribs = parent.getValue();
+                if (attribs != null) {
+                    return attribs.getValue(attributeName);
+                }
+                return null;
+            });
+        }
+        return false;
+    }
+
+    /**
+     * Inspired by {@link org.apache.camel.parser.XmlRouteParser#isSimplePredicate(Node)}.
+     *
+     * @param  name
+     * @param  getAttributeFunction
+     * @return
+     */
+    public static boolean hasSimplePredicateChild(String name, Function<String, String> getAttributeFunction) {
+
+        if (name == null) {
+            return false;
+        }
+        if (name.equals("completionPredicate") || name.equals("completion")) {
+            return true;
+        }
+        if (name.equals("onWhen") || name.equals("when") || name.equals("handled") || name.equals("continued")) {
+            return true;
+        }
+        if (name.equals("retryWhile") || name.equals("filter") || name.equals("validate")) {
+            return true;
+        }
+        // special for loop
+        if (name.equals("loop") && "true".equalsIgnoreCase(getAttributeFunction.apply("doWhile"))) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        if (inExpression) {
+            expressionBuilder.append(ch, start, length);
+        }
+    }
+
+}
diff --git a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CSimpleExpressionSourceBuildItem.java b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CSimpleExpressionSourceBuildItem.java
new file mode 100644
index 0000000..1547e6f
--- /dev/null
+++ b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CSimpleExpressionSourceBuildItem.java
@@ -0,0 +1,58 @@
+/*
+ * 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.quarkus.core.deployment.spi;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+/**
+ * A {@link MultiBuildItem} bearing info about a CSimple language expression that needs to get compiled.
+ */
+public final class CSimpleExpressionSourceBuildItem extends MultiBuildItem {
+
+    private final String sourceCode;
+    private final String classNameBase;
+    private final boolean predicate;
+
+    public CSimpleExpressionSourceBuildItem(String sourceCode, boolean predicate, String classNameBase) {
+        this.sourceCode = sourceCode;
+        this.predicate = predicate;
+        this.classNameBase = classNameBase;
+    }
+
+    /**
+     * @return the expression source code to compile
+     */
+    public String getSourceCode() {
+        return sourceCode;
+    }
+
+    /**
+     * @return a fully qualified class name that the compiler may use as a base for the name of the class into which it
+     *         compiles the source code returned by {@link #getSourceCode()}
+     */
+    public String getClassNameBase() {
+        return classNameBase;
+    }
+
+    /**
+     * @return {@code true} if the expression is a predicate; {@code false} otherwise
+     */
+    public boolean isPredicate() {
+        return predicate;
+    }
+
+}
diff --git a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CompiledCSimpleExpressionBuildItem.java b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CompiledCSimpleExpressionBuildItem.java
new file mode 100644
index 0000000..e81139c
--- /dev/null
+++ b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/spi/CompiledCSimpleExpressionBuildItem.java
@@ -0,0 +1,57 @@
+/*
+ * 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.quarkus.core.deployment.spi;
+
+import io.quarkus.builder.item.MultiBuildItem;
+
+/**
+ * A {@link MultiBuildItem} bearing info about a compiled CSimple language expression.
+ */
+public final class CompiledCSimpleExpressionBuildItem extends MultiBuildItem {
+
+    private final String sourceCode;
+    private final String className;
+    private final boolean predicate;
+
+    public CompiledCSimpleExpressionBuildItem(String sourceCode, boolean predicate, String className) {
+        this.sourceCode = sourceCode;
+        this.predicate = predicate;
+        this.className = className;
+    }
+
+    /**
+     * @return the source code out which the class returned by {@link #getClassName()} was compiled
+     */
+    public String getSourceCode() {
+        return sourceCode;
+    }
+
+    /**
+     * @return a fully qualified class name compiled from the source code returned by {@link #getSourceCode()}
+     */
+    public String getClassName() {
+        return className;
+    }
+
+    /**
+     * @return {@code true} if the expression is a predicate; {@code false} otherwise
+     */
+    public boolean isPredicate() {
+        return predicate;
+    }
+
+}
diff --git a/extensions-core/core/runtime/src/main/adoc/limitations.adoc b/extensions-core/core/runtime/src/main/adoc/limitations.adoc
new file mode 100644
index 0000000..9697d1c
--- /dev/null
+++ b/extensions-core/core/runtime/src/main/adoc/limitations.adoc
@@ -0,0 +1,17 @@
+=== CSimple language
+
+CSimple language is supported only in
+
+* XML DSL
+* Java DSL when implemented in a class extending `org.apache.camel.builder.RouteBuilder`
+
+The compilation of CSimple scripts happens at build time. To extract the scripts from the route definitions, these need
+to be assembled at build time. This may fail if the given route requires some data that is only available at runtime.
+You can use the `quarkus.camel.csimple.on-build-time-analysis-failure` configuration parameter to decide
+what should happen in such cases. The possible values are `warn` (default), `fail` or `ignore`.
+
+[WARNING]
+====
+CSimple language will not work on Camel Quarkus if used in a `org.apache.camel.builder.LambdaRouteBuilder`
+====
+
diff --git a/integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreRoutes.java b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CSimpleLanguageRecorder.java
similarity index 52%
copy from integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreRoutes.java
copy to extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CSimpleLanguageRecorder.java
index 4aa1678..d0237d7 100644
--- a/integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreRoutes.java
+++ b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CSimpleLanguageRecorder.java
@@ -16,17 +16,25 @@
  */
 package org.apache.camel.quarkus.core;
 
-import org.apache.camel.builder.RouteBuilder;
+import io.quarkus.runtime.RuntimeValue;
+import io.quarkus.runtime.annotations.Recorder;
+import org.apache.camel.language.csimple.CSimpleExpression;
+import org.apache.camel.language.csimple.CSimpleLanguage;
+import org.apache.camel.language.csimple.CSimpleLanguage.Builder;
 
-public class CoreRoutes extends RouteBuilder {
+@Recorder
+public class CSimpleLanguageRecorder {
 
-    @Override
-    public void configure() {
-        from("timer:keep-alive")
-                .routeId("timer")
-                .setBody().constant("I'm alive !")
-                .to("log:keep-alive");
+    public RuntimeValue<CSimpleLanguage.Builder> csimpleLanguageBuilder() {
+        return new RuntimeValue<>(CSimpleLanguage.builder());
+    }
+
+    public void addExpression(RuntimeValue<Builder> builder, RuntimeValue<CSimpleExpression> expression) {
+        builder.getValue().expression(expression.getValue());
+    }
 
+    public RuntimeValue<?> buildCSimpleLanguage(RuntimeValue<Builder> builder) {
+        return new RuntimeValue<>(builder.getValue().build());
     }
 
 }
diff --git a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelConfig.java b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelConfig.java
index 78573ef..e856007 100644
--- a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelConfig.java
+++ b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/CamelConfig.java
@@ -26,6 +26,10 @@ import io.quarkus.runtime.annotations.ConfigRoot;
 
 @ConfigRoot(name = "camel", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED)
 public class CamelConfig {
+    public enum FailureRemedy {
+        fail, warn, ignore
+    }
+
     /**
      * Build time configuration options for {@link CamelRuntime} bootstrap.
      */
@@ -56,6 +60,12 @@ public class CamelConfig {
     @ConfigItem(name = "native")
     public NativeConfig native_;
 
+    /**
+     * Build time configuration options for the Camel CSimple language.
+     */
+    @ConfigItem
+    public CSimpleConfig csimple;
+
     @ConfigGroup
     public static class BootstrapConfig {
         /**
@@ -329,4 +339,12 @@ public class CamelConfig {
         @ConfigItem(defaultValue = "true")
         public boolean models;
     }
+
+    @ConfigGroup
+    public static class CSimpleConfig {
+
+        /** What to do if it is not possible to extract CSimple expressions from a route definition at build time. */
+        @ConfigItem(defaultValue = "warn")
+        public FailureRemedy onBuildTimeAnalysisFailure;
+    }
 }
diff --git a/extensions-core/main/deployment/src/main/java/org/apache/camel/quarkus/main/deployment/CSimpleXmlProcessor.java b/extensions-core/main/deployment/src/main/java/org/apache/camel/quarkus/main/deployment/CSimpleXmlProcessor.java
new file mode 100644
index 0000000..c227733
--- /dev/null
+++ b/extensions-core/main/deployment/src/main/java/org/apache/camel/quarkus/main/deployment/CSimpleXmlProcessor.java
@@ -0,0 +1,127 @@
+/*
+ * 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.quarkus.main.deployment;
+
+import java.io.Closeable;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.SAXException;
+
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.impl.engine.DefaultPackageScanResourceResolver;
+import org.apache.camel.quarkus.core.CamelConfig;
+import org.apache.camel.quarkus.core.deployment.LanguageExpressionContentHandler;
+import org.apache.camel.quarkus.core.deployment.spi.CSimpleExpressionSourceBuildItem;
+import org.apache.camel.quarkus.core.deployment.spi.CamelRoutesBuilderClassBuildItem;
+import org.apache.camel.quarkus.core.deployment.util.CamelSupport;
+import org.jboss.logging.Logger;
+
+public class CSimpleXmlProcessor {
+
+    private static final Logger LOG = Logger.getLogger(CSimpleXmlProcessor.class);
+
+    @BuildStep
+    void collectCSimpleExpresions(
+            CamelConfig config,
+            List<CamelRoutesBuilderClassBuildItem> routesBuilderClasses,
+            BuildProducer<CSimpleExpressionSourceBuildItem> csimpleExpressions)
+            throws ParserConfigurationException, SAXException, IOException {
+
+        final List<String> locations = Stream.of("camel.main.xml-routes", "camel.main.xml-rests")
+                .map(prop -> CamelSupport.getOptionalConfigValue(prop, String[].class, new String[0]))
+                .flatMap(Stream::of)
+                .collect(Collectors.toList());
+
+        try (DefaultPackageScanResourceResolver resolver = new DefaultPackageScanResourceResolver()) {
+            resolver.setCamelContext(new DefaultCamelContext());
+            final SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+            saxParserFactory.setNamespaceAware(true);
+            SAXParser saxParser = saxParserFactory.newSAXParser();
+            for (String part : locations) {
+                try {
+                    try (CloseableCollection<InputStream> set = new CloseableCollection<InputStream>(
+                            resolver.findResources(part))) {
+                        for (InputStream is : set) {
+                            LOG.debugf("Found XML routes from location: %s", part);
+                            try {
+                                saxParser.parse(
+                                        is,
+                                        new LanguageExpressionContentHandler(
+                                                "csimple",
+                                                (script, isPredicate) -> csimpleExpressions.produce(
+                                                        new CSimpleExpressionSourceBuildItem(
+                                                                script,
+                                                                isPredicate,
+                                                                "org.apache.camel.language.csimple.XmlRouteBuilder"))));
+                            } finally {
+                                if (is != null) {
+                                    is.close();
+                                }
+                            }
+                        }
+                    }
+                } catch (FileNotFoundException e) {
+                    LOG.debugf("No XML routes found in %s. Skipping XML routes detection.", part);
+                } catch (Exception e) {
+                    throw new RuntimeException("Could not analyze CSimple expressions in " + part, e);
+                }
+            }
+        }
+    }
+
+    static class CloseableCollection<E extends Closeable> implements Closeable, Iterable<E> {
+        private final Collection<E> delegate;
+
+        public CloseableCollection(Collection<E> delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void close() throws IOException {
+            List<Exception> exceptions = new ArrayList<>();
+            for (Closeable closeable : delegate) {
+                try {
+                    closeable.close();
+                } catch (Exception e) {
+                    exceptions.add(e);
+                }
+            }
+            if (!exceptions.isEmpty()) {
+                throw new IOException("Could not close a resource", exceptions.get(0));
+            }
+        }
+
+        @Override
+        public Iterator<E> iterator() {
+            return delegate.iterator();
+        }
+    }
+}
diff --git a/integration-tests/core/pom.xml b/integration-tests/core/pom.xml
index 13c761a..9c77a65 100644
--- a/integration-tests/core/pom.xml
+++ b/integration-tests/core/pom.xml
@@ -119,6 +119,48 @@
 
     </dependencies>
 
+    <build>
+        <plugins>
+<!--             <plugin>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-csimple-maven-plugin</artifactId>
+                <version>${camel.version}</version>
+                <executions>
+                    <execution>
+                        <id>generate</id>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>3.1.0</version>
+                <executions>
+                    <execution>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                            <goal>add-resource</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>src/generated/java</source>
+                            </sources>
+                            <resources>
+                                <resource>
+                                    <directory>src/generated/resources</directory>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin> -->
+        </plugins>
+    </build>
+
 
     <profiles>
         <profile>
diff --git a/integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreResource.java b/integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreResource.java
index 8587ef5..60e4c2f 100644
--- a/integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreResource.java
+++ b/integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreResource.java
@@ -28,7 +28,9 @@ import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 import javax.json.Json;
 import javax.json.JsonObject;
+import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -39,6 +41,7 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.ExtendedCamelContext;
 import org.apache.camel.NoSuchLanguageException;
+import org.apache.camel.ProducerTemplate;
 import org.apache.camel.Route;
 import org.apache.camel.builder.LambdaRouteBuilder;
 import org.apache.camel.builder.TemplatedRouteBuilder;
@@ -59,6 +62,9 @@ public class CoreResource {
     @Inject
     CamelContext context;
 
+    @Inject
+    ProducerTemplate producerTemplate;
+
     @Path("/registry/log/exchange-formatter")
     @GET
     @Produces(MediaType.APPLICATION_JSON)
@@ -261,4 +267,12 @@ public class CoreResource {
     public boolean headersMapFactory() {
         return context.adapt(ExtendedCamelContext.class).getHeadersMapFactory() instanceof DefaultHeadersMapFactory;
     }
+
+    @Path("/csimple-hello")
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    public String csimpleHello(String body) {
+        return producerTemplate.requestBody("direct:csimple-hello", body, String.class);
+    }
 }
diff --git a/integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreRoutes.java b/integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreRoutes.java
index 4aa1678..5956f38 100644
--- a/integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreRoutes.java
+++ b/integration-tests/core/src/main/java/org/apache/camel/quarkus/core/CoreRoutes.java
@@ -27,6 +27,9 @@ public class CoreRoutes extends RouteBuilder {
                 .setBody().constant("I'm alive !")
                 .to("log:keep-alive");
 
+        from("direct:csimple-hello")
+                .setBody().csimple("Hello ${body}");
+
     }
 
 }
diff --git a/integration-tests/core/src/test/java/org/apache/camel/quarkus/core/CoreTest.java b/integration-tests/core/src/test/java/org/apache/camel/quarkus/core/CoreTest.java
index 0ba16c9..a96f2fe 100644
--- a/integration-tests/core/src/test/java/org/apache/camel/quarkus/core/CoreTest.java
+++ b/integration-tests/core/src/test/java/org/apache/camel/quarkus/core/CoreTest.java
@@ -21,6 +21,7 @@ import java.net.HttpURLConnection;
 
 import io.quarkus.test.junit.QuarkusTest;
 import io.restassured.RestAssured;
+import io.restassured.http.ContentType;
 import io.restassured.response.Response;
 import org.apache.camel.support.DefaultLRUCacheFactory;
 import org.junit.jupiter.api.Test;
@@ -123,4 +124,15 @@ public class CoreTest {
     void testDefaultHeadersMapFactoryConfigured() {
         RestAssured.when().get("/test/headersmap-factory").then().body(is("true"));
     }
+
+    @Test
+    public void csimpleHello() {
+        RestAssured.given()
+                .body("Joe")
+                .contentType(ContentType.TEXT)
+                .post("/test/csimple-hello")
+                .then()
+                .body(is("Hello Joe"));
+    }
+
 }
diff --git a/integration-tests/main-xml-io/src/main/java/org/apache/camel/quarkus/main/CoreMainXmlIoResource.java b/integration-tests/main-xml-io/src/main/java/org/apache/camel/quarkus/main/CoreMainXmlIoResource.java
index d69e170..fb3ce7d 100644
--- a/integration-tests/main-xml-io/src/main/java/org/apache/camel/quarkus/main/CoreMainXmlIoResource.java
+++ b/integration-tests/main-xml-io/src/main/java/org/apache/camel/quarkus/main/CoreMainXmlIoResource.java
@@ -90,4 +90,12 @@ public class CoreMainXmlIoResource {
     public String namespaceAware(String body) {
         return template.requestBody("direct:namespace-aware", body, String.class);
     }
+
+    @Path("/csimple-xml-dsl")
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    public String csimpleXmlDsl(String body) {
+        return template.requestBody("direct:csimple-xml-dsl", body, String.class);
+    }
 }
diff --git a/integration-tests/main-xml-io/src/main/resources/routes/my-routes.xml b/integration-tests/main-xml-io/src/main/resources/routes/my-routes.xml
index 48feb17..ae9376a 100644
--- a/integration-tests/main-xml-io/src/main/resources/routes/my-routes.xml
+++ b/integration-tests/main-xml-io/src/main/resources/routes/my-routes.xml
@@ -41,4 +41,11 @@
         </setBody>
     </route>
 
+    <route id="csimple-xml-dsl">
+        <from uri="direct:csimple-xml-dsl"/>
+        <setBody>
+            <csimple>Hi ${body}</csimple>
+        </setBody>
+    </route>
+
 </routes>
\ No newline at end of file
diff --git a/integration-tests/main-xml-io/src/test/java/org/apache/camel/quarkus/main/CoreMainXmlIoTest.java b/integration-tests/main-xml-io/src/test/java/org/apache/camel/quarkus/main/CoreMainXmlIoTest.java
index 617c17b..c8c07c7 100644
--- a/integration-tests/main-xml-io/src/test/java/org/apache/camel/quarkus/main/CoreMainXmlIoTest.java
+++ b/integration-tests/main-xml-io/src/test/java/org/apache/camel/quarkus/main/CoreMainXmlIoTest.java
@@ -30,7 +30,7 @@ import org.apache.camel.xml.in.ModelParserXMLRoutesDefinitionLoader;
 import org.junit.jupiter.api.Test;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.hamcrest.core.Is.is;
+import static org.hamcrest.Matchers.is;
 
 @QuarkusTest
 public class CoreMainXmlIoTest {
@@ -75,4 +75,15 @@ public class CoreMainXmlIoTest {
                 .body(is("bar"));
 
     }
+
+    @Test
+    public void csimpleXml() {
+        RestAssured.given()
+                .body("Joe")
+                .contentType(ContentType.TEXT)
+                .post("/test/csimple-xml-dsl")
+                .then()
+                .body(is("Hi Joe"));
+    }
+
 }
diff --git a/integration-tests/main/src/test/java/org/apache/camel/quarkus/main/CoreMainTest.java b/integration-tests/main/src/test/java/org/apache/camel/quarkus/main/CoreMainTest.java
index 5ab20b9..5f6b344 100644
--- a/integration-tests/main/src/test/java/org/apache/camel/quarkus/main/CoreMainTest.java
+++ b/integration-tests/main/src/test/java/org/apache/camel/quarkus/main/CoreMainTest.java
@@ -104,7 +104,8 @@ public class CoreMainTest {
                 "file", "org.apache.camel.language.simple.FileLanguage",
                 "header", "org.apache.camel.language.header.HeaderLanguage",
                 "simple", "org.apache.camel.language.simple.SimpleLanguage",
-                "ref", "org.apache.camel.language.ref.RefLanguage"));
+                "ref", "org.apache.camel.language.ref.RefLanguage",
+                "csimple", "org.apache.camel.language.csimple.CSimpleLanguage"));
 
         Map<String, String> factoryFinderMap = p.getMap("factory-finder.class-map", String.class, String.class);