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/11 17:16:08 UTC

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

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 37c3e7216bd4578ebdd2b75ce608951cf94e1f37
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);