diff --git a/camel-k-loader-groovy/src/test/groovy/org/apache/camel/k/loader/groovy/LoaderTest.groovy b/camel-k-loader-groovy/src/test/groovy/org/apache/camel/k/loader/groovy/LoaderTest.groovy
index c637fda28..7479a586c 100644
--- a/camel-k-loader-groovy/src/test/groovy/org/apache/camel/k/loader/groovy/LoaderTest.groovy
+++ b/camel-k-loader-groovy/src/test/groovy/org/apache/camel/k/loader/groovy/LoaderTest.groovy
@@ -22,7 +22,7 @@ import org.apache.camel.builder.RouteBuilder
import org.apache.camel.impl.DefaultCamelContext
import org.apache.camel.k.Runtime
import org.apache.camel.k.Sources
-import org.apache.camel.k.listener.RoutesConfigurer
+import org.apache.camel.k.support.SourcesSupport
import org.apache.camel.model.FromDefinition
import org.apache.camel.model.ToDefinition
import spock.lang.Specification
@@ -35,7 +35,7 @@ class LoaderTest extends Specification {
def source = Sources.fromURI("classpath:routes.groovy")
when:
- def loader = RoutesConfigurer.load(runtime, source)
+ def loader = SourcesSupport.load(runtime, source)
then:
loader instanceof GroovySourceLoader
@@ -60,7 +60,7 @@ class LoaderTest extends Specification {
def source = Sources.fromURI("classpath:routes-with-endpoint-dsl.groovy")
when:
- def loader = RoutesConfigurer.load(runtime, source)
+ def loader = SourcesSupport.load(runtime, source)
then:
loader instanceof GroovySourceLoader
diff --git a/camel-k-loader-groovy/src/test/groovy/org/apache/camel/k/loader/groovy/dsl/IntegrationTest.groovy b/camel-k-loader-groovy/src/test/groovy/org/apache/camel/k/loader/groovy/dsl/IntegrationTest.groovy
index 11f29acca..433a8622e 100644
--- a/camel-k-loader-groovy/src/test/groovy/org/apache/camel/k/loader/groovy/dsl/IntegrationTest.groovy
+++ b/camel-k-loader-groovy/src/test/groovy/org/apache/camel/k/loader/groovy/dsl/IntegrationTest.groovy
@@ -36,7 +36,7 @@ import spock.lang.Specification
import javax.sql.DataSource
-import static org.apache.camel.k.listener.RoutesConfigurer.forRoutes
+import static org.apache.camel.k.support.SourcesSupport.forRoutes
class IntegrationTest extends Specification {
diff --git a/camel-k-loader-java/src/test/java/org/apache/camel/k/loader/java/RoutesLoaderTest.java b/camel-k-loader-java/src/test/java/org/apache/camel/k/loader/java/RoutesLoaderTest.java
index 2519babc0..a97070bf9 100644
--- a/camel-k-loader-java/src/test/java/org/apache/camel/k/loader/java/RoutesLoaderTest.java
+++ b/camel-k-loader-java/src/test/java/org/apache/camel/k/loader/java/RoutesLoaderTest.java
@@ -30,7 +30,7 @@
import org.apache.camel.k.Source;
import org.apache.camel.k.SourceLoader;
import org.apache.camel.k.Sources;
-import org.apache.camel.k.listener.RoutesConfigurer;
+import org.apache.camel.k.support.SourcesSupport;
import org.apache.camel.model.ProcessDefinition;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.model.SetBodyDefinition;
@@ -47,7 +47,7 @@ public class RoutesLoaderTest {
public void testLoadJavaWithNestedClass() throws Exception {
TestRuntime runtime = new TestRuntime();
Source source = Sources.fromURI("classpath:MyRoutesWithNestedClass.java");
- SourceLoader loader = RoutesConfigurer.load(runtime, source);
+ SourceLoader loader = SourcesSupport.load(runtime, source);
assertThat(loader).isInstanceOf(JavaSourceLoader.class);
assertThat(runtime.builders).hasSize(1);
@@ -69,7 +69,7 @@ public void testLoadJavaWithNestedClass() throws Exception {
public void testLoadJavaWithRestConfiguration() throws Exception {
TestRuntime runtime = new TestRuntime();
Source source = Sources.fromURI("classpath:MyRoutesWithRestConfiguration.java");
- SourceLoader loader = RoutesConfigurer.load(runtime, source);
+ SourceLoader loader = SourcesSupport.load(runtime, source);
assertThat(loader).isInstanceOf(JavaSourceLoader.class);
assertThat(runtime.builders).hasSize(1);
@@ -84,7 +84,7 @@ public void testLoadJavaWithRestConfiguration() throws Exception {
public void testLoadJavaConfiguration() throws Exception {
TestRuntime runtime = new TestRuntime();
Source source = Sources.fromURI("classpath:MyRoutesConfig.java");
- SourceLoader loader = RoutesConfigurer.load(runtime, source);
+ SourceLoader loader = SourcesSupport.load(runtime, source);
assertThat(loader).isInstanceOf(JavaSourceLoader.class);
assertThat(runtime.builders).isEmpty();
@@ -95,7 +95,7 @@ public void testLoadJavaConfiguration() throws Exception {
public void testLoadJavaWithModel() throws Exception {
TestRuntime runtime = new TestRuntime();
Source source = Sources.fromURI("classpath:MyRoutesWithModel.java");
- SourceLoader loader = RoutesConfigurer.load(runtime, source);
+ SourceLoader loader = SourcesSupport.load(runtime, source);
assertThat(loader).isInstanceOf(JavaSourceLoader.class);
assertThat(runtime.builders).hasSize(1);
@@ -114,7 +114,7 @@ public void testLoadJavaWithModel() throws Exception {
public void testLoadJavaWithNestedType() throws Exception {
TestRuntime runtime = new TestRuntime();
Source source = Sources.fromURI("classpath:MyRoutesWithNestedTypes.java");
- SourceLoader loader = RoutesConfigurer.load(runtime, source);
+ SourceLoader loader = SourcesSupport.load(runtime, source);
assertThat(loader).isInstanceOf(JavaSourceLoader.class);
assertThat(runtime.builders).hasSize(1);
@@ -129,7 +129,7 @@ public void testLoadJavaWithNestedType() throws Exception {
public void testLoaders(String location, Class extends SourceLoader> type) throws Exception {
TestRuntime runtime = new TestRuntime();
Source source = Sources.fromURI(location);
- SourceLoader loader = RoutesConfigurer.load(runtime, source);
+ SourceLoader loader = SourcesSupport.load(runtime, source);
assertThat(loader).isInstanceOf(type);
assertThat(runtime.builders).hasSize(1);
diff --git a/camel-k-loader-js/src/test/java/org/apache/camel/k/loader/js/RoutesLoaderTest.java b/camel-k-loader-js/src/test/java/org/apache/camel/k/loader/js/RoutesLoaderTest.java
index 846daad03..bd92fe93d 100644
--- a/camel-k-loader-js/src/test/java/org/apache/camel/k/loader/js/RoutesLoaderTest.java
+++ b/camel-k-loader-js/src/test/java/org/apache/camel/k/loader/js/RoutesLoaderTest.java
@@ -29,7 +29,7 @@
import org.apache.camel.k.Source;
import org.apache.camel.k.SourceLoader;
import org.apache.camel.k.Sources;
-import org.apache.camel.k.listener.RoutesConfigurer;
+import org.apache.camel.k.support.SourcesSupport;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.model.ToDefinition;
import org.junit.jupiter.params.ParameterizedTest;
@@ -44,7 +44,7 @@ public class RoutesLoaderTest {
public void testLoaders(String location, Class extends SourceLoader> type) throws Exception {
TestRuntime runtime = new TestRuntime();
Source source = Sources.fromURI(location);
- SourceLoader loader = RoutesConfigurer.load(runtime, source);
+ SourceLoader loader = SourcesSupport.load(runtime, source);
assertThat(loader).isInstanceOf(type);
assertThat(runtime.builders).hasSize(1);
diff --git a/camel-k-loader-js/src/test/java/org/apache/camel/k/loader/js/dsl/IntegrationTest.java b/camel-k-loader-js/src/test/java/org/apache/camel/k/loader/js/dsl/IntegrationTest.java
index 326ddc5c1..c3199dbd0 100644
--- a/camel-k-loader-js/src/test/java/org/apache/camel/k/loader/js/dsl/IntegrationTest.java
+++ b/camel-k-loader-js/src/test/java/org/apache/camel/k/loader/js/dsl/IntegrationTest.java
@@ -23,7 +23,7 @@
import org.apache.camel.component.seda.SedaComponent;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.k.Runtime;
-import org.apache.camel.k.listener.RoutesConfigurer;
+import org.apache.camel.k.support.SourcesSupport;
import org.apache.camel.model.FromDefinition;
import org.apache.camel.model.ModelCamelContext;
import org.apache.camel.model.RouteDefinition;
@@ -55,7 +55,7 @@ public void shutDown() {
}
private void configureRoutes(String... routes) {
- RoutesConfigurer.forRoutes(routes).accept(Runtime.Phase.ConfigureRoutes, runtime);
+ SourcesSupport.forRoutes(routes).accept(Runtime.Phase.ConfigureRoutes, runtime);
}
@Test
diff --git a/camel-k-loader-kotlin/camel-k-loader-kotlin-itests/src/test/java/org/apache/camel/k/loader/kotlin/itests/LoaderTest.java b/camel-k-loader-kotlin/camel-k-loader-kotlin-itests/src/test/java/org/apache/camel/k/loader/kotlin/itests/LoaderTest.java
index bc25de785..38be00067 100644
--- a/camel-k-loader-kotlin/camel-k-loader-kotlin-itests/src/test/java/org/apache/camel/k/loader/kotlin/itests/LoaderTest.java
+++ b/camel-k-loader-kotlin/camel-k-loader-kotlin-itests/src/test/java/org/apache/camel/k/loader/kotlin/itests/LoaderTest.java
@@ -21,7 +21,7 @@
import org.apache.camel.k.Runtime;
import org.apache.camel.k.Source;
import org.apache.camel.k.Sources;
-import org.apache.camel.k.listener.RoutesConfigurer;
+import org.apache.camel.k.support.SourcesSupport;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -33,7 +33,7 @@ public void testLoad() throws Exception {
final Runtime runtime = Runtime.on(context);
final Source source = Sources.fromURI("classpath:routes.kts");
- RoutesConfigurer.load(runtime, source);
+ SourcesSupport.load(runtime, source);
try {
context.start();
diff --git a/camel-k-loader-kotlin/camel-k-loader-kotlin/src/test/kotlin/org/apache/camel/k/loader/kotlin/LoaderTest.kt b/camel-k-loader-kotlin/camel-k-loader-kotlin/src/test/kotlin/org/apache/camel/k/loader/kotlin/LoaderTest.kt
index d6840fae0..0bdbd63d6 100644
--- a/camel-k-loader-kotlin/camel-k-loader-kotlin/src/test/kotlin/org/apache/camel/k/loader/kotlin/LoaderTest.kt
+++ b/camel-k-loader-kotlin/camel-k-loader-kotlin/src/test/kotlin/org/apache/camel/k/loader/kotlin/LoaderTest.kt
@@ -22,7 +22,7 @@ import org.apache.camel.builder.RouteBuilder
import org.apache.camel.impl.DefaultCamelContext
import org.apache.camel.k.Runtime
import org.apache.camel.k.Sources
-import org.apache.camel.k.listener.RoutesConfigurer
+import org.apache.camel.k.support.SourcesSupport
import org.apache.camel.model.ProcessDefinition
import org.apache.camel.model.ToDefinition
import org.assertj.core.api.Assertions.assertThat
@@ -35,7 +35,7 @@ class LoaderTest {
fun `load routes`() {
val runtime = TestRuntime()
val source = Sources.fromURI("classpath:routes.kts")
- val loader = RoutesConfigurer.load(runtime, source)
+ val loader = SourcesSupport.load(runtime, source)
assertThat(loader).isInstanceOf(KotlinSourceLoader::class.java)
assertThat(runtime.builders).hasSize(1)
@@ -56,7 +56,7 @@ class LoaderTest {
fun `load routes with endpoint dsl`() {
val runtime = TestRuntime()
val source = Sources.fromURI("classpath:routes-with-endpoint-dsl.kts")
- val loader = RoutesConfigurer.load(runtime, source)
+ val loader = SourcesSupport.load(runtime, source)
assertThat(loader).isInstanceOf(KotlinSourceLoader::class.java)
assertThat(runtime.builders).hasSize(1)
diff --git a/camel-k-loader-kotlin/camel-k-loader-kotlin/src/test/kotlin/org/apache/camel/k/loader/kotlin/dsl/IntegrationTest.kt b/camel-k-loader-kotlin/camel-k-loader-kotlin/src/test/kotlin/org/apache/camel/k/loader/kotlin/dsl/IntegrationTest.kt
index 95c0da7e8..cc19fde75 100644
--- a/camel-k-loader-kotlin/camel-k-loader-kotlin/src/test/kotlin/org/apache/camel/k/loader/kotlin/dsl/IntegrationTest.kt
+++ b/camel-k-loader-kotlin/camel-k-loader-kotlin/src/test/kotlin/org/apache/camel/k/loader/kotlin/dsl/IntegrationTest.kt
@@ -24,7 +24,7 @@ import org.apache.camel.component.log.LogComponent
import org.apache.camel.component.seda.SedaComponent
import org.apache.camel.impl.DefaultCamelContext
import org.apache.camel.k.Runtime
-import org.apache.camel.k.listener.RoutesConfigurer.forRoutes
+import org.apache.camel.k.support.SourcesSupport.forRoutes
import org.apache.camel.language.bean.BeanLanguage
import org.apache.camel.model.ModelCamelContext
import org.apache.camel.model.rest.GetVerbDefinition
diff --git a/camel-k-main/camel-k-runtime-main/pom.xml b/camel-k-main/camel-k-runtime-main/pom.xml
index ad8438fd2..79b93296a 100644
--- a/camel-k-main/camel-k-runtime-main/pom.xml
+++ b/camel-k-main/camel-k-runtime-main/pom.xml
@@ -148,6 +148,11 @@
camel-k-runtime-knative
test
+
+ org.apache.camel.k
+ camel-kamelet
+ test
+
diff --git a/camel-k-main/camel-k-runtime-main/src/main/java/org/apache/camel/k/main/ApplicationRuntime.java b/camel-k-main/camel-k-runtime-main/src/main/java/org/apache/camel/k/main/ApplicationRuntime.java
index 7d68d0e5a..5ba138f92 100644
--- a/camel-k-main/camel-k-runtime-main/src/main/java/org/apache/camel/k/main/ApplicationRuntime.java
+++ b/camel-k-main/camel-k-runtime-main/src/main/java/org/apache/camel/k/main/ApplicationRuntime.java
@@ -30,14 +30,12 @@
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.k.CompositeClassloader;
import org.apache.camel.k.Runtime;
-import org.apache.camel.k.support.PropertiesSupport;
import org.apache.camel.main.BaseMainSupport;
import org.apache.camel.main.MainSupport;
import org.apache.camel.main.RoutesCollector;
import org.apache.camel.model.RouteTemplatesDefinition;
import org.apache.camel.model.RoutesDefinition;
import org.apache.camel.model.rest.RestsDefinition;
-import org.apache.camel.spi.HasId;
import org.apache.camel.util.function.ThrowingConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -103,15 +101,6 @@ public void addListeners(Iterable listeners) {
}
public void addListener(Runtime.Listener listener) {
- if (listener instanceof HasId) {
- String id = ((HasId) listener).getId();
- if (!id.endsWith(".")) {
- id = id + ".";
- }
-
- PropertiesSupport.bindProperties(getCamelContext(), listener, id);
- }
-
LOGGER.info("Add listener: {}", listener);
this.listeners.add(listener);
diff --git a/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/RuntimeTest.java b/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/RuntimeTest.java
index 59681e641..e7301576f 100644
--- a/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/RuntimeTest.java
+++ b/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/RuntimeTest.java
@@ -17,6 +17,7 @@
package org.apache.camel.k.main;
import java.util.List;
+import java.util.Map;
import org.apache.camel.CamelContext;
import org.apache.camel.Route;
@@ -24,9 +25,12 @@
import org.apache.camel.component.knative.spi.Knative;
import org.apache.camel.component.knative.spi.KnativeEnvironment;
import org.apache.camel.k.Runtime;
+import org.apache.camel.k.SourceType;
import org.apache.camel.k.http.PlatformHttpServiceContextCustomizer;
import org.apache.camel.k.listener.ContextConfigurer;
-import org.apache.camel.k.listener.RoutesConfigurer;
+import org.apache.camel.k.listener.SourcesConfigurer;
+import org.apache.camel.k.main.support.MyBean;
+import org.apache.camel.k.support.SourcesSupport;
import org.apache.camel.k.test.AvailablePortFinder;
import org.apache.camel.model.ModelCamelContext;
import org.apache.camel.model.ToDefinition;
@@ -55,7 +59,7 @@ public void cleanUp() throws Exception {
@Test
void testLoadMultipleRoutes() throws Exception {
runtime.addListener(new ContextConfigurer());
- runtime.addListener(RoutesConfigurer.forRoutes("classpath:r1.js", "classpath:r2.mytype?language=js"));
+ runtime.addListener(SourcesSupport.forRoutes("classpath:r1.js", "classpath:r2.mytype?language=js"));
runtime.addListener(Runtime.Phase.Started, r -> {
CamelContext context = r.getCamelContext();
List routes = context.getRoutes();
@@ -73,7 +77,7 @@ void testLoadMultipleRoutes() throws Exception {
@Test
void testLoadRouteAndRest() throws Exception {
runtime.addListener(new ContextConfigurer());
- runtime.addListener(RoutesConfigurer.forRoutes("classpath:routes.xml", "classpath:rests.xml"));
+ runtime.addListener(SourcesSupport.forRoutes("classpath:routes.xml", "classpath:rests.xml"));
runtime.addListener(Runtime.Phase.Started, r -> {
CamelContext context = r.getCamelContext();
@@ -93,16 +97,55 @@ void testLoadRouteWithExpression() throws Exception {
));
runtime.addListener(new ContextConfigurer());
- runtime.addListener(RoutesConfigurer.forRoutes("classpath:routes-with-expression.xml"));
- runtime.addListener(Runtime.Phase.Started, r -> runtime.stop());
+ runtime.addListener(SourcesSupport.forRoutes("classpath:routes-with-expression.xml"));
+ runtime.addListener(Runtime.Phase.Started, Runtime::stop);
runtime.run();
}
@Test
public void testLoadJavaSource() throws Exception {
- ApplicationRuntime runtime = new ApplicationRuntime();
- runtime.addListener(RoutesConfigurer.forRoutes("classpath:MyRoutesWithBeans.java", "classpath:MyRoutesConfig.java"));
- runtime.addListener(Runtime.Phase.Started, r -> runtime.stop());
+ runtime.addListener(SourcesSupport.forRoutes("classpath:MyRoutesWithBeans.java", "classpath:MyRoutesConfig.java"));
+ runtime.addListener(Runtime.Phase.Started, r -> {
+ assertThat(runtime.getCamelContext().getRoutes()).hasSize(1);
+ assertThat(runtime.getRegistry().lookupByName("my-processor")).isNotNull();
+ assertThat(runtime.getRegistry().lookupByName("my-bean")).isInstanceOfSatisfying(MyBean.class, b -> {
+ assertThat(b).hasFieldOrPropertyWithValue("name", "my-bean-name");
+ });
+ r.stop();
+ });
+ runtime.run();
+ }
+
+ @Test
+ public void testLoadJavaSourceFromProperties() throws Exception {
+ runtime.setInitialProperties(
+ "camel.k.sources[0].name", "MyRoutesWithBeans",
+ "camel.k.sources[0].location", "classpath:MyRoutesWithBeans.java",
+ "camel.k.sources[0].language", "java",
+ "camel.k.sources[1].name", "MyRoutesConfig",
+ "camel.k.sources[1].location", "classpath:MyRoutesConfig.java",
+ "camel.k.sources[1].language", "java"
+ );
+ runtime.addListener(new SourcesConfigurer());
+ runtime.addListener(Runtime.Phase.Started, r -> {
+ assertThat(runtime.getCamelContext().getRoutes()).hasSize(1);
+ assertThat(runtime.getRegistry().lookupByName("my-processor")).isNotNull();
+ assertThat(runtime.getRegistry().lookupByName("my-bean")).isInstanceOfSatisfying(MyBean.class, b -> {
+ assertThat(b).hasFieldOrPropertyWithValue("name", "my-bean-name");
+ });
+ r.stop();
+ });
+ runtime.run();
+ }
+
+ @Test
+ public void testLoadJavaSourceFromSimpleProperties() throws Exception {
+ runtime.setInitialProperties(
+ "camel.k.sources[0].location", "classpath:MyRoutesWithBeans.java",
+ "camel.k.sources[1].location", "classpath:MyRoutesConfig.java"
+ );
+ runtime.addListener(new SourcesConfigurer());
+ runtime.addListener(Runtime.Phase.Started, Runtime::stop);
runtime.run();
assertThat(runtime.getRegistry().lookupByName("my-processor")).isNotNull();
@@ -111,6 +154,70 @@ public void testLoadJavaSource() throws Exception {
});
}
+ @Test
+ public void testLoadTemplates() throws Exception {
+ runtime.setInitialProperties(
+ "camel.k.sources[0].id", "my-template",
+ "camel.k.sources[0].location", "classpath:MyRouteTemplate.java",
+ "camel.k.sources[0].type", SourceType.template.name(),
+ "camel.k.sources[0].property-names[0]", "message"
+ );
+ runtime.addListener(new SourcesConfigurer());
+ runtime.addListener(Runtime.Phase.Started, r -> {
+ CamelContext context = runtime.getCamelContext();
+
+ assertThat(context.adapt(ModelCamelContext.class).getRouteTemplateDefinitions()).hasSize(1);
+ assertThat(context.adapt(ModelCamelContext.class).getRouteDefinitions()).isEmpty();
+
+ context.addRouteFromTemplate("myRoute", "my-template", Map.of("message", "test"));
+
+ assertThat(context.getRoutes())
+ .hasSize(1)
+ .first().hasFieldOrPropertyWithValue("id", "myRoute");
+
+ r.stop();
+ });
+ runtime.run();
+ }
+
+ @Test
+ public void testLoadJavaSourceWithKamelet() throws Exception {
+ runtime.setInitialProperties(
+ // template
+ "camel.k.sources[0].id", "my-template",
+ "camel.k.sources[0].location", "classpath:MyRouteTemplate.java",
+ "camel.k.sources[0].type", SourceType.template.name(),
+ "camel.k.sources[0].property-names[0]", "message",
+ // route
+ "camel.k.sources[1].location", "classpath:MyRoutesWithKamelets.java",
+ "camel.k.sources[1].type", SourceType.source.name(),
+ // props
+ "camel.kamelet.my-template.message", "default-message"
+ );
+ runtime.addListener(new SourcesConfigurer());
+ runtime.addListener(Runtime.Phase.Started, r -> {
+ CamelContext context = runtime.getCamelContext();
+
+ // templates
+ assertThat(context.adapt(ModelCamelContext.class).getRouteTemplateDefinitions()).hasSize(1);
+
+ // 2 routes defined in MyRoutesWithKamelets
+ // 2 routes materialized from templates by camel-kamelet
+ assertThat(context.adapt(ModelCamelContext.class).getRouteDefinitions()).hasSize(4);
+
+ assertThat(context.getRoutes())
+ .hasSize(4)
+ .extracting(Route::getId)
+ .containsExactlyInAnyOrder(
+ "k1", "k2", // routes from MyRoutesWithKamelets
+ "myKamelet1", "myKamelet2" // routes from templates
+ );
+
+ r.stop();
+ });
+ runtime.run();
+ }
+
@Test
public void testLoadJavaSourceWrap() throws Exception {
KnativeComponent component = new KnativeComponent();
@@ -123,8 +230,8 @@ public void testLoadJavaSourceWrap() throws Exception {
phsc.apply(runtime.getCamelContext());
runtime.getCamelContext().addComponent("knative", component);
- runtime.addListener(RoutesConfigurer.forRoutes("classpath:MyRoutesWithBeans.java?interceptors=knative-source"));
- runtime.addListener(Runtime.Phase.Started, r -> runtime.stop());
+ runtime.addListener(SourcesSupport.forRoutes("classpath:MyRoutesWithBeans.java?interceptors=knative-source"));
+ runtime.addListener(Runtime.Phase.Started, Runtime::stop);
runtime.run();
assertThat(runtime.getRegistry().lookupByName("my-bean")).isInstanceOfSatisfying(MyBean.class, b -> {
diff --git a/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/MyBean.java b/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/support/MyBean.java
similarity index 96%
rename from camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/MyBean.java
rename to camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/support/MyBean.java
index 2069e084c..650709d30 100644
--- a/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/MyBean.java
+++ b/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/support/MyBean.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.k.main;
+package org.apache.camel.k.main.support;
public class MyBean {
private final String name;
diff --git a/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/TestCustomizer.java b/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/support/TestCustomizer.java
similarity index 97%
rename from camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/TestCustomizer.java
rename to camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/support/TestCustomizer.java
index 46761011a..c5e17700c 100644
--- a/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/TestCustomizer.java
+++ b/camel-k-main/camel-k-runtime-main/src/test/java/org/apache/camel/k/main/support/TestCustomizer.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.k.main;
+package org.apache.camel.k.main.support;
import org.apache.camel.CamelContext;
import org.apache.camel.k.ContextCustomizer;
diff --git a/camel-k-main/camel-k-runtime-main/src/test/resources/META-INF/services/org/apache/camel/k/customizer/test b/camel-k-main/camel-k-runtime-main/src/test/resources/META-INF/services/org/apache/camel/k/customizer/test
index a393d0e62..26544f0df 100644
--- a/camel-k-main/camel-k-runtime-main/src/test/resources/META-INF/services/org/apache/camel/k/customizer/test
+++ b/camel-k-main/camel-k-runtime-main/src/test/resources/META-INF/services/org/apache/camel/k/customizer/test
@@ -15,4 +15,4 @@
# limitations under the License.
#
-class=org.apache.camel.k.main.TestCustomizer
\ No newline at end of file
+class=org.apache.camel.k.main.support.TestCustomizer
\ No newline at end of file
diff --git a/camel-k-main/camel-k-runtime-main/src/test/resources/MyRouteTemplate.java b/camel-k-main/camel-k-runtime-main/src/test/resources/MyRouteTemplate.java
new file mode 100644
index 000000000..b70f3090f
--- /dev/null
+++ b/camel-k-main/camel-k-runtime-main/src/test/resources/MyRouteTemplate.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+import org.apache.camel.k.main.support.MyBean;
+
+public class MyRouteTemplate extends RouteBuilder {
+ @Override
+ public void configure() throws Exception {
+ from("direct:{{routeId}}")
+ .setBody().constant("{{message}}")
+ .to("log:{{routeId}}");
+ }
+}
\ No newline at end of file
diff --git a/camel-k-main/camel-k-runtime-main/src/test/resources/MyRoutesWithBeans.java b/camel-k-main/camel-k-runtime-main/src/test/resources/MyRoutesWithBeans.java
index 0659a75a2..003e97ca6 100644
--- a/camel-k-main/camel-k-runtime-main/src/test/resources/MyRoutesWithBeans.java
+++ b/camel-k-main/camel-k-runtime-main/src/test/resources/MyRoutesWithBeans.java
@@ -17,6 +17,7 @@
import org.apache.camel.BindToRegistry;
import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.k.main.support.MyBean;
public class MyRoutesWithBeans extends RouteBuilder {
@Override
@@ -28,7 +29,7 @@ public void configure() throws Exception {
}
@BindToRegistry("my-bean")
- public org.apache.camel.k.main.MyBean createMyBean() {
- return new org.apache.camel.k.main.MyBean("my-bean-name");
+ public MyBean createMyBean() {
+ return new MyBean("my-bean-name");
}
}
\ No newline at end of file
diff --git a/camel-k-main/camel-k-runtime-main/src/test/resources/MyRoutesWithKamelets.java b/camel-k-main/camel-k-runtime-main/src/test/resources/MyRoutesWithKamelets.java
new file mode 100644
index 000000000..7e1f4b1e1
--- /dev/null
+++ b/camel-k-main/camel-k-runtime-main/src/test/resources/MyRoutesWithKamelets.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;
+import org.apache.camel.k.main.support.MyBean;
+
+public class MyRoutesWithKamelets extends RouteBuilder {
+ @Override
+ public void configure() throws Exception {
+ from("direct:k1")
+ .routeId("k1")
+ .to("kamelet:my-template/myKamelet1?message=my-message");
+ from("direct:k2")
+ .routeId("k2")
+ .to("kamelet:my-template/myKamelet2");
+ }
+}
\ No newline at end of file
diff --git a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-core/src/test/java/org/apache/camel/k/core/quarkus/deployment/ExtensionTest.java b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-core/src/test/java/org/apache/camel/k/core/quarkus/deployment/ExtensionTest.java
index bbf417c85..b653dc2e9 100644
--- a/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-core/src/test/java/org/apache/camel/k/core/quarkus/deployment/ExtensionTest.java
+++ b/camel-k-quarkus/camel-k-quarkus-itests/camel-k-quarkus-itests-core/src/test/java/org/apache/camel/k/core/quarkus/deployment/ExtensionTest.java
@@ -24,7 +24,7 @@
import io.restassured.path.json.JsonPath;
import org.apache.camel.k.CompositeClassloader;
import org.apache.camel.k.listener.ContextConfigurer;
-import org.apache.camel.k.listener.RoutesConfigurer;
+import org.apache.camel.k.listener.SourcesConfigurer;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -45,7 +45,7 @@ public void testServices() {
assertThat(p.getList("services", String.class)).contains(
ContextConfigurer.class.getName(),
- RoutesConfigurer.class.getName()
+ SourcesConfigurer.class.getName()
);
}
diff --git a/camel-k-quarkus/camel-k-runtime-quarkus/deployment/src/main/java/org/apache/camel/k/quarkus/deployment/DeploymentProcessor.java b/camel-k-quarkus/camel-k-runtime-quarkus/deployment/src/main/java/org/apache/camel/k/quarkus/deployment/DeploymentProcessor.java
index 3136a69f9..076ca3cb1 100644
--- a/camel-k-quarkus/camel-k-runtime-quarkus/deployment/src/main/java/org/apache/camel/k/quarkus/deployment/DeploymentProcessor.java
+++ b/camel-k-quarkus/camel-k-runtime-quarkus/deployment/src/main/java/org/apache/camel/k/quarkus/deployment/DeploymentProcessor.java
@@ -26,11 +26,8 @@
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import org.apache.camel.k.Runtime;
import org.apache.camel.k.quarkus.ApplicationRecorder;
-import org.apache.camel.quarkus.core.deployment.spi.CamelServiceDestination;
-import org.apache.camel.quarkus.core.deployment.spi.CamelServicePatternBuildItem;
import org.apache.camel.quarkus.main.CamelMainApplication;
import org.apache.camel.quarkus.main.deployment.spi.CamelMainListenerBuildItem;
-import org.apache.camel.spi.HasId;
public class DeploymentProcessor {
@BuildStep
@@ -42,19 +39,7 @@ public ReflectiveClassBuildItem reflectiveClasses() {
@BuildStep
CamelMainListenerBuildItem registerListener(ApplicationRecorder recorder) {
List listeners = new ArrayList<>();
- ServiceLoader.load(Runtime.Listener.class).forEach(listener -> {
- if (listener instanceof HasId) {
- String id = ((HasId) listener).getId();
- if (!id.endsWith(".")) {
- id = id + ".";
- }
-
- // TODO: this has to be done at runtime
- //PropertiesSupport.bindProperties(getCamelContext(), listener, id);
- }
-
- listeners.add(listener);
- });
+ ServiceLoader.load(Runtime.Listener.class).forEach(listeners::add);
return new CamelMainListenerBuildItem(recorder.createMainListener(listeners));
}
diff --git a/camel-k-runtime-core/pom.xml b/camel-k-runtime-core/pom.xml
index b9bfeb0a1..608ff3e67 100644
--- a/camel-k-runtime-core/pom.xml
+++ b/camel-k-runtime-core/pom.xml
@@ -93,6 +93,45 @@
+
+
+ org.apache.camel
+ camel-package-maven-plugin
+ ${camel.version}
+
+
+ generate-configurer
+ process-classes
+
+ generate-configurer
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ ${build-helper-maven-plugin.version}
+
+
+ generate-sources
+
+ add-source
+ add-resource
+
+
+
+
+
+
+
+ src/generated/resources
+
+
+
+
+
+
diff --git a/camel-k-runtime-core/src/generated/java/org/apache/camel/k/SourceDefinitionConfigurer.java b/camel-k-runtime-core/src/generated/java/org/apache/camel/k/SourceDefinitionConfigurer.java
new file mode 100644
index 000000000..7d41d8a37
--- /dev/null
+++ b/camel-k-runtime-core/src/generated/java/org/apache/camel/k/SourceDefinitionConfigurer.java
@@ -0,0 +1,90 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.k;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.k.SourceDefinition;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@SuppressWarnings("unchecked")
+public class SourceDefinitionConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+ @Override
+ public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+ org.apache.camel.k.SourceDefinition target = (org.apache.camel.k.SourceDefinition) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "compressed":
+ case "Compressed": target.setCompressed(property(camelContext, boolean.class, value)); return true;
+ case "content":
+ case "Content": target.setContent(property(camelContext, byte[].class, value)); return true;
+ case "id":
+ case "Id": target.setId(property(camelContext, java.lang.String.class, value)); return true;
+ case "interceptors":
+ case "Interceptors": target.setInterceptors(property(camelContext, java.util.List.class, value)); return true;
+ case "language":
+ case "Language": target.setLanguage(property(camelContext, java.lang.String.class, value)); return true;
+ case "loader":
+ case "Loader": target.setLoader(property(camelContext, java.lang.String.class, value)); return true;
+ case "location":
+ case "Location": target.setLocation(property(camelContext, java.lang.String.class, value)); return true;
+ case "name":
+ case "Name": target.setName(property(camelContext, java.lang.String.class, value)); return true;
+ case "propertynames":
+ case "PropertyNames": target.setPropertyNames(property(camelContext, java.util.List.class, value)); return true;
+ case "type":
+ case "Type": target.setType(property(camelContext, org.apache.camel.k.SourceType.class, value)); return true;
+ default: return false;
+ }
+ }
+
+ @Override
+ public Map getAllOptions(Object target) {
+ Map answer = new CaseInsensitiveMap();
+ answer.put("Compressed", boolean.class);
+ answer.put("Content", byte[].class);
+ answer.put("Id", java.lang.String.class);
+ answer.put("Interceptors", java.util.List.class);
+ answer.put("Language", java.lang.String.class);
+ answer.put("Loader", java.lang.String.class);
+ answer.put("Location", java.lang.String.class);
+ answer.put("Name", java.lang.String.class);
+ answer.put("PropertyNames", java.util.List.class);
+ answer.put("Type", org.apache.camel.k.SourceType.class);
+ return answer;
+ }
+
+ @Override
+ public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+ org.apache.camel.k.SourceDefinition target = (org.apache.camel.k.SourceDefinition) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "compressed":
+ case "Compressed": return target.isCompressed();
+ case "content":
+ case "Content": return target.getContent();
+ case "id":
+ case "Id": return target.getId();
+ case "interceptors":
+ case "Interceptors": return target.getInterceptors();
+ case "language":
+ case "Language": return target.getLanguage();
+ case "loader":
+ case "Loader": return target.getLoader();
+ case "location":
+ case "Location": return target.getLocation();
+ case "name":
+ case "Name": return target.getName();
+ case "propertynames":
+ case "PropertyNames": return target.getPropertyNames();
+ case "type":
+ case "Type": return target.getType();
+ default: return null;
+ }
+ }
+}
+
diff --git a/camel-k-runtime-core/src/generated/java/org/apache/camel/k/listener/SourcesConfigurerConfigurer.java b/camel-k-runtime-core/src/generated/java/org/apache/camel/k/listener/SourcesConfigurerConfigurer.java
new file mode 100644
index 000000000..a9faffa78
--- /dev/null
+++ b/camel-k-runtime-core/src/generated/java/org/apache/camel/k/listener/SourcesConfigurerConfigurer.java
@@ -0,0 +1,45 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.k.listener;
+
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.k.listener.SourcesConfigurer;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@SuppressWarnings("unchecked")
+public class SourcesConfigurerConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+ @Override
+ public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) {
+ org.apache.camel.k.listener.SourcesConfigurer target = (org.apache.camel.k.listener.SourcesConfigurer) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "sources":
+ case "Sources": target.setSources(property(camelContext, org.apache.camel.k.SourceDefinition[].class, value)); return true;
+ default: return false;
+ }
+ }
+
+ @Override
+ public Map getAllOptions(Object target) {
+ Map answer = new CaseInsensitiveMap();
+ answer.put("Sources", org.apache.camel.k.SourceDefinition[].class);
+ return answer;
+ }
+
+ @Override
+ public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+ org.apache.camel.k.listener.SourcesConfigurer target = (org.apache.camel.k.listener.SourcesConfigurer) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "sources":
+ case "Sources": return target.getSources();
+ default: return null;
+ }
+ }
+}
+
diff --git a/camel-k-runtime-core/src/generated/resources/META-INF/services/org/apache/camel/configurer/SourceDefinition b/camel-k-runtime-core/src/generated/resources/META-INF/services/org/apache/camel/configurer/SourceDefinition
new file mode 100644
index 000000000..ac318bd4f
--- /dev/null
+++ b/camel-k-runtime-core/src/generated/resources/META-INF/services/org/apache/camel/configurer/SourceDefinition
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.k.SourceDefinitionConfigurer
diff --git a/camel-k-runtime-core/src/generated/resources/META-INF/services/org/apache/camel/configurer/SourcesConfigurer b/camel-k-runtime-core/src/generated/resources/META-INF/services/org/apache/camel/configurer/SourcesConfigurer
new file mode 100644
index 000000000..618e94371
--- /dev/null
+++ b/camel-k-runtime-core/src/generated/resources/META-INF/services/org/apache/camel/configurer/SourcesConfigurer
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.k.listener.SourcesConfigurerConfigurer
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 a14a17d6d..beae5129f 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
@@ -25,12 +25,15 @@
import java.util.Optional;
import org.apache.camel.CamelContext;
+import org.apache.camel.spi.HasId;
-public interface Source {
+public interface Source extends HasId {
String getName();
String getLanguage();
+ SourceType getType();
Optional getLoader();
List getInterceptors();
+ List getPropertyNames();
InputStream resolveAsInputStream(CamelContext ctx);
default Reader resolveAsReader(CamelContext ctx) {
diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/SourceDefinition.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/SourceDefinition.java
new file mode 100644
index 000000000..330ab1397
--- /dev/null
+++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/SourceDefinition.java
@@ -0,0 +1,232 @@
+/*
+ * 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;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.k.support.StringSupport;
+import org.apache.camel.spi.Configurer;
+import org.apache.camel.spi.IdAware;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
+
+@Configurer
+public class SourceDefinition implements IdAware {
+ private String id;
+ private String name;
+ private String language;
+ private String loader;
+ private List interceptors;
+ private SourceType type;
+ private List propertyNames;
+ private String location;
+ private byte[] content;
+ private boolean compressed;
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Sets the id
+ */
+ @Override
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * The name of the source.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * The language use to define the source.
+ */
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public String getLoader() {
+ return loader;
+ }
+
+ /**
+ * The {@link SourceLoader} that should be used to load the content of the source.
+ */
+ public void setLoader(String loader) {
+ this.loader = loader;
+ }
+
+ public List getInterceptors() {
+ return interceptors;
+ }
+
+ /**
+ * The {@link org.apache.camel.k.SourceLoader.Interceptor} that should be applied.
+ */
+ public void setInterceptors(List interceptors) {
+ this.interceptors = interceptors;
+ }
+
+ public SourceType getType() {
+ return type;
+ }
+
+ /**
+ * The {@link SourceType} of the source.
+ */
+ public void setType(SourceType type) {
+ this.type = type;
+ }
+
+ public List getPropertyNames() {
+ return propertyNames;
+ }
+
+ /**
+ * The list of properties names the source requires (used only for templates).
+ */
+ public void setPropertyNames(List propertyNames) {
+ this.propertyNames = propertyNames;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ /**
+ * The location of the source.
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ public byte[] getContent() {
+ return content;
+ }
+
+ /**
+ * The content of the source.
+ */
+ public void setContent(byte[] content) {
+ this.content = content;
+ }
+
+ public boolean isCompressed() {
+ return compressed;
+ }
+
+ /**
+ * If the content of the source is compressed.
+ */
+ public void setCompressed(boolean compressed) {
+ this.compressed = compressed;
+ }
+
+ @Override
+ public String toString() {
+ return "SourceDefinition{" +
+ "name='" + name + '\'' +
+ ", language='" + language + '\'' +
+ ", loader='" + loader + '\'' +
+ ", interceptors=" + interceptors +
+ ", type=" + type +
+ ", propertiesNames=" + propertyNames +
+ ", location='" + location + '\'' +
+ ", content=" + (content != null ? "<...>" : null) +
+ ", compressed=" + compressed +
+ '}';
+ }
+
+ // ***********************************
+ //
+ // Helpers
+ //
+ // ***********************************
+
+ public static SourceDefinition fromURI(String uri) throws Exception {
+ final String location = StringSupport.substringBefore(uri, "?");
+
+ if (!location.startsWith(Constants.SCHEME_PREFIX_CLASSPATH) && !location.startsWith(Constants.SCHEME_PREFIX_FILE)) {
+ throw new IllegalArgumentException("No valid resource format, expected scheme:path, found " + uri);
+ }
+
+ final String query = StringSupport.substringAfter(uri, "?");
+ final Map params = URISupport.parseQuery(query);
+ final String id = (String) params.get("id");
+ final String languageName = (String) params.get("language");
+ final String compression = (String) params.get("compression");
+ final String loader = (String) params.get("loader");
+ final String interceptors = (String) params.get("interceptors");
+
+ String language = languageName;
+ if (ObjectHelper.isEmpty(language)) {
+ language = StringSupport.substringAfterLast(location, ":");
+ language = StringSupport.substringAfterLast(language, ".");
+ }
+ if (ObjectHelper.isEmpty(language)) {
+ throw new IllegalArgumentException("Unknown language " + language);
+ }
+
+ String name = (String) params.get("name");
+ if (name == null) {
+ name = StringSupport.substringAfter(location, ":");
+ name = StringSupport.substringBeforeLast(name, ".");
+
+ if (name.contains("/")) {
+ name = StringSupport.substringAfterLast(name, "/");
+ }
+ }
+
+ SourceDefinition answer = new SourceDefinition();
+ answer.id = id;
+ answer.location = location;
+ answer.name = name;
+ answer.language = language;
+ answer.loader = loader;
+ answer.interceptors = interceptors != null ? List.of(interceptors.split(",")) : Collections.emptyList();
+ answer.compressed = Boolean.parseBoolean(compression);
+
+ return answer;
+ }
+
+ public static SourceDefinition fromBytes(String id, String name, String language, String loader, List interceptors, byte[] content) {
+ SourceDefinition answer = new SourceDefinition();
+ answer.id = id;
+ answer.name = name;
+ answer.language = language;
+ answer.loader = loader;
+ answer.interceptors = interceptors != null ? interceptors : Collections.emptyList();
+ answer.content = content;
+
+ return answer;
+ }
+}
diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/SourceLoader.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/SourceLoader.java
index 8fe13338e..923b3eefc 100644
--- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/SourceLoader.java
+++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/SourceLoader.java
@@ -101,11 +101,14 @@ interface Interceptor {
/**
* Invoked before the source is materialized top a RoutesBuilder.
*/
- void beforeLoad(SourceLoader loader, Source source);
+ default void beforeLoad(SourceLoader loader, Source source) {
+ }
/**
* Invoked after the source is materialized and before is added to the runtime.
*/
- Result afterLoad(SourceLoader loader, Source source, Result result);
+ default Result afterLoad(SourceLoader loader, Source source, Result result) {
+ return result;
+ }
}
}
diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/SourceType.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/SourceType.java
new file mode 100644
index 000000000..ed9717a28
--- /dev/null
+++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/SourceType.java
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+public enum SourceType {
+ source,
+ template
+}
diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/Sources.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/Sources.java
index 976d65206..239f5055c 100644
--- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/Sources.java
+++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/Sources.java
@@ -18,181 +18,119 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.zip.GZIPInputStream;
import org.apache.camel.CamelContext;
import org.apache.camel.k.support.StringSupport;
-import org.apache.camel.spi.ClassResolver;
import org.apache.camel.support.ResourceHelper;
import org.apache.camel.util.ObjectHelper;
-import org.apache.camel.util.URISupport;
public final class Sources {
private Sources() {
}
public static Source fromURI(String uri) throws Exception {
- return new URI(uri);
+ return fromDefinition(SourceDefinition.fromURI(uri));
}
public static Source fromBytes(String name, String language, String loader, List interceptors, byte[] content) {
- return new InMemory(name, language, loader, interceptors, content);
+ return fromDefinition(SourceDefinition.fromBytes(null, name, language, loader, interceptors, content));
}
public static Source fromBytes(String name, String language, String loader, byte[] content) {
- return new InMemory(name, language, loader, content);
+ return fromDefinition(SourceDefinition.fromBytes(null, name, language, loader, null, content));
}
public static Source fromBytes(String language, byte[] content) {
- return new InMemory(UUID.randomUUID().toString(), language, null, content);
+ return fromDefinition(SourceDefinition.fromBytes(null, UUID.randomUUID().toString(), language, null, null, content));
}
- private static final class InMemory implements Source {
- private final String name;
- private final String language;
- private final String loader;
- private final List interceptors;
- private final byte[] content;
-
- public InMemory(String name, String language, String loader, byte[] content) {
- this.name = name;
- this.language = language;
- this.loader = loader;
- this.interceptors = Collections.emptyList();
- this.content = content;
+ public static Source fromDefinition(SourceDefinition definition) {
+ if (definition.getLocation() == null && definition.getContent() == null) {
+ throw new IllegalArgumentException("Either the source location or the source content should be set");
}
- public InMemory(String name, String language, String loader, List interceptors, byte[] content) {
- this.name = name;
- this.language = language;
- this.loader = loader;
- this.interceptors = new ArrayList<>(interceptors);
- this.content = content;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public String getLanguage() {
- return language;
- }
+ return new Source() {
+ @Override
+ public String getId() {
+ return ObjectHelper.supplyIfEmpty(definition.getId(), this::getName);
+ }
- @Override
- public Optional getLoader() {
- return Optional.ofNullable(loader);
- }
+ @Override
+ public String getName() {
+ String answer = definition.getName();
+ if (ObjectHelper.isEmpty(answer) && ObjectHelper.isNotEmpty(definition.getLocation())) {
+ answer = StringSupport.substringAfter(definition.getLocation(), ":");
+ answer = StringSupport.substringBeforeLast(answer, ".");
- @Override
- public List getInterceptors() {
- return interceptors;
- }
+ if (answer.contains("/")) {
+ answer = StringSupport.substringAfterLast(answer, "/");
+ }
+ }
- @Override
- public InputStream resolveAsInputStream(CamelContext ctx) {
- if (content == null) {
- throw new IllegalArgumentException("No content defined");
+ return answer;
}
- return new ByteArrayInputStream(this.content);
- }
- }
-
- private static final class URI implements Source {
- private final String location;
- private final String name;
- private final String language;
- private final String loader;
- private final List interceptors;
- private final boolean compressed;
-
- private URI(String uri) throws Exception {
- final String location = StringSupport.substringBefore(uri, "?");
+ @Override
+ public String getLanguage() {
+ String answer = definition.getLanguage();
+ if (ObjectHelper.isEmpty(answer) && ObjectHelper.isNotEmpty(definition.getLocation())) {
+ answer = StringSupport.substringAfterLast(definition.getLocation(), ":");
+ answer = StringSupport.substringAfterLast(answer, ".");
+ }
- if (!location.startsWith(Constants.SCHEME_PREFIX_CLASSPATH) && !location.startsWith(Constants.SCHEME_PREFIX_FILE)) {
- throw new IllegalArgumentException("No valid resource format, expected scheme:path, found " + uri);
+ return answer;
}
- final String query = StringSupport.substringAfter(uri, "?");
- final Map 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");
- final String interceptors = (String) params.get("interceptors");
-
- String language = languageName;
- if (ObjectHelper.isEmpty(language)) {
- language = StringSupport.substringAfterLast(location, ":");
- language = StringSupport.substringAfterLast(language, ".");
- }
- if (ObjectHelper.isEmpty(language)) {
- throw new IllegalArgumentException("Unknown language " + language);
+ @Override
+ public SourceType getType() {
+ return ObjectHelper.supplyIfEmpty(definition.getType(), () -> SourceType.source);
}
- String name = (String) params.get("name");
- if (name == null) {
- name = StringSupport.substringAfter(location, ":");
- name = StringSupport.substringBeforeLast(name, ".");
-
- if (name.contains("/")) {
- name = StringSupport.substringAfterLast(name, "/");
- }
+ @Override
+ public Optional getLoader() {
+ return Optional.ofNullable(definition.getLoader());
}
- this.location = location;
- this.name = name;
- this.language = language;
- this.loader = loader;
- this.interceptors = interceptors != null ? Arrays.asList(interceptors.split(",", -1)) : Collections.emptyList();
- this.compressed = Boolean.parseBoolean(compression);
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public String getLanguage() {
- return language;
- }
-
- @Override
- public Optional getLoader() {
- return Optional.ofNullable(loader);
- }
-
- @Override
- public List getInterceptors() {
- return interceptors;
- }
-
- @Override
- public InputStream resolveAsInputStream(CamelContext ctx) {
- if (location == null) {
- throw new IllegalArgumentException("Cannot resolve null URI");
+ @Override
+ public List getInterceptors() {
+ return ObjectHelper.supplyIfEmpty(definition.getInterceptors(), Collections::emptyList);
}
- try {
- final ClassResolver cr = ctx.getClassResolver();
- final InputStream is = ResourceHelper.resolveResourceAsInputStream(cr, location);
+ @Override
+ public List getPropertyNames() {
+ return ObjectHelper.supplyIfEmpty(definition.getPropertyNames(), Collections::emptyList);
+ }
- return compressed
- ? new GZIPInputStream(Base64.getDecoder().wrap(is))
- : is;
- } catch (Exception e) {
- throw new RuntimeException(e);
+ /**
+ * Read the content of the source as {@link InputStream}.
+ *
+ * @param ctx the {@link CamelContext}
+ * @return the {@link InputStream} representing the source content
+ */
+ @Override
+ public InputStream resolveAsInputStream(CamelContext ctx) {
+ try {
+ InputStream is;
+
+ if (definition.getContent() != null) {
+ is = new ByteArrayInputStream(definition.getContent());
+ } else {
+ is = ResourceHelper.resolveMandatoryResourceAsInputStream(ctx, definition.getLocation());
+ }
+
+ return definition.isCompressed()
+ ? new GZIPInputStream(Base64.getDecoder().wrap(is))
+ : is;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
- }
+ };
}
}
diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/RoutesConfigurer.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/RoutesConfigurer.java
deleted file mode 100644
index c701621e8..000000000
--- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/RoutesConfigurer.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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.listener;
-
-import java.util.List;
-
-import org.apache.camel.RuntimeCamelException;
-import org.apache.camel.k.Constants;
-import org.apache.camel.k.Runtime;
-import org.apache.camel.k.RuntimeAware;
-import org.apache.camel.k.Source;
-import org.apache.camel.k.SourceLoader;
-import org.apache.camel.k.Sources;
-import org.apache.camel.k.support.RuntimeSupport;
-import org.apache.camel.util.ObjectHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class RoutesConfigurer extends AbstractPhaseListener {
- private static final Logger LOGGER = LoggerFactory.getLogger(RoutesConfigurer.class);
-
- public RoutesConfigurer() {
- super(Runtime.Phase.ConfigureRoutes);
- }
-
- @Override
- protected void accept(Runtime runtime) {
- String routes = System.getProperty(Constants.PROPERTY_CAMEL_K_ROUTES);
-
- if (ObjectHelper.isEmpty(routes)) {
- routes = System.getenv(Constants.ENV_CAMEL_K_ROUTES);
- }
-
- if (ObjectHelper.isEmpty(routes)) {
- LOGGER.warn("No routes found in {} environment variable", Constants.ENV_CAMEL_K_ROUTES);
- return;
- }
-
- load(runtime, routes.split(",", -1));
- }
-
- protected void load(Runtime runtime, String[] routes) {
- for (String route: routes) {
- if (ObjectHelper.isEmpty(route)) {
- continue;
- }
-
- try {
- load(runtime, Sources.fromURI(route));
- } catch (Exception e) {
- throw RuntimeCamelException.wrapRuntimeCamelException(e);
- }
-
- LOGGER.info("Loading routes from: {}", route);
- }
- }
-
- public static RoutesConfigurer forRoutes(String... routes) {
- return new RoutesConfigurer() {
- @Override
- protected void accept(Runtime runtime) {
- load(runtime, routes);
- }
- };
- }
-
- public static SourceLoader load(Runtime runtime, Source source) {
- final List interceptors = RuntimeSupport.loadInterceptors(runtime.getCamelContext(), source);
- final SourceLoader loader = RuntimeSupport.loaderFor(runtime.getCamelContext(), source);
-
- try {
- for (SourceLoader.Interceptor interceptor: interceptors) {
- if (interceptor instanceof RuntimeAware) {
- ((RuntimeAware) interceptor).setRuntime(runtime);
- }
-
- interceptor.beforeLoad(loader, source);
- }
-
- SourceLoader.Result result = loader.load(runtime, source);
- for (SourceLoader.Interceptor interceptor: interceptors) {
- result = interceptor.afterLoad(loader, source, result);
- }
-
- result.builder().ifPresent(runtime::addRoutes);
- result.configuration().ifPresent(runtime::addConfiguration);
- } catch (Exception e) {
- throw RuntimeCamelException.wrapRuntimeCamelException(e);
- }
-
- return loader;
- }
-}
diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/SourcesConfigurer.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/SourcesConfigurer.java
new file mode 100644
index 000000000..5545947ff
--- /dev/null
+++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/listener/SourcesConfigurer.java
@@ -0,0 +1,77 @@
+/*
+ * 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.listener;
+
+import org.apache.camel.k.Constants;
+import org.apache.camel.k.Runtime;
+import org.apache.camel.k.SourceDefinition;
+import org.apache.camel.k.support.PropertiesSupport;
+import org.apache.camel.k.support.SourcesSupport;
+import org.apache.camel.spi.Configurer;
+import org.apache.camel.util.ObjectHelper;
+
+@Configurer
+public class SourcesConfigurer extends AbstractPhaseListener {
+ public static final String CAMEL_K_PREFIX = "camel.k.";
+ public static final String CAMEL_K_SOURCES_PREFIX = "camel.k.sources[";
+
+ private SourceDefinition[] sources;
+
+ public SourcesConfigurer() {
+ super(Runtime.Phase.ConfigureRoutes);
+ }
+
+ public SourceDefinition[] getSources() {
+ return sources;
+ }
+
+ public void setSources(SourceDefinition[] sources) {
+ this.sources = sources;
+ }
+
+ @Override
+ protected void accept(Runtime runtime) {
+ //
+ // load routes from env var for backward compatibility
+ //
+ String routes = System.getProperty(Constants.PROPERTY_CAMEL_K_ROUTES);
+ if (ObjectHelper.isEmpty(routes)) {
+ routes = System.getenv(Constants.ENV_CAMEL_K_ROUTES);
+ }
+
+ if (ObjectHelper.isNotEmpty(routes)) {
+ SourcesSupport.loadSources(runtime, routes.split(","));
+ }
+
+ //
+ // load routes from properties
+ //
+ // In order not to load any unwanted property, the filer remove any
+ // property that can't be bound to this configurer.
+ //
+ PropertiesSupport.bindProperties(
+ runtime.getCamelContext(),
+ this,
+ k -> k.startsWith(CAMEL_K_SOURCES_PREFIX),
+ CAMEL_K_PREFIX);
+
+ if (ObjectHelper.isNotEmpty(this.getSources())) {
+ SourcesSupport.loadSources(runtime, this.getSources());
+ }
+ }
+
+}
diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/PropertiesSupport.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/PropertiesSupport.java
index e1e7996e4..bd57ae651 100644
--- a/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/PropertiesSupport.java
+++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/PropertiesSupport.java
@@ -26,34 +26,84 @@
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
+import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
+import java.util.function.Predicate;
import org.apache.camel.CamelContext;
+import org.apache.camel.Component;
+import org.apache.camel.ExtendedCamelContext;
import org.apache.camel.k.Constants;
import org.apache.camel.spi.PropertiesComponent;
+import org.apache.camel.spi.PropertyConfigurer;
import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.support.service.ServiceHelper;
import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.StringHelper;
public final class PropertiesSupport {
private PropertiesSupport() {
}
- @SuppressWarnings("unchecked")
- public static boolean bindProperties(CamelContext context, Object target, String prefix) {
+ public static T bindProperties(CamelContext context, T target, String prefix) {
+ return bindProperties(context, target, prefix, false);
+ }
+
+ public static T bindProperties(CamelContext context, T target, String prefix, boolean stripPrefix) {
+ return bindProperties(context, target, k -> k.startsWith(prefix), prefix, stripPrefix);
+ }
+
+ public static T bindProperties(CamelContext context, T target, Predicate filter, String prefix) {
+ return bindProperties(context, target, filter, prefix, false);
+ }
+
+ public static T bindProperties(CamelContext context, T target, Predicate filter, String prefix, boolean stripPrefix) {
final PropertiesComponent component = context.getPropertiesComponent();
- final Properties properties = component.loadProperties(k -> k.startsWith(prefix));
+ final Properties propertiesWithPrefix = component.loadProperties(filter);
+ final Map properties = new HashMap<>();
+
+ propertiesWithPrefix.stringPropertyNames().forEach(
+ name -> properties.put(
+ // PropertyBindingSupport does not support dash properties
+ StringHelper.dashToCamelCase(stripPrefix ? name.substring(prefix.length()) : name),
+ propertiesWithPrefix.getProperty(name))
+ );
+
+ PropertyConfigurer configurer = null;
+ if (target instanceof Component) {
+ // the component needs to be initialized to have the configurer ready
+ ServiceHelper.initService(target);
+ configurer = ((Component) target).getComponentPropertyConfigurer();
+ }
+
+ if (configurer == null) {
+ String name = target.getClass().getSimpleName();
+ if (target instanceof ExtendedCamelContext) {
+ // special for camel context itself as we have an extended configurer
+ name = "ExtendedCamelContext";
+ }
- return PropertyBindingSupport.build()
+ // see if there is a configurer for it
+ configurer = context.adapt(ExtendedCamelContext.class)
+ .getConfigurerResolver()
+ .resolvePropertyConfigurer(name, context);
+ }
+
+ PropertyBindingSupport.build()
+ .withIgnoreCase(true)
.withCamelContext(context)
.withTarget(target)
- .withProperties((Map)properties)
- .withRemoveParameters(false)
- .withOptionPrefix(prefix)
+ .withProperties(properties)
+ .withRemoveParameters(true)
+ .withOptionPrefix(stripPrefix ? null : prefix)
+ .withConfigurer(configurer)
.bind();
+
+ return target;
}
public static String resolveApplicationPropertiesLocation() {
diff --git a/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/SourcesSupport.java b/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/SourcesSupport.java
new file mode 100644
index 000000000..7d6fa6463
--- /dev/null
+++ b/camel-k-runtime-core/src/main/java/org/apache/camel/k/support/SourcesSupport.java
@@ -0,0 +1,164 @@
+/*
+ * 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.support;
+
+import java.util.List;
+
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.builder.RouteBuilderLifecycleStrategy;
+import org.apache.camel.k.Runtime;
+import org.apache.camel.k.RuntimeAware;
+import org.apache.camel.k.Source;
+import org.apache.camel.k.SourceDefinition;
+import org.apache.camel.k.SourceLoader;
+import org.apache.camel.k.SourceType;
+import org.apache.camel.k.Sources;
+import org.apache.camel.k.listener.AbstractPhaseListener;
+import org.apache.camel.k.listener.SourcesConfigurer;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.RouteTemplateDefinition;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class SourcesSupport {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SourcesConfigurer.class);
+
+ private SourcesSupport() {
+ }
+
+ public static Runtime.Listener forRoutes(String... sources) {
+ return new AbstractPhaseListener(Runtime.Phase.ConfigureRoutes) {
+ @Override
+ protected void accept(Runtime runtime) {
+ loadSources(runtime, sources);
+ }
+ };
+ }
+
+ public static Runtime.Listener forRoutes(SourceDefinition... definitions) {
+ return new AbstractPhaseListener(Runtime.Phase.ConfigureRoutes) {
+ @Override
+ protected void accept(Runtime runtime) {
+ loadSources(runtime, definitions);
+ }
+ };
+ }
+
+ public static void loadSources(Runtime runtime, String... routes) {
+ for (String route: routes) {
+ if (ObjectHelper.isEmpty(route)) {
+ continue;
+ }
+
+ LOGGER.debug("Loading routes from: {}", route);
+
+ try {
+ load(runtime, Sources.fromURI(route));
+ } catch (Exception e) {
+ throw RuntimeCamelException.wrapRuntimeCamelException(e);
+ }
+ }
+ }
+
+ public static void loadSources(Runtime runtime, SourceDefinition... definitions) {
+ for (SourceDefinition definition: definitions) {
+ LOGGER.debug("Loading routes from: {}", definition);
+
+ load(runtime, Sources.fromDefinition(definition));
+ }
+ }
+
+ public static SourceLoader load(Runtime runtime, Source source) {
+ final SourceLoader loader = RuntimeSupport.loaderFor(runtime.getCamelContext(), source);
+ final List interceptors = source.getType() == SourceType.source
+ ? sourceInterceptors(runtime, source)
+ : templateInterceptors(runtime, source);
+
+ try {
+ for (SourceLoader.Interceptor interceptor: interceptors) {
+ if (interceptor instanceof RuntimeAware) {
+ ((RuntimeAware) interceptor).setRuntime(runtime);
+ }
+
+ interceptor.beforeLoad(loader, source);
+ }
+
+ SourceLoader.Result result = loader.load(runtime, source);
+
+ for (SourceLoader.Interceptor interceptor: interceptors) {
+ result = interceptor.afterLoad(loader, source, result);
+ }
+
+ result.builder().ifPresent(runtime::addRoutes);
+ result.configuration().ifPresent(runtime::addConfiguration);
+ } catch (Exception e) {
+ throw RuntimeCamelException.wrapRuntimeCamelException(e);
+ }
+
+ return loader;
+ }
+
+ private static List sourceInterceptors(Runtime runtime, Source source) {
+ return RuntimeSupport.loadInterceptors(runtime.getCamelContext(), source);
+ }
+
+ private static List templateInterceptors(Runtime runtime, Source source) {
+ if (!source.getInterceptors().isEmpty()) {
+ LOGGER.warn("Interceptors associated to the route template {} will be ignored", source.getName());
+ }
+
+ return List.of(
+ new SourceLoader.Interceptor() {
+ @Override
+ public SourceLoader.Result afterLoad(SourceLoader loader, Source source, SourceLoader.Result result) {
+ RouteBuilder builder = result.builder()
+ .map(RouteBuilder.class::cast)
+ .orElseThrow(() -> new IllegalArgumentException("Unexpected routes builder type"));
+
+ builder.addLifecycleInterceptor(new RouteBuilderLifecycleStrategy() {
+ @Override
+ public void afterConfigure(RouteBuilder builder) {
+ List routes = builder.getRouteCollection().getRoutes();
+ List templates = builder.getRouteTemplateCollection().getRouteTemplates();
+
+ if (routes.size() != 1) {
+ throw new IllegalArgumentException("There should be a single route definition, got " + routes.size());
+ }
+ if (!templates.isEmpty()) {
+ throw new IllegalArgumentException("There should not be any template, got " + templates.size());
+ }
+
+ // create a new template from the source
+ RouteTemplateDefinition templatesDefinition = builder.getRouteTemplateCollection().routeTemplate(source.getId());
+ templatesDefinition.setRoute(routes.get(0));
+
+ source.getPropertyNames().forEach(templatesDefinition::templateParameter);
+
+ // remove all routes definitions as they have been translated
+ // in the related route template
+ routes.clear();
+ }
+ });
+
+ return SourceLoader.Result.on(builder);
+ }
+ }
+ );
+ }
+}
diff --git a/camel-k-runtime-core/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener b/camel-k-runtime-core/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener
index 9caf5beb2..f0c50b74f 100644
--- a/camel-k-runtime-core/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener
+++ b/camel-k-runtime-core/src/main/resources/META-INF/services/org.apache.camel.k.Runtime$Listener
@@ -16,5 +16,5 @@
#
org.apache.camel.k.listener.ContextConfigurer
-org.apache.camel.k.listener.RoutesConfigurer
+org.apache.camel.k.listener.SourcesConfigurer
org.apache.camel.k.listener.PropertiesConfigurer
diff --git a/camel-k-runtime-core/src/test/java/org/apache/camel/k/SourceTest.java b/camel-k-runtime-core/src/test/java/org/apache/camel/k/SourceTest.java
index 4191de29e..87279cffb 100644
--- a/camel-k-runtime-core/src/test/java/org/apache/camel/k/SourceTest.java
+++ b/camel-k-runtime-core/src/test/java/org/apache/camel/k/SourceTest.java
@@ -18,6 +18,7 @@
import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
public class SourceTest {
@@ -42,4 +43,13 @@ public void testUnsupportedLanguage() {
);
}
+ @Test
+ public void sourceCanBeContructedFromLocation() {
+ SourceDefinition definition = new SourceDefinition();
+ definition.setLocation("classpath:MyRoutes.java");
+
+ assertThat(Sources.fromDefinition(definition))
+ .hasFieldOrPropertyWithValue("name", "MyRoutes")
+ .hasFieldOrPropertyWithValue("language", "java");
+ }
}
diff --git a/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/PropertiesSupportTest.java b/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/PropertiesSupportTest.java
new file mode 100644
index 000000000..6a91b581b
--- /dev/null
+++ b/camel-k-runtime-core/src/test/java/org/apache/camel/k/support/PropertiesSupportTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.support;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.k.SourceDefinition;
+import org.apache.camel.k.listener.SourcesConfigurer;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.k.test.CamelKTestSupport.asProperties;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PropertiesSupportTest {
+ @Test
+ public void propertiesAreBoundToSourcesConfigurer() {
+ CamelContext context = new DefaultCamelContext();
+ context.getPropertiesComponent().setInitialProperties(asProperties(
+ "camel.k.sources[0].name", "MyRoutesWithBeans",
+ "camel.k.sources[0].location", "classpath:MyRoutesWithBeans.java",
+ "camel.k.sources[1].name", "MyRoutesConfig",
+ "camel.k.sources[1].location", "classpath:MyRoutesConfig.java",
+ "camel.k.sources[1].property-names[0]", "foo",
+ "camel.k.sources[1].property-names[1]", "bar"
+ ));
+
+ SourcesConfigurer configuration = new SourcesConfigurer();
+
+ PropertiesSupport.bindProperties(
+ context,
+ configuration,
+ k -> k.startsWith(SourcesConfigurer.CAMEL_K_SOURCES_PREFIX),
+ SourcesConfigurer.CAMEL_K_PREFIX);
+
+ assertThat(configuration.getSources())
+ .hasSize(2)
+ .anyMatch(byNameAndLocation("MyRoutesWithBeans", "classpath:MyRoutesWithBeans.java")
+ .and(d -> d.getPropertyNames() == null))
+ .anyMatch(byNameAndLocation("MyRoutesConfig", "classpath:MyRoutesConfig.java")
+ .and(d -> d.getPropertyNames() != null && d.getPropertyNames().containsAll(List.of("foo", "bar"))));
+ }
+
+ @Test
+ public void propertiesWithGapsAreBoundToSourcesConfigurer() {
+ CamelContext context = new DefaultCamelContext();
+ context.getPropertiesComponent().setInitialProperties(asProperties(
+ "camel.k.sources[0].name", "MyRoutesWithBeans",
+ "camel.k.sources[0].location", "classpath:MyRoutesWithBeans.java",
+ "camel.k.sources[2].name", "MyRoutesConfig",
+ "camel.k.sources[2].location", "classpath:MyRoutesConfig.java"
+ ));
+
+ SourcesConfigurer configuration = new SourcesConfigurer();
+
+ PropertiesSupport.bindProperties(
+ context,
+ configuration,
+ k -> k.startsWith(SourcesConfigurer.CAMEL_K_SOURCES_PREFIX),
+ SourcesConfigurer.CAMEL_K_PREFIX);
+
+ assertThat(configuration.getSources())
+ .hasSize(3)
+ .filteredOn(Objects::nonNull)
+ .hasSize(2)
+ .anyMatch(byNameAndLocation("MyRoutesWithBeans", "classpath:MyRoutesWithBeans.java"))
+ .anyMatch(byNameAndLocation("MyRoutesConfig", "classpath:MyRoutesConfig.java"));
+ }
+
+ // ***************************
+ //
+ // Helpers
+ //
+ // ***************************
+
+ private static Predicate byNameAndLocation(String name, String location) {
+ return def -> Objects.equals(def.getName(), name) && Objects.equals(def.getLocation(), location);
+ }
+}
diff --git a/camel-k-runtime-cron/src/test/java/org/apache/camel/k/cron/CronTest.java b/camel-k-runtime-cron/src/test/java/org/apache/camel/k/cron/CronTest.java
index d90427a13..5c30e159c 100644
--- a/camel-k-runtime-cron/src/test/java/org/apache/camel/k/cron/CronTest.java
+++ b/camel-k-runtime-cron/src/test/java/org/apache/camel/k/cron/CronTest.java
@@ -22,8 +22,8 @@
import org.apache.camel.CamelContext;
import org.apache.camel.component.mock.MockEndpoint;
-import org.apache.camel.k.listener.RoutesConfigurer;
import org.apache.camel.k.main.ApplicationRuntime;
+import org.apache.camel.k.support.SourcesSupport;
import org.apache.camel.support.LifecycleStrategySupport;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -39,7 +39,7 @@ public void testCronTimerActivation(String routes, String cronOverride) throws E
runtime.setProperties(
"loader.interceptor.cron.overridable-components", cronOverride
);
- runtime.addListener(RoutesConfigurer.forRoutes(routes));
+ runtime.addListener(SourcesSupport.forRoutes(routes));
// To check auto-termination of Camel context
CountDownLatch termination = new CountDownLatch(1);
diff --git a/camel-k-runtime-knative/src/test/java/org/apache/camel/k/knative/customizer/KnativeSinkBindingCustomizerTest.java b/camel-k-runtime-knative/src/test/java/org/apache/camel/k/knative/customizer/KnativeSinkBindingCustomizerTest.java
index 7dab46d51..c92199bbe 100644
--- a/camel-k-runtime-knative/src/test/java/org/apache/camel/k/knative/customizer/KnativeSinkBindingCustomizerTest.java
+++ b/camel-k-runtime-knative/src/test/java/org/apache/camel/k/knative/customizer/KnativeSinkBindingCustomizerTest.java
@@ -41,8 +41,8 @@
import org.apache.camel.k.SourceLoader;
import org.apache.camel.k.Sources;
import org.apache.camel.k.http.PlatformHttpServiceContextCustomizer;
-import org.apache.camel.k.listener.RoutesConfigurer;
import org.apache.camel.k.support.RuntimeSupport;
+import org.apache.camel.k.support.SourcesSupport;
import org.apache.camel.k.test.AvailablePortFinder;
import org.junit.jupiter.api.Test;
@@ -106,7 +106,7 @@ public void testWrapLoaderWithSyntheticServiceDefinition() throws Exception {
RuntimeSupport.configureContextCustomizers(runtime);
Source source = Sources.fromBytes("groovy", "from('direct:start').setBody().header('MyHeader').to('knative://endpoint/mySynk')".getBytes(StandardCharsets.UTF_8));
- SourceLoader loader = RoutesConfigurer.load(runtime, source);
+ SourceLoader loader = SourcesSupport.load(runtime, source);
assertThat(loader.getSupportedLanguages()).contains(source.getLanguage());
assertThat(runtime.builders).hasSize(1);
diff --git a/camel-k-runtime-knative/src/test/java/org/apache/camel/k/knative/yaml/parser/KnativeConverterTest.java b/camel-k-runtime-knative/src/test/java/org/apache/camel/k/knative/yaml/parser/KnativeConverterTest.java
index fe818cd85..88a265b99 100644
--- a/camel-k-runtime-knative/src/test/java/org/apache/camel/k/knative/yaml/parser/KnativeConverterTest.java
+++ b/camel-k-runtime-knative/src/test/java/org/apache/camel/k/knative/yaml/parser/KnativeConverterTest.java
@@ -27,8 +27,8 @@
import org.apache.camel.k.Source;
import org.apache.camel.k.SourceLoader;
import org.apache.camel.k.Sources;
-import org.apache.camel.k.listener.RoutesConfigurer;
import org.apache.camel.k.loader.yaml.YamlSourceLoader;
+import org.apache.camel.k.support.SourcesSupport;
import org.apache.camel.model.FromDefinition;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.RouteDefinition;
@@ -43,7 +43,7 @@ public class KnativeConverterTest {
public void testLoadRoutes() throws Exception {
TestRuntime runtime = new TestRuntime();
Source source = Sources.fromURI("classpath:route.yaml");
- SourceLoader loader = RoutesConfigurer.load(runtime, source);
+ SourceLoader loader = SourcesSupport.load(runtime, source);
assertThat(loader).isInstanceOf(YamlSourceLoader.class);
assertThat(runtime.builders).hasSize(1);
diff --git a/camel-k-runtime-knative/src/test/java/org/apache/camel/k/loader/knative/KnativeSourceRoutesLoaderTest.java b/camel-k-runtime-knative/src/test/java/org/apache/camel/k/loader/knative/KnativeSourceRoutesLoaderTest.java
index 513005eba..a41d21eeb 100644
--- a/camel-k-runtime-knative/src/test/java/org/apache/camel/k/loader/knative/KnativeSourceRoutesLoaderTest.java
+++ b/camel-k-runtime-knative/src/test/java/org/apache/camel/k/loader/knative/KnativeSourceRoutesLoaderTest.java
@@ -18,10 +18,7 @@
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
-import java.util.Map;
-import java.util.Properties;
import java.util.UUID;
import java.util.stream.Stream;
@@ -30,8 +27,6 @@
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.knative.KnativeComponent;
import org.apache.camel.component.knative.KnativeConstants;
-import org.apache.camel.component.knative.spi.CloudEvent;
-import org.apache.camel.component.knative.spi.CloudEvents;
import org.apache.camel.component.knative.spi.Knative;
import org.apache.camel.component.knative.spi.KnativeEnvironment;
import org.apache.camel.component.mock.MockEndpoint;
@@ -41,7 +36,7 @@
import org.apache.camel.k.SourceLoader;
import org.apache.camel.k.Sources;
import org.apache.camel.k.http.PlatformHttpServiceContextCustomizer;
-import org.apache.camel.k.listener.RoutesConfigurer;
+import org.apache.camel.k.support.SourcesSupport;
import org.apache.camel.k.test.AvailablePortFinder;
import org.apache.camel.model.ModelCamelContext;
import org.apache.camel.model.RouteDefinition;
@@ -84,7 +79,7 @@ public void testWrapLoader(String uri) throws Exception {
context.addComponent(KnativeConstants.SCHEME, component);
Source source = Sources.fromURI(uri);
- SourceLoader loader = RoutesConfigurer.load(runtime, source);
+ SourceLoader loader = SourcesSupport.load(runtime, source);
assertThat(loader.getSupportedLanguages()).contains(source.getLanguage());
assertThat(runtime.builders).hasSize(1);
diff --git a/camel-k-runtime-webhook/src/test/java/org/apache/camel/k/webhook/WebhookTest.java b/camel-k-runtime-webhook/src/test/java/org/apache/camel/k/webhook/WebhookTest.java
index 768e4f64e..010e85a01 100644
--- a/camel-k-runtime-webhook/src/test/java/org/apache/camel/k/webhook/WebhookTest.java
+++ b/camel-k-runtime-webhook/src/test/java/org/apache/camel/k/webhook/WebhookTest.java
@@ -30,8 +30,8 @@
import org.apache.camel.NamedNode;
import org.apache.camel.Route;
import org.apache.camel.k.listener.ContextConfigurer;
-import org.apache.camel.k.listener.RoutesConfigurer;
import org.apache.camel.k.main.ApplicationRuntime;
+import org.apache.camel.k.support.SourcesSupport;
import org.apache.camel.spi.RoutePolicy;
import org.apache.camel.spi.RoutePolicyFactory;
import org.apache.camel.support.RoutePolicySupport;
@@ -71,7 +71,7 @@ public void testWebhookRegistration(WebhookAction action) throws Exception {
AtomicBoolean routeStarted = new AtomicBoolean();
runtime.addListener(new ContextConfigurer());
- runtime.addListener(RoutesConfigurer.forRoutes("classpath:webhook.js"));
+ runtime.addListener(SourcesSupport.forRoutes("classpath:webhook.js"));
runtime.getCamelContext().addRoutePolicyFactory(new RoutePolicyFactory() {
@Override
public RoutePolicy createRoutePolicy(CamelContext camelContext, String routeId, NamedNode route) {
@@ -132,7 +132,7 @@ public void testRegistrationFailure(WebhookAction action) throws Exception {
);
runtime.addListener(new ContextConfigurer());
- runtime.addListener(RoutesConfigurer.forRoutes("classpath:webhook.js"));
+ runtime.addListener(SourcesSupport.forRoutes("classpath:webhook.js"));
Assertions.assertThrows(FailedToCreateRouteException.class, runtime::run);
}
@@ -146,7 +146,7 @@ public void testAutoRegistrationNotDisabled() throws Exception {
runtime.getCamelContext().addComponent("dummy", new DummyWebhookComponent());
runtime.addListener(new ContextConfigurer());
- runtime.addListener(RoutesConfigurer.forRoutes("classpath:webhook.js"));
+ runtime.addListener(SourcesSupport.forRoutes("classpath:webhook.js"));
Assertions.assertThrows(FailedToCreateRouteException.class, runtime::run);
}
diff --git a/camel-kamelet/pom.xml b/camel-kamelet/pom.xml
index 33e09c34f..e459ea9cb 100644
--- a/camel-kamelet/pom.xml
+++ b/camel-kamelet/pom.xml
@@ -21,7 +21,7 @@
org.apache.camel.k
camel-k-runtime-parent
- 1.5.0-SNAPSHOT
+ 1.5.1-SNAPSHOT
4.0.0
@@ -76,6 +76,21 @@
camel-log
test
+
+ org.apache.camel
+ camel-mock
+ test
+
+
+ org.apache.camel
+ camel-mock
+ test
+
+
+ org.apache.camel
+ camel-test-junit5
+ test
+
diff --git a/camel-kamelet/src/generated/resources/org/apache/camel/component/kamelet/kamelet.json b/camel-kamelet/src/generated/resources/org/apache/camel/component/kamelet/kamelet.json
index 93ef4969b..50552af50 100644
--- a/camel-kamelet/src/generated/resources/org/apache/camel/component/kamelet/kamelet.json
+++ b/camel-kamelet/src/generated/resources/org/apache/camel/component/kamelet/kamelet.json
@@ -11,7 +11,7 @@
"supportLevel": "Preview",
"groupId": "org.apache.camel.k",
"artifactId": "camel-kamelet",
- "version": "1.5.0-SNAPSHOT",
+ "version": "1.5.1-SNAPSHOT",
"scheme": "kamelet",
"extendsScheme": "",
"syntax": "kamelet:templateId\/routeId",
diff --git a/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java b/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
index 043f44eb2..3712c7c20 100644
--- a/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
+++ b/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
@@ -16,10 +16,26 @@
*/
package org.apache.camel.component.kamelet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
import java.util.function.Predicate;
+import org.apache.camel.CamelContext;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.spi.PropertiesComponent;
+import org.apache.camel.util.StringHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
public final class Kamelet {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Kamelet.class);
+
public static final String SCHEME = "kamelet";
+ public static final String PROPERTIES_PREFIX = "camel.kamelet.";
private Kamelet() {
}
@@ -27,4 +43,60 @@ private Kamelet() {
public static Predicate startsWith(String prefix) {
return item -> item.startsWith(prefix);
}
+
+ public static void createRouteForEndpoint(KameletEndpoint endpoint) {
+ try {
+ LOGGER.debug("Creating route from template {}", endpoint.getTemplateId());
+
+ ModelCamelContext context = endpoint.getCamelContext().adapt(ModelCamelContext.class);
+ String id = context.addRouteFromTemplate(endpoint.getRouteId(), endpoint.getTemplateId(), endpoint.getKameletProperties());
+ RouteDefinition def = context.getRouteDefinition(id);
+ if (!def.isPrepared()) {
+ context.startRouteDefinitions(List.of(def));
+ }
+
+ LOGGER.debug("Route {} created from template {}", id, endpoint.getTemplateId());
+ } catch (Exception e) {
+ throw new RuntimeCamelException(e);
+ }
+ }
+
+ public static String extractTemplateId(CamelContext context, String remaining) {
+ String answer = StringHelper.before(remaining, "/");
+ if (answer == null) {
+ answer = remaining;
+ }
+
+ return answer;
+ }
+
+ public static String extractRouteId(CamelContext context, String remaining) {
+ String answer = StringHelper.after(remaining, "/");
+ if (answer == null) {
+ answer = extractTemplateId(context, remaining) + "-" + context.getUuidGenerator().generateUuid();
+ }
+
+ return answer;
+ }
+
+ public static Map extractKameletProperties(CamelContext context, String... elements) {
+ PropertiesComponent pc = context.getPropertiesComponent();
+ Map properties = new HashMap<>();
+ String prefix = Kamelet.PROPERTIES_PREFIX;
+
+ for (String element: elements) {
+ if (element == null) {
+ continue;
+ }
+
+ prefix = prefix + element + ".";
+
+ Properties prefixed = pc.loadProperties(Kamelet.startsWith(prefix));
+ for (String name : prefixed.stringPropertyNames()) {
+ properties.put(name.substring(prefix.length()), prefixed.getProperty(name));
+ }
+ }
+
+ return properties;
+ }
}
diff --git a/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java b/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
index 83898a2ef..0f8233f08 100644
--- a/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
+++ b/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
@@ -17,7 +17,6 @@
package org.apache.camel.component.kamelet;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -25,16 +24,17 @@
import org.apache.camel.Endpoint;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.model.ModelCamelContext;
-import org.apache.camel.model.RouteDefinition;
import org.apache.camel.spi.CamelEvent;
+import org.apache.camel.spi.ManagementStrategy;
import org.apache.camel.spi.annotations.Component;
import org.apache.camel.support.DefaultComponent;
import org.apache.camel.support.EventNotifierSupport;
import org.apache.camel.support.service.ServiceHelper;
-import org.apache.camel.util.StringHelper;
@Component(Kamelet.SCHEME)
public class KameletComponent extends DefaultComponent {
+ private volatile EventHandler notifier;
+
public KameletComponent() {
this(null);
}
@@ -43,15 +43,10 @@ public KameletComponent(CamelContext context) {
super(context);
}
- // use as temporary to keep track of created kamelet endpoints during startup as we need to defer
- // create routes from templates until camel context has finished loading all routes and whatnot
- private final List endpoints = new ArrayList<>();
- private volatile RouteTemplateEventNotifier notifier;
-
@Override
protected Endpoint createEndpoint(String uri, String remaining, Map parameters) throws Exception {
- final String templateId = extractTemplateId(remaining);
- final String routeId = extractRouteId(remaining);
+ final String templateId = Kamelet.extractTemplateId(getCamelContext(), remaining);
+ final String routeId = Kamelet.extractRouteId(getCamelContext(), remaining);
//
// The properties for the kamelets are determined by global properties
@@ -62,10 +57,10 @@ protected Endpoint createEndpoint(String uri, String remaining, Map kameletProperties = extractKameletProperties(templateId, routeId);
+ Map kameletProperties = Kamelet.extractKameletProperties(getCamelContext(), templateId, routeId);
kameletProperties.putAll(parameters);
- kameletProperties.putIfAbsent("templateId", templateId);
- kameletProperties.putIfAbsent("routeId", routeId);
+ kameletProperties.put("templateId", templateId);
+ kameletProperties.put("routeId", routeId);
// Remaining parameter should be related to the route and to avoid the
// parameters validation to fail, we need to clear the parameters map.
@@ -79,54 +74,15 @@ protected Endpoint createEndpoint(String uri, String remaining, Map extractKameletProperties(String... elements) {
- Map properties = new HashMap<>();
- String prefix = "camel.kamelet.";
-
- for (String element: elements) {
- if (element == null) {
- continue;
- }
-
- prefix = prefix + element + ".";
-
- properties.putAll(
- (Map)getCamelContext().getPropertiesComponent().loadProperties(Kamelet.startsWith(prefix))
- );
-
- }
-
- return properties;
- }
-
@Override
protected void doInit() throws Exception {
super.doInit();
if (!getCamelContext().isRunAllowed()) {
- // setup event listener which must be started to get triggered during initialization of camel context
- notifier = new RouteTemplateEventNotifier(this);
+ notifier = new EventHandler();
+
ServiceHelper.startService(notifier);
- getCamelContext().getManagementStrategy().addEventNotifier(notifier);
+ getManagementStrategy().addEventNotifier(notifier);
}
}
@@ -134,54 +90,59 @@ protected void doInit() throws Exception {
protected void doStop() throws Exception {
if (notifier != null) {
ServiceHelper.stopService(notifier);
- getCamelContext().getManagementStrategy().removeEventNotifier(notifier);
+
+ getManagementStrategy().removeEventNotifier(notifier);
notifier = null;
}
+
super.doStop();
}
void onEndpointAdd(KameletEndpoint endpoint) {
if (notifier == null) {
try {
- addRouteFromTemplate(endpoint);
+ Kamelet.createRouteForEndpoint(endpoint);
} catch (Exception e) {
throw RuntimeCamelException.wrapRuntimeException(e);
}
} else {
// remember endpoints as we defer adding routes for them till later
- this.endpoints.add(endpoint);
+ notifier.track(endpoint);
}
}
- void addRouteFromTemplate(KameletEndpoint endpoint) throws Exception {
- ModelCamelContext context = endpoint.getCamelContext().adapt(ModelCamelContext.class);
- String id = context.addRouteFromTemplate(endpoint.getRouteId(), endpoint.getTemplateId(), endpoint.getKameletProperties());
- RouteDefinition def = context.getRouteDefinition(id);
- if (!def.isPrepared()) {
- List list = new ArrayList<>(1);
- list.add(def);
- context.startRouteDefinitions(list);
- }
+ private ManagementStrategy getManagementStrategy() {
+ return getCamelContext().adapt(ModelCamelContext.class).getManagementStrategy();
}
- private static class RouteTemplateEventNotifier extends EventNotifierSupport {
-
- private final KameletComponent component;
-
- public RouteTemplateEventNotifier(KameletComponent component) {
- this.component = component;
+ /*
+ * This EventNotifier is used to keep track of created kamelet endpoints during startup as
+ * we need to defer create routes from templates until camel context has finished loading
+ * all routes and whatnot.
+ *
+ * Once the camel context is initialized all the endpoint tracked by this EventNotifier will
+ * be used to create route s from templates.
+ */
+ private class EventHandler extends EventNotifierSupport {
+ private final List endpoints;
+
+ public EventHandler() {
+ this.endpoints = new ArrayList<>();
}
@Override
public void notify(CamelEvent event) throws Exception {
- for (KameletEndpoint endpoint : component.endpoints) {
- component.addRouteFromTemplate(endpoint);
+ for (KameletEndpoint endpoint : endpoints) {
+ Kamelet.createRouteForEndpoint(endpoint);
}
- component.endpoints.clear();
+
+ endpoints.clear();
+
// we were only needed during initializing/starting up camel, so remove after use
ServiceHelper.stopService(this);
- component.getCamelContext().getManagementStrategy().removeEventNotifier(this);
- component.notifier = null;
+
+ getManagementStrategy().removeEventNotifier(this);
+ notifier = null;
}
@Override
@@ -192,5 +153,8 @@ public boolean isEnabled(CamelEvent event) {
return event instanceof CamelEvent.CamelContextInitializedEvent;
}
+ public void track(KameletEndpoint endpoint) {
+ this.endpoints.add(endpoint);
+ }
}
}
diff --git a/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java b/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java
index 760964732..cae7cd4e3 100644
--- a/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java
+++ b/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java
@@ -16,6 +16,7 @@
*/
package org.apache.camel.component.kamelet;
+import java.util.Collections;
import java.util.Map;
import org.apache.camel.AsyncCallback;
@@ -32,6 +33,7 @@
import org.apache.camel.support.DefaultConsumer;
import org.apache.camel.support.DefaultEndpoint;
import org.apache.camel.support.service.ServiceHelper;
+import org.apache.camel.util.ObjectHelper;
@UriEndpoint(
firstVersion = "3.5.0",
@@ -60,9 +62,13 @@ public KameletEndpoint(
super(uri, component);
+ ObjectHelper.notNull(templateId, "template id");
+ ObjectHelper.notNull(routeId, "route id");
+ ObjectHelper.notNull(kameletProperties, "kamelet properties");
+
this.templateId = templateId;
this.routeId = routeId;
- this.kameletProperties = kameletProperties;
+ this.kameletProperties = Collections.unmodifiableMap(kameletProperties);
this.kameletUri = "direct:" + routeId;
}
@@ -98,7 +104,7 @@ public Consumer createConsumer(Processor processor) throws Exception {
@Override
protected void doInit() throws Exception {
super.doInit();
- // only need to add during init phase
+
getComponent().onEndpointAdd(this);
}
diff --git a/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletAddAfterCamelStartedTest.java b/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletAddAfterCamelStartedTest.java
deleted file mode 100644
index fdc9dc676..000000000
--- a/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletAddAfterCamelStartedTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.component.kamelet;
-
-import java.util.UUID;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.impl.DefaultCamelContext;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class KameletAddAfterCamelStartedTest {
- private static final Logger LOGGER = LoggerFactory.getLogger(KameletAddAfterCamelStartedTest.class);
-
- @Test
- public void test() throws Exception {
- String body = UUID.randomUUID().toString();
-
- CamelContext context = new DefaultCamelContext();
- context.addRoutes(new RouteBuilder() {
- @Override
- public void configure() throws Exception {
- routeTemplate("setBody")
- .templateParameter("bodyValue")
- .from("direct:{{routeId}}")
- .setBody().constant("{{bodyValue}}");
- }
- });
-
- /*
- context.addRouteFromTemplate("setBody")
- .routeId("test")
- .parameter("routeId", "test")
- .parameter("bodyValue", body)
- .build();
- */
-
- // start camel here and add routes with kamelts later
- context.start();
-
- context.addRoutes(new RouteBuilder() {
- @Override
- public void configure() throws Exception {
- // routes
- from("direct:template")
- .toF("kamelet:setBody/test?bodyValue=%s", body)
- .to("log:1");
- }
- });
-
- assertThat(
- context.createFluentProducerTemplate().to("direct:template").withBody("test").request(String.class)
- ).isEqualTo(body);
-
- context.stop();
- }
-}
diff --git a/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletBasicTest.java b/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletBasicTest.java
new file mode 100644
index 000000000..a7363ba82
--- /dev/null
+++ b/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletBasicTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.component.kamelet;
+
+import java.util.UUID;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.http.annotation.Obsolete;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class KameletBasicTest extends CamelTestSupport {
+ @Test
+ public void canProduceToKamelets() {
+ String body = UUID.randomUUID().toString();
+
+ assertThat(
+ fluentTemplate.toF("kamelet:setBody/test?bodyValue=%s", body).request(String.class)
+ ).isEqualTo(body);
+ }
+
+ @Test
+ public void canConsumeFromKamelets() {
+ assertThat(
+ consumer.receiveBody("kamelet:tick", Integer.class)
+ ).isEqualTo(1);
+ }
+
+ @Test
+ public void kameletsCanBeCreatedAfterContextIsStarted() throws Exception {
+ String body = UUID.randomUUID().toString();
+
+ RouteBuilder.addRoutes(context, b -> {
+ b.from("direct:template")
+ .toF("kamelet:setBody/test?bodyValue=%s", body);
+ });
+
+ assertThat(
+ fluentTemplate.to("direct:template").request(String.class)
+ ).isEqualTo(body);
+ }
+
+ // **********************************************
+ //
+ // test set-up
+ //
+ // **********************************************
+
+ @Obsolete
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ routeTemplate("setBody")
+ .templateParameter("bodyValue")
+ .from("direct:{{routeId}}")
+ .setBody().constant("{{bodyValue}}");
+
+ routeTemplate("tick")
+ .from("timer:{{routeId}}?repeatCount=1&delay=-1")
+ .setBody().exchangeProperty(Exchange.TIMER_COUNTER)
+ .to("direct:{{routeId}}");
+ }
+ };
+ }
+}
diff --git a/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletPropertiesTest.java b/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletPropertiesTest.java
new file mode 100644
index 000000000..d33a15bb1
--- /dev/null
+++ b/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletPropertiesTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.component.kamelet;
+
+import java.util.Properties;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.apache.http.annotation.Obsolete;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.k.test.CamelKTestSupport.asProperties;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class KameletPropertiesTest extends CamelTestSupport {
+ @Test
+ public void propertiesAreTakenFromRouteId() throws Exception {
+ assertThat(
+ fluentTemplate
+ .to("kamelet:setBody/test")
+ .request(String.class)
+ ).isEqualTo("from-route");
+ }
+
+ @Test
+ public void propertiesAreTakenFromTemplateId() throws Exception {
+ assertThat(
+ fluentTemplate
+ .to("kamelet:setBody")
+ .request(String.class)
+ ).isEqualTo("from-template");
+ }
+
+ @Test
+ public void propertiesAreTakenFromURI() {
+ assertThat(
+ fluentTemplate
+ .to("kamelet:setBody?bodyValue={{bodyValue}}")
+ .request(String.class)
+ ).isEqualTo("from-uri");
+ }
+
+ // **********************************************
+ //
+ // test set-up
+ //
+ // **********************************************
+
+ @Override
+ protected Properties useOverridePropertiesWithPropertiesComponent() {
+ return asProperties(
+ "bodyValue", "from-uri",
+ Kamelet.PROPERTIES_PREFIX + "setBody.bodyValue", "from-template",
+ Kamelet.PROPERTIES_PREFIX + "setBody.test.bodyValue", "from-route"
+ );
+ }
+
+ @Obsolete
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ // template
+ routeTemplate("setBody")
+ .templateParameter("bodyValue")
+ .from("direct:{{routeId}}")
+ .setBody().constant("{{bodyValue}}");
+ }
+ };
+ }
+}
diff --git a/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletTest.java b/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletTest.java
deleted file mode 100644
index 32634a0d8..000000000
--- a/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.component.kamelet;
-
-import java.util.UUID;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.impl.DefaultCamelContext;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class KameletTest {
- private static final Logger LOGGER = LoggerFactory.getLogger(KameletTest.class);
-
- @Test
- public void test() throws Exception {
- String body = UUID.randomUUID().toString();
-
- CamelContext context = new DefaultCamelContext();
- context.addRoutes(new RouteBuilder() {
- @Override
- public void configure() throws Exception {
- routeTemplate("setBody")
- .templateParameter("bodyValue")
- .from("direct:{{routeId}}")
- .setBody().constant("{{bodyValue}}");
- }
- });
-
- /*
- context.addRouteFromTemplate("setBody")
- .routeId("test")
- .parameter("routeId", "test")
- .parameter("bodyValue", body)
- .build();
- */
-
- context.addRoutes(new RouteBuilder() {
- @Override
- public void configure() throws Exception {
- // routes
- from("direct:template")
- .toF("kamelet:setBody/test?bodyValue=%s", body)
- .to("log:1");
- }
- });
-
- context.start();
-
- assertThat(
- context.createFluentProducerTemplate().to("direct:template").withBody("test").request(String.class)
- ).isEqualTo(body);
-
- context.stop();
- }
-}
diff --git a/camel-kamelet/src/test/resources/log4j2-test.xml b/camel-kamelet/src/test/resources/log4j2-test.xml
index 486a0f041..bc3cb8c92 100644
--- a/camel-kamelet/src/test/resources/log4j2-test.xml
+++ b/camel-kamelet/src/test/resources/log4j2-test.xml
@@ -26,11 +26,13 @@
+
+
-
+
diff --git a/examples/camel-k-runtime-example-groovy/data/application.properties b/examples/camel-k-runtime-example-groovy/data/application.properties
index ac98d6e1d..3be9292e4 100644
--- a/examples/camel-k-runtime-example-groovy/data/application.properties
+++ b/examples/camel-k-runtime-example-groovy/data/application.properties
@@ -16,7 +16,7 @@
## ---------------------------------------------------------------------------
#
-# Logging
+# logging
#
logging.level.org.apache.camel.k = DEBUG
@@ -27,5 +27,14 @@ camel.main.name = camel-k
camel.main.stream-caching-enabled = true
camel.main.stream-caching-spool-directory = ${java.io.tmpdir}/camel-k
+#
+# camel-k - sources
+#
+camel.k.sources[0].name = routes
+camel.k.sources[0].language = groovy
+camel.k.sources[0].location = file:{{data.dir}}/routes.groovy
+#
+# misc
+#
message = test
diff --git a/examples/camel-k-runtime-example-groovy/pom.xml b/examples/camel-k-runtime-example-groovy/pom.xml
index 8514eb9da..661f2b90b 100644
--- a/examples/camel-k-runtime-example-groovy/pom.xml
+++ b/examples/camel-k-runtime-example-groovy/pom.xml
@@ -69,8 +69,8 @@
${project.basedir}/data/application.properties
- camel.k.routes
- file:${project.basedir}/data/routes.groovy
+ data.dir
+ ${project.basedir}/data
diff --git a/tooling/camel-k-test/src/main/java/org/apache/camel/k/test/CamelKTestSupport.java b/tooling/camel-k-test/src/main/java/org/apache/camel/k/test/CamelKTestSupport.java
new file mode 100644
index 000000000..05c6e64bc
--- /dev/null
+++ b/tooling/camel-k-test/src/main/java/org/apache/camel/k/test/CamelKTestSupport.java
@@ -0,0 +1,48 @@
+/*
+ * 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.test;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+
+public final class CamelKTestSupport {
+ private CamelKTestSupport() {
+ }
+
+ public static Properties asProperties(String... properties) {
+ if ((properties.length & 1) != 0) {
+ throw new InternalError("length is odd");
+ }
+
+ Properties answer = new Properties();
+ for (int i = 0; i < properties.length; i += 2) {
+ answer.setProperty(
+ Objects.requireNonNull(properties[i]),
+ Objects.requireNonNull(properties[i + 1]));
+ }
+
+ return answer;
+ }
+
+ public static Properties asProperties(Map properties) {
+ Properties answer = new Properties();
+ answer.putAll(properties);
+
+ return answer;
+ }
+}