You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2019/08/19 16:36:06 UTC

[camel-k-runtime] 01/03: knative: add custom loader for sources

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

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

commit 638040cca4bb754a536f48c3c9c02e06e4796234
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Mon Aug 5 18:28:00 2019 +0200

    knative: add custom loader for sources
---
 camel-k-loader-knative/pom.xml                     | 186 +++++++++++++++++++++
 .../loader/knative/KnativeSourceRoutesLoader.java  | 101 +++++++++++
 .../org/apache/camel/k/loader/knative-source       |  18 ++
 .../apache/camel/k/loader/knative-source-groovy    |  18 ++
 .../org/apache/camel/k/loader/knative-source-java  |  18 ++
 .../org/apache/camel/k/loader/knative-source-js    |  18 ++
 .../org/apache/camel/k/loader/knative-source-kts   |  18 ++
 .../org/apache/camel/k/loader/knative-source-xml   |  18 ++
 .../org/apache/camel/k/loader/knative-source-yaml  |  18 ++
 .../knative/KnativeSourceRoutesLoaderTest.java     | 157 +++++++++++++++++
 .../src/test/resources/log4j2-test.xml             |  36 ++++
 .../src/test/resources/routes.groovy               |  19 +++
 .../src/test/resources/routes.java                 |  32 ++++
 .../src/test/resources/routes.js                   |  19 +++
 .../src/test/resources/routes.kts                  |  20 +++
 .../src/test/resources/routes.xml                  |  28 ++++
 .../src/test/resources/routes.yaml                 |  23 +++
 camel-k-runtime-bom/pom.xml                        |   5 +
 .../src/main/java/org/apache/camel/k/Source.java   |  15 +-
 .../org/apache/camel/k/support/RuntimeSupport.java |  28 +++-
 camel-k-runtime-knative/pom.xml                    |   5 +
 .../component/knative/KnativeEnvironment.java      |  25 +++
 pom.xml                                            |   8 +-
 .../maven/processors/CatalogProcessor3x.java       |  16 ++
 24 files changed, 840 insertions(+), 9 deletions(-)

diff --git a/camel-k-loader-knative/pom.xml b/camel-k-loader-knative/pom.xml
new file mode 100644
index 0000000..aed021c
--- /dev/null
+++ b/camel-k-loader-knative/pom.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>org.apache.camel.k</groupId>
+        <artifactId>camel-k-runtime-parent</artifactId>
+        <version>1.0.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-k-loader-knative</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-runtime-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-core-engine</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- ****************************** -->
+        <!--                                -->
+        <!-- TESTS                          -->
+        <!--                                -->
+        <!-- ****************************** -->
+
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-knative</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-yaml</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-xml</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-groovy</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-kotlin</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-js</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-java</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-properties</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-direct</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-log</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-mock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-undertow</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-cloud</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-endpointdsl</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy</artifactId>
+            <version>${groovy.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <version>${log4j2.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.gmavenplus</groupId>
+                <artifactId>gmavenplus-plugin</artifactId>
+                <version>${gmavenplus-plugin.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>addTestSources</goal>
+                            <goal>compileTests</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <invokeDynamic>true</invokeDynamic>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <useSystemClassLoader>false</useSystemClassLoader>
+                    <forkCount>0</forkCount>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/camel-k-loader-knative/src/main/java/org/apache/camel/k/loader/knative/KnativeSourceRoutesLoader.java b/camel-k-loader-knative/src/main/java/org/apache/camel/k/loader/knative/KnativeSourceRoutesLoader.java
new file mode 100644
index 0000000..7b106b5
--- /dev/null
+++ b/camel-k-loader-knative/src/main/java/org/apache/camel/k/loader/knative/KnativeSourceRoutesLoader.java
@@ -0,0 +1,101 @@
+/*
+ * 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.k.loader.knative;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.engine.AbstractCamelContext;
+import org.apache.camel.k.RoutesLoader;
+import org.apache.camel.k.Source;
+import org.apache.camel.k.support.RuntimeSupport;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.ToDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class KnativeSourceRoutesLoader implements RoutesLoader {
+    private static final Logger LOGGER = LoggerFactory.getLogger(KnativeSourceRoutesLoader.class);
+    private static final String LOADER_ID = "knative-source";
+    private static final String LANGUAGE_PREFIX = LOADER_ID + "-";
+
+    @Override
+    public List<String> getSupportedLanguages() {
+        return Arrays.asList(LANGUAGE_PREFIX + "yaml");
+    }
+
+    @Override
+    public RouteBuilder load(CamelContext camelContext, Source source) throws Exception {
+        if (LOADER_ID.equals(source.getLanguage())) {
+            throw new IllegalArgumentException("Cannot load source of type " +  source.getLanguage());
+        }
+
+        String languageId = source.getLanguage();
+        if (languageId.startsWith(LANGUAGE_PREFIX)) {
+            languageId = languageId.substring(LANGUAGE_PREFIX.length());
+        }
+
+        final RoutesLoader loader = RuntimeSupport.lookupLoaderByLanguage(camelContext, languageId);
+        final RouteBuilder builder = loader.load(camelContext, source);
+
+        return new RouteBuilder() {
+            @Override
+            public void setContext(CamelContext context) {
+                builder.setContext(context);
+            }
+
+            @Override
+            public void configure() throws Exception {
+            }
+
+            @Override
+            public void addRoutesToCamelContext(CamelContext context) throws Exception {
+                //TODO: this is a little hack as then configureRoutes will
+                //      be invoked twice: 1 by this hack and 1 by delegated
+                //      builder. Maybe, we should add builder lifecycle events.
+                List<RouteDefinition> definitions = builder.configureRoutes(context).getRoutes();
+
+                if (definitions.size() > 1) {
+                    throw new IllegalArgumentException("Cannot determine route top enrich");
+                }
+
+                final String sink = context.resolvePropertyPlaceholders("{{env:KNATIVE_SYNC:sink}}");
+                final String uri = String.format("knative://endpoint/%s", sink);
+                final RouteDefinition definition = definitions.get(0);
+
+                LOGGER.info("Add sink:{} to route:{}", uri,  definition.getId());
+
+                // assuming that route is linear like there's no content based routing
+                // or ant other EIP that would branch the flow
+                definition.getOutputs().add(new ToDefinition(uri));
+
+                //TODO: this is needed for java language because by default
+                //      camel main inspects route builders to detect beans
+                //      to be registered to the camel registry but as the
+                //      original builder is masked by this wrapping builder,
+                //      beans can't be automatically discovered
+                context.adapt(AbstractCamelContext.class)
+                    .getBeanPostProcessor()
+                    .postProcessBeforeInitialization(builder, builder.getClass().getName());
+
+                builder.addRoutesToCamelContext(context);
+            }
+        };
+    }
+}
diff --git a/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source
new file mode 100644
index 0000000..6245d34
--- /dev/null
+++ b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+class=org.apache.camel.k.loader.knative.KnativeSourceRoutesLoader
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-groovy b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-groovy
new file mode 100644
index 0000000..6245d34
--- /dev/null
+++ b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-groovy
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+class=org.apache.camel.k.loader.knative.KnativeSourceRoutesLoader
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-java b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-java
new file mode 100644
index 0000000..6245d34
--- /dev/null
+++ b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-java
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+class=org.apache.camel.k.loader.knative.KnativeSourceRoutesLoader
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-js b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-js
new file mode 100644
index 0000000..6245d34
--- /dev/null
+++ b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-js
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+class=org.apache.camel.k.loader.knative.KnativeSourceRoutesLoader
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-kts b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-kts
new file mode 100644
index 0000000..6245d34
--- /dev/null
+++ b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-kts
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+class=org.apache.camel.k.loader.knative.KnativeSourceRoutesLoader
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-xml b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-xml
new file mode 100644
index 0000000..6245d34
--- /dev/null
+++ b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-xml
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+class=org.apache.camel.k.loader.knative.KnativeSourceRoutesLoader
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-yaml b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-yaml
new file mode 100644
index 0000000..6245d34
--- /dev/null
+++ b/camel-k-loader-knative/src/main/resources/META-INF/services/org/apache/camel/k/loader/knative-source-yaml
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+class=org.apache.camel.k.loader.knative.KnativeSourceRoutesLoader
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/test/java/org/apache/camel/k/loader/knative/KnativeSourceRoutesLoaderTest.java b/camel-k-loader-knative/src/test/java/org/apache/camel/k/loader/knative/KnativeSourceRoutesLoaderTest.java
new file mode 100644
index 0000000..a7d8f2a
--- /dev/null
+++ b/camel-k-loader-knative/src/test/java/org/apache/camel/k/loader/knative/KnativeSourceRoutesLoaderTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.k.loader.knative;
+
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Stream;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.knative.KnativeComponent;
+import org.apache.camel.component.knative.KnativeEnvironment;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.k.RoutesLoader;
+import org.apache.camel.k.Source;
+import org.apache.camel.k.support.RuntimeSupport;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.test.AvailablePortFinder;
+import org.junit.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class KnativeSourceRoutesLoaderTest {
+    private static final Logger LOGGER = LoggerFactory.getLogger(KnativeSourceRoutesLoaderTest.class);
+
+    static Stream<Arguments> parameters() {
+        return Stream.of(
+            Arguments.arguments("classpath:routes.yaml?loader=knative-source"),
+            Arguments.arguments("classpath:routes.yaml?language=knative-source-yaml"),
+            Arguments.arguments("classpath:routes.xml?loader=knative-source"),
+            Arguments.arguments("classpath:routes.xml?language=knative-source-xml"),
+            Arguments.arguments("classpath:routes.groovy?loader=knative-source"),
+            Arguments.arguments("classpath:routes.groovy?language=knative-source-groovy"),
+            Arguments.arguments("classpath:routes.kts?loader=knative-source"),
+            Arguments.arguments("classpath:routes.kts?language=knative-source-kts"),
+            Arguments.arguments("classpath:routes.js?loader=knative-source"),
+            Arguments.arguments("classpath:routes.js?language=knative-source-js"),
+            Arguments.arguments("classpath:routes.java?name=MyRoutes.java&loader=knative-source"),
+            Arguments.arguments("classpath:routes.java?name=MyRoutes.java&language=knative-source-java")
+        ).sequential();
+    }
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    public void testWrapLoader(String uri) throws Exception {
+        LOGGER.info("uri: {}", uri);
+
+        final int port = AvailablePortFinder.getNextAvailable();
+        final String data = UUID.randomUUID().toString();
+
+        KnativeComponent component = new KnativeComponent();
+        component.setEnvironment(KnativeEnvironment.on(
+            KnativeEnvironment.httpEndpoint("sink", "localhost", port)
+        ));
+
+        CamelContext context = new DefaultCamelContext();
+        context.disableJMX();
+        context.setStreamCaching(true);
+        context.addComponent("knative", component);
+
+        Source source = Source.create(uri);
+        RoutesLoader loader = RuntimeSupport.loaderFor(context, source);
+        RouteBuilder builder = loader.load(context, source);
+
+        assertThat(loader).isInstanceOf(KnativeSourceRoutesLoader.class);
+        assertThat(builder).isNotNull();
+
+        try {
+            context.addRoutes(builder);
+            context.addRoutes(new RouteBuilder() {
+                @Override
+                public void configure() throws Exception {
+                    fromF("undertow:http://localhost:%d", port)
+                        .routeId("http")
+                        .to("mock:result");
+                }
+            });
+            context.start();
+
+            List<RouteDefinition> definitions = context.adapt(ModelCamelContext.class).getRouteDefinitions();
+
+            assertThat(definitions).hasSize(2);
+            assertThat(definitions).first().satisfies(d -> {
+                assertThat(d.getOutputs()).last().hasFieldOrPropertyWithValue(
+                    "endpointUri",
+                    "knative://endpoint/sink"
+                );
+            });
+
+            MockEndpoint mock = context.getEndpoint("mock:result", MockEndpoint.class);
+            mock.expectedMessageCount(1);
+            mock.expectedBodiesReceived(data);
+
+            context.createProducerTemplate().sendBodyAndHeader(
+                "direct:start",
+                "",
+                "MyHeader",
+                data);
+
+            mock.assertIsSatisfied();
+        } finally {
+            context.stop();
+        }
+    }
+
+    @Test
+    public void testWrapLoaderWithBeanRegistration() throws Exception {
+        final int port = AvailablePortFinder.getNextAvailable();
+
+        KnativeComponent component = new KnativeComponent();
+        component.setEnvironment(KnativeEnvironment.on(
+            KnativeEnvironment.httpEndpoint("sink", "localhost", port)
+        ));
+
+        CamelContext context = new DefaultCamelContext();
+        context.disableJMX();
+        context.setStreamCaching(true);
+        context.addComponent("knative", component);
+
+        Source source = Source.create("classpath:routes.java?name=MyRoutes.java&loader=knative-source");
+        RoutesLoader loader = RuntimeSupport.loaderFor(context, source);
+        RouteBuilder builder = loader.load(context, source);
+
+        assertThat(loader).isInstanceOf(KnativeSourceRoutesLoader.class);
+        assertThat(builder).isNotNull();
+
+        try {
+            context.addRoutes(builder);
+            context.start();
+
+            assertThat(context.getRegistry().lookupByName("my-bean")).isInstanceOfSatisfying(String.class, "my-bean-string"::equals);
+        } finally {
+            context.stop();
+        }
+    }
+}
diff --git a/camel-k-loader-knative/src/test/resources/log4j2-test.xml b/camel-k-loader-knative/src/test/resources/log4j2-test.xml
new file mode 100644
index 0000000..42e5526
--- /dev/null
+++ b/camel-k-loader-knative/src/test/resources/log4j2-test.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<Configuration status="INFO">
+  <Appenders>
+    <Console name="STDOUT" target="SYSTEM_OUT">
+      <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}|%-5level|%t|%c{1} - %msg%n"/>
+    </Console>
+    <Null name="NONE"/>
+  </Appenders>
+
+
+  <Loggers>
+    <Root level="INFO">
+      <!--<AppenderRef ref="STDOUT"/>-->
+      <AppenderRef ref="NONE"/>
+    </Root>
+  </Loggers>
+
+</Configuration>
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/test/resources/routes.groovy b/camel-k-loader-knative/src/test/resources/routes.groovy
new file mode 100644
index 0000000..d4e7b78
--- /dev/null
+++ b/camel-k-loader-knative/src/test/resources/routes.groovy
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+from('direct:start')
+    .setBody().simple('${header[MyHeader]}')
+    .to('log:knative')
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/test/resources/routes.java b/camel-k-loader-knative/src/test/resources/routes.java
new file mode 100644
index 0000000..ffa38de
--- /dev/null
+++ b/camel-k-loader-knative/src/test/resources/routes.java
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.builder.RouteBuilder;
+
+public class MyRoutes extends RouteBuilder {
+    @Override
+    public void configure() throws Exception {
+        from("direct:start")
+            .setBody().simple("${header[MyHeader]}")
+            .to("log:knative");
+    }
+
+    @BindToRegistry("my-bean")
+    public static String myBean() {
+        return "my-bean-string";
+    }
+}
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/test/resources/routes.js b/camel-k-loader-knative/src/test/resources/routes.js
new file mode 100644
index 0000000..75de4de
--- /dev/null
+++ b/camel-k-loader-knative/src/test/resources/routes.js
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+from('direct:start')
+    .setBody().simple('${header[MyHeader]}')
+    .to('log:knative');
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/test/resources/routes.kts b/camel-k-loader-knative/src/test/resources/routes.kts
new file mode 100644
index 0000000..48b8b1c
--- /dev/null
+++ b/camel-k-loader-knative/src/test/resources/routes.kts
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+from("direct:start")
+    .setBody().simple("\${header[MyHeader]}")
+    .to("log:knative")
+
diff --git a/camel-k-loader-knative/src/test/resources/routes.xml b/camel-k-loader-knative/src/test/resources/routes.xml
new file mode 100644
index 0000000..f0d953a
--- /dev/null
+++ b/camel-k-loader-knative/src/test/resources/routes.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<routes xmlns="http://camel.apache.org/schema/spring">
+  <route>
+    <from uri="direct:start"/>
+    <setBody>
+      <simple>${header[MyHeader]}</simple>
+    </setBody>
+    <to uri="log:knative"/>
+  </route>
+</routes>
\ No newline at end of file
diff --git a/camel-k-loader-knative/src/test/resources/routes.yaml b/camel-k-loader-knative/src/test/resources/routes.yaml
new file mode 100644
index 0000000..b1fd1f8
--- /dev/null
+++ b/camel-k-loader-knative/src/test/resources/routes.yaml
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+- from:
+    uri: "direct:start"
+    steps:
+      - set-body:
+          simple: "${header[MyHeader]}"
+      - to: "log:knative"
diff --git a/camel-k-runtime-bom/pom.xml b/camel-k-runtime-bom/pom.xml
index eab16f3..bf06606 100644
--- a/camel-k-runtime-bom/pom.xml
+++ b/camel-k-runtime-bom/pom.xml
@@ -163,6 +163,11 @@
                 <artifactId>camel-k-loader-java</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.camel.k</groupId>
+                <artifactId>camel-k-loader-knative</artifactId>
+                <version>${project.version}</version>
+            </dependency>
 
             <!-- legacy -->
             <dependency>
diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/Source.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/Source.java
index 6f34870..83e94f9 100644
--- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/Source.java
+++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/Source.java
@@ -17,6 +17,7 @@
 package org.apache.camel.k;
 
 import java.util.Map;
+import java.util.Optional;
 
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.URISupport;
@@ -26,12 +27,14 @@ public final class Source {
     private final String name;
     private final String location;
     private final String language;
+    private final String loader;
     private final boolean compressed;
 
-    private Source(String name, String location, String language, boolean compression) {
+    private Source(String name, String location, String language, String loader, boolean compression) {
         this.name = name;
         this.location = location;
         this.language = language;
+        this.loader = loader;
         this.compressed = compression;
     }
 
@@ -51,12 +54,17 @@ public final class Source {
         return compressed;
     }
 
+    public Optional<String> getLoader() {
+        return Optional.ofNullable(loader);
+    }
+
     @Override
     public String toString() {
         return "Source{"
             + "location='" + location + '\''
             + ", language=" + language
-            + " , compressed=" + compressed
+            + ", loader=" + loader
+            + ", compressed=" + compressed
             + '}';
     }
 
@@ -71,6 +79,7 @@ public final class Source {
         final Map<String, Object> params = URISupport.parseQuery(query);
         final String languageName = (String) params.get("language");
         final String compression = (String) params.get("compression");
+        final String loader = (String) params.get("loader");
 
 
         String language = languageName;
@@ -92,6 +101,6 @@ public final class Source {
             }
         }
 
-        return new Source(name, location, language, Boolean.valueOf(compression));
+        return new Source(name, location, language, loader, Boolean.valueOf(compression));
     }
 }
diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java
index 8f9a6b4..796b96e 100644
--- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java
+++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/RuntimeSupport.java
@@ -152,21 +152,39 @@ public final class RuntimeSupport {
     // *********************************
 
     public static RoutesLoader loaderFor(CamelContext context, Source source) {
+        return source.getLoader().map(
+            loaderId -> lookupLoaderById(context, loaderId)
+        ).orElseGet(
+            () -> lookupLoaderByLanguage(context, source.getLanguage())
+        );
+    }
+
+    public static RoutesLoader lookupLoaderById(CamelContext context, String loaderId) {
+        RoutesLoader answer = context.getRegistry().findByTypeWithName(RoutesLoader.class).get(loaderId);
+        if (answer == null) {
+            return lookupLoaderFromResource(context, loaderId);
+        }
+
+        return answer;
+    }
+
+    public static RoutesLoader lookupLoaderByLanguage(CamelContext context, String language) {
         return  context.getRegistry().findByType(RoutesLoader.class).stream()
-            .filter(rl -> rl.getSupportedLanguages().contains(source.getLanguage()))
+            .filter(rl -> rl.getSupportedLanguages().contains(language))
             .findFirst()
-            .orElseGet(() -> lookupLoaderFromResource(context, source));
+            .orElseGet(() -> lookupLoaderFromResource(context, language));
+
     }
 
-    public static RoutesLoader lookupLoaderFromResource(CamelContext context, Source source) {
+    public static RoutesLoader lookupLoaderFromResource(CamelContext context, String loaderId) {
         final FactoryFinder finder;
         final RoutesLoader loader;
 
         try {
             finder = context.adapt(ExtendedCamelContext.class).getFactoryFinder(Constants.ROUTES_LOADER_RESOURCE_PATH);
-            loader = (RoutesLoader)finder.newInstance(source.getLanguage());
+            loader = (RoutesLoader)finder.newInstance(loaderId);
         } catch (NoFactoryAvailableException e) {
-            throw new IllegalArgumentException("Unable to find loader for: " + source, e);
+            throw new IllegalArgumentException("Unable to find loader for: " + loaderId, e);
         }
 
         return loader;
diff --git a/camel-k-runtime-knative/pom.xml b/camel-k-runtime-knative/pom.xml
index e11407d..ab1bfb1 100644
--- a/camel-k-runtime-knative/pom.xml
+++ b/camel-k-runtime-knative/pom.xml
@@ -41,6 +41,11 @@
             <artifactId>camel-k-loader-yaml</artifactId>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-loader-knative</artifactId>
+            <optional>true</optional>
+        </dependency>
 
         <dependency>
             <groupId>org.apache.camel</groupId>
diff --git a/camel-knative/src/main/java/org/apache/camel/component/knative/KnativeEnvironment.java b/camel-knative/src/main/java/org/apache/camel/component/knative/KnativeEnvironment.java
index 19c11af..814740a 100644
--- a/camel-knative/src/main/java/org/apache/camel/component/knative/KnativeEnvironment.java
+++ b/camel-knative/src/main/java/org/apache/camel/component/knative/KnativeEnvironment.java
@@ -20,6 +20,7 @@ import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -170,6 +171,30 @@ public class KnativeEnvironment {
         }
     }
 
+    public static KnativeServiceDefinition httpEndpoint(String name, String host, int port) {
+        return new KnativeEnvironment.KnativeServiceDefinition(
+            Knative.Type.endpoint,
+            Knative.Protocol.http,
+            name,
+            host,
+            port,
+            Collections.emptyMap());
+    }
+
+    public static KnativeServiceDefinition httpChannel(String name, String host, int port) {
+        return new KnativeEnvironment.KnativeServiceDefinition(
+            Knative.Type.channel,
+            Knative.Protocol.http,
+            name,
+            host,
+            port,
+            Collections.emptyMap());
+    }
+
+    public static KnativeEnvironment on(KnativeServiceDefinition... definitions) {
+        return new KnativeEnvironment(Arrays.asList(definitions));
+    }
+
     // ************************
     //
     // Types
diff --git a/pom.xml b/pom.xml
index 9458562..8feca0b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -173,7 +173,7 @@
                                 </sourceDirectories>
                                 <headerLocation>header-java.txt</headerLocation>
                                 <includes>**/*.java,**/*.groovy,**/*.scala,**/*.properties,**/*.xml,**/*.xsd</includes>
-                                <excludes>**/MyRoutes*.java</excludes>
+                                <excludes>**/MyRoutes*.java,**/routes*.java</excludes>
                             </configuration>
                             <goals>
                                 <goal>checkstyle</goal>
@@ -197,6 +197,7 @@
         <module>camel-k-loader-js</module>
         <module>camel-k-loader-xml</module>
         <module>camel-k-loader-java</module>
+        <module>camel-k-loader-knative</module>
 
         <module>camel-k-runtime-health</module>
         <module>camel-k-runtime-servlet</module>
@@ -321,6 +322,11 @@
                 <artifactId>camel-k-loader-java</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.camel.k</groupId>
+                <artifactId>camel-k-loader-knative</artifactId>
+                <version>${project.version}</version>
+            </dependency>
 
             <!-- legacy -->
             <dependency>
diff --git a/tooling/camel-k-maven-plugin/src/main/java/org/apache/camel/k/tooling/maven/processors/CatalogProcessor3x.java b/tooling/camel-k-maven-plugin/src/main/java/org/apache/camel/k/tooling/maven/processors/CatalogProcessor3x.java
index 5fa5398..49b0adb 100644
--- a/tooling/camel-k-maven-plugin/src/main/java/org/apache/camel/k/tooling/maven/processors/CatalogProcessor3x.java
+++ b/tooling/camel-k-maven-plugin/src/main/java/org/apache/camel/k/tooling/maven/processors/CatalogProcessor3x.java
@@ -182,6 +182,21 @@ public class CatalogProcessor3x implements CatalogProcessor {
 
         // ************************
         //
+        // camel-k-loader-knative
+        //
+        // ************************
+
+        {
+            CamelArtifact artifact = new CamelArtifact();
+            artifact.setGroupId("org.apache.camel.k");
+            artifact.setArtifactId("camel-k-loader-knative");
+            artifact.setVersion(project.getVersion());
+
+            artifacts.put(artifact.getArtifactId(), artifact);
+        }
+
+        // ************************
+        //
         // camel-knative
         //
         // ************************
@@ -243,6 +258,7 @@ public class CatalogProcessor3x implements CatalogProcessor {
             artifact.addDependency("org.apache.camel", "camel-cloud");
             artifact.addDependency("org.apache.camel", "camel-http-common");
             artifact.addDependency("org.apache.camel.k", "camel-k-loader-yaml");
+            artifact.addDependency("org.apache.camel.k", "camel-k-loader-knative");
             artifact.addDependency("org.apache.camel.k", "camel-knative");
             artifact.addDependency("org.apache.camel.k", "camel-knative-http");