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 02551d887..93855b223 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 @@ -34,6 +34,7 @@ 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; @@ -203,14 +204,13 @@ protected void doInit() throws Exception { @Override protected void doStart() throws Exception { super.doStart(); - if (getCamelContext() != null) { - try { - // if we were veto started then mark as completed - getCamelContext().start(); - } finally { - if (getCamelContext().isVetoStarted()) { - completed(); - } + + try { + // if we were veto started then mark as completed + getCamelContext().start(); + } finally { + if (getCamelContext().isVetoStarted()) { + completed(); } } } @@ -218,18 +218,12 @@ protected void doStart() throws Exception { @Override protected void doStop() throws Exception { super.doStop(); - if (getCamelContext() != null) { - getCamelContext().stop(); - } + getCamelContext().stop(); } @Override protected ProducerTemplate findOrCreateCamelTemplate() { - if (getCamelContext() != null) { - return getCamelContext().createProducerTemplate(); - } else { - return null; - } + return getCamelContext().createProducerTemplate(); } } @@ -244,6 +238,11 @@ public List collectXmlRoutesFromDirectory(CamelContext camelCo return Collections.emptyList(); } + @Override + public List collectXmlRouteTemplatesFromDirectory(CamelContext camelContext, String directory) throws Exception { + return Collections.emptyList(); + } + @Override public List collectXmlRestsFromDirectory(CamelContext camelContext, String directory) throws Exception { return Collections.emptyList(); diff --git a/camel-k-runtime-bom/pom.xml b/camel-k-runtime-bom/pom.xml index 4b1622fe3..0ef61efb8 100644 --- a/camel-k-runtime-bom/pom.xml +++ b/camel-k-runtime-bom/pom.xml @@ -134,6 +134,11 @@ camel-knative-test ${project.version} + + org.apache.camel.k + camel-kamelet + ${project.version} + org.apache.camel.k camel-k-maven-plugin diff --git a/camel-kamelet/pom.xml b/camel-kamelet/pom.xml new file mode 100644 index 000000000..33e09c34f --- /dev/null +++ b/camel-kamelet/pom.xml @@ -0,0 +1,137 @@ + + + + + org.apache.camel.k + camel-k-runtime-parent + 1.5.0-SNAPSHOT + + 4.0.0 + + camel-kamelet + + + + + + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + org.apache.camel + camel-core-engine + + + org.apache.camel + camel-direct + + + + + + + + + + org.apache.camel.k + camel-k-test + test + + + org.apache.camel + camel-core-languages + test + + + org.apache.camel + camel-timer + test + + + org.apache.camel + camel-log + test + + + + + + + + org.jboss.jandex + jandex-maven-plugin + + + make-index + + jandex + + + + + + org.apache.camel + camel-component-maven-plugin + ${camel.version} + + + generate + + generate + + process-classes + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${build-helper-maven-plugin.version} + + + generate-sources + + add-source + add-resource + + + + src/generated/java + + + + src/generated/resources + + + + + + + + + + diff --git a/camel-kamelet/src/generated/java/org/apache/camel/component/kamelet/KameletComponentConfigurer.java b/camel-kamelet/src/generated/java/org/apache/camel/component/kamelet/KameletComponentConfigurer.java new file mode 100644 index 000000000..a0a384d7f --- /dev/null +++ b/camel-kamelet/src/generated/java/org/apache/camel/component/kamelet/KameletComponentConfigurer.java @@ -0,0 +1,55 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.component.kamelet; + +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.support.component.PropertyConfigurerSupport; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@SuppressWarnings("unchecked") +public class KameletComponentConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + KameletComponent target = (KameletComponent) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "basicpropertybinding": + case "basicPropertyBinding": target.setBasicPropertyBinding(property(camelContext, boolean.class, value)); return true; + case "bridgeerrorhandler": + case "bridgeErrorHandler": target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); return true; + case "lazystartproducer": + case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; + default: return false; + } + } + + @Override + public Map getAllOptions(Object target) { + Map answer = new CaseInsensitiveMap(); + answer.put("basicPropertyBinding", boolean.class); + answer.put("bridgeErrorHandler", boolean.class); + answer.put("lazyStartProducer", boolean.class); + return answer; + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + KameletComponent target = (KameletComponent) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "basicpropertybinding": + case "basicPropertyBinding": return target.isBasicPropertyBinding(); + case "bridgeerrorhandler": + case "bridgeErrorHandler": return target.isBridgeErrorHandler(); + case "lazystartproducer": + case "lazyStartProducer": return target.isLazyStartProducer(); + default: return null; + } + } +} + diff --git a/camel-kamelet/src/generated/java/org/apache/camel/component/kamelet/KameletEndpointConfigurer.java b/camel-kamelet/src/generated/java/org/apache/camel/component/kamelet/KameletEndpointConfigurer.java new file mode 100644 index 000000000..bb99f6380 --- /dev/null +++ b/camel-kamelet/src/generated/java/org/apache/camel/component/kamelet/KameletEndpointConfigurer.java @@ -0,0 +1,68 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.component.kamelet; + +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.support.component.PropertyConfigurerSupport; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@SuppressWarnings("unchecked") +public class KameletEndpointConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + KameletEndpoint target = (KameletEndpoint) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "basicpropertybinding": + case "basicPropertyBinding": target.setBasicPropertyBinding(property(camelContext, boolean.class, value)); return true; + case "bridgeerrorhandler": + case "bridgeErrorHandler": target.setBridgeErrorHandler(property(camelContext, boolean.class, value)); return true; + case "exceptionhandler": + case "exceptionHandler": target.setExceptionHandler(property(camelContext, org.apache.camel.spi.ExceptionHandler.class, value)); return true; + case "exchangepattern": + case "exchangePattern": target.setExchangePattern(property(camelContext, org.apache.camel.ExchangePattern.class, value)); return true; + case "lazystartproducer": + case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; + case "synchronous": target.setSynchronous(property(camelContext, boolean.class, value)); return true; + default: return false; + } + } + + @Override + public Map getAllOptions(Object target) { + Map answer = new CaseInsensitiveMap(); + answer.put("basicPropertyBinding", boolean.class); + answer.put("bridgeErrorHandler", boolean.class); + answer.put("exceptionHandler", org.apache.camel.spi.ExceptionHandler.class); + answer.put("exchangePattern", org.apache.camel.ExchangePattern.class); + answer.put("lazyStartProducer", boolean.class); + answer.put("synchronous", boolean.class); + return answer; + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + KameletEndpoint target = (KameletEndpoint) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "basicpropertybinding": + case "basicPropertyBinding": return target.isBasicPropertyBinding(); + case "bridgeerrorhandler": + case "bridgeErrorHandler": return target.isBridgeErrorHandler(); + case "exceptionhandler": + case "exceptionHandler": return target.getExceptionHandler(); + case "exchangepattern": + case "exchangePattern": return target.getExchangePattern(); + case "lazystartproducer": + case "lazyStartProducer": return target.isLazyStartProducer(); + case "synchronous": return target.isSynchronous(); + default: return null; + } + } +} + diff --git a/camel-kamelet/src/generated/resources/META-INF/services/org/apache/camel/component/kamelet b/camel-kamelet/src/generated/resources/META-INF/services/org/apache/camel/component/kamelet new file mode 100644 index 000000000..be8044e7f --- /dev/null +++ b/camel-kamelet/src/generated/resources/META-INF/services/org/apache/camel/component/kamelet @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.kamelet.KameletComponent diff --git a/camel-kamelet/src/generated/resources/META-INF/services/org/apache/camel/configurer/kamelet-component b/camel-kamelet/src/generated/resources/META-INF/services/org/apache/camel/configurer/kamelet-component new file mode 100644 index 000000000..88d3725c2 --- /dev/null +++ b/camel-kamelet/src/generated/resources/META-INF/services/org/apache/camel/configurer/kamelet-component @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.kamelet.KameletComponentConfigurer diff --git a/camel-kamelet/src/generated/resources/META-INF/services/org/apache/camel/configurer/kamelet-endpoint b/camel-kamelet/src/generated/resources/META-INF/services/org/apache/camel/configurer/kamelet-endpoint new file mode 100644 index 000000000..17182a5e3 --- /dev/null +++ b/camel-kamelet/src/generated/resources/META-INF/services/org/apache/camel/configurer/kamelet-endpoint @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.kamelet.KameletEndpointConfigurer 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 new file mode 100644 index 000000000..93ef4969b --- /dev/null +++ b/camel-kamelet/src/generated/resources/org/apache/camel/component/kamelet/kamelet.json @@ -0,0 +1,38 @@ +{ + "component": { + "kind": "component", + "name": "kamelet", + "title": "Kamelet", + "description": "The Apache Software Foundation provides support for the Apache community of open-source software projects.\n The Apache projects are characterized by a collaborative, consensus based development process, an open and\n pragmatic software license, and a desire to create high quality software that leads the way in its field.\n We consider ourselves not simply a group of projects sharing a server, but rather a community of developers\n and users.", + "deprecated": false, + "firstVersion": "3.5.0", + "label": "camel-k", + "javaType": "org.apache.camel.component.kamelet.KameletComponent", + "supportLevel": "Preview", + "groupId": "org.apache.camel.k", + "artifactId": "camel-kamelet", + "version": "1.5.0-SNAPSHOT", + "scheme": "kamelet", + "extendsScheme": "", + "syntax": "kamelet:templateId\/routeId", + "async": false, + "consumerOnly": false, + "producerOnly": false, + "lenientProperties": false + }, + "componentProperties": { + "bridgeErrorHandler": { "kind": "property", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored." }, + "lazyStartProducer": { "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, + "basicPropertyBinding": { "kind": "property", "displayName": "Basic Property Binding", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "secret": false, "defaultValue": false, "description": "Whether the component should use basic property binding (Camel 2.x) or the newer property binding with additional capabilities" } + }, + "properties": { + "templateId": { "kind": "path", "displayName": "Template Id", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "secret": false, "description": "The Route Template ID" }, + "routeId": { "kind": "path", "displayName": "Route Id", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "secret": false, "description": "The Route ID. Default value notice: The ID will be auto-generated if not provided" }, + "bridgeErrorHandler": { "kind": "parameter", "displayName": "Bridge Error Handler", "group": "consumer", "label": "consumer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "secret": false, "defaultValue": false, "description": "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while the consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler. By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored." }, + "exceptionHandler": { "kind": "parameter", "displayName": "Exception Handler", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", "deprecated": false, "secret": false, "description": "To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this option is not in use. By default the consumer will deal with exceptions, that will be logged at WARN or ERROR level and ignored." }, + "exchangePattern": { "kind": "parameter", "displayName": "Exchange Pattern", "group": "consumer (advanced)", "label": "consumer,advanced", "required": false, "type": "object", "javaType": "org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut", "InOptionalOut" ], "deprecated": false, "secret": false, "description": "Sets the exchange pattern when the consumer creates an exchange." }, + "lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, + "basicPropertyBinding": { "kind": "parameter", "displayName": "Basic Property Binding", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "secret": false, "defaultValue": false, "description": "Whether the endpoint should use basic property binding (Camel 2.x) or the newer property binding with additional capabilities" }, + "synchronous": { "kind": "parameter", "displayName": "Synchronous", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "secret": false, "defaultValue": "false", "description": "Sets whether synchronous processing should be strictly used, or Camel is allowed to use asynchronous processing (if supported)." } + } +} 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 new file mode 100644 index 000000000..043f44eb2 --- /dev/null +++ b/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java @@ -0,0 +1,30 @@ +/* + * 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.function.Predicate; + +public final class Kamelet { + public static final String SCHEME = "kamelet"; + + private Kamelet() { + } + + public static Predicate startsWith(String prefix) { + return item -> item.startsWith(prefix); + } +} 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 new file mode 100644 index 000000000..f5c6f6e69 --- /dev/null +++ b/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java @@ -0,0 +1,107 @@ +/* + * 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.HashMap; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.Endpoint; +import org.apache.camel.spi.annotations.Component; +import org.apache.camel.support.DefaultComponent; +import org.apache.camel.util.StringHelper; + +@Component(Kamelet.SCHEME) +public class KameletComponent extends DefaultComponent { + public KameletComponent() { + this(null); + } + + public KameletComponent(CamelContext context) { + super(context); + } + + @Override + protected Endpoint createEndpoint(String uri, String remaining, Map parameters) throws Exception { + final String templateId = extractTemplateId(remaining); + final String routeId = extractRouteId(remaining); + + // + // The properties for the kamelets are determined by global properties + // and local endpoint parametes, + // + // Global parameters are loaded in the following order: + // + // camel.kamelet." + templateId + // camel.kamelet." + templateId + "." routeId + // + Map kameletProperties = extractKameletProperties(templateId, routeId); + kameletProperties.putAll(parameters); + kameletProperties.putIfAbsent("templateId", templateId); + kameletProperties.putIfAbsent("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. + parameters.clear(); + + KameletEndpoint endpoint = new KameletEndpoint(uri, this, templateId, routeId, kameletProperties); + + // No parameters are expected here. + setProperties(endpoint, parameters); + + return endpoint; + } + + private String extractTemplateId(String remaining) { + String answer = StringHelper.before(remaining, "/"); + if (answer == null) { + answer = remaining; + } + + return answer; + } + + private String extractRouteId(String remaining) { + String answer = StringHelper.after(remaining, "/"); + if (answer == null) { + answer = extractTemplateId(remaining) + "-" + getCamelContext().getUuidGenerator().generateUuid(); + } + + return answer; + } + + @SuppressWarnings("unchecked") + private 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; + } +} 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 new file mode 100644 index 000000000..22cd5430b --- /dev/null +++ b/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletEndpoint.java @@ -0,0 +1,169 @@ +/* + * 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.Map; + +import org.apache.camel.Consumer; +import org.apache.camel.Endpoint; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.Producer; +import org.apache.camel.spi.Metadata; +import org.apache.camel.spi.UriEndpoint; +import org.apache.camel.spi.UriPath; +import org.apache.camel.support.DefaultConsumer; +import org.apache.camel.support.DefaultEndpoint; +import org.apache.camel.support.DefaultProducer; +import org.apache.camel.support.service.ServiceHelper; + +@UriEndpoint( + firstVersion = "3.5.0", + scheme = "kamelet", + syntax = "kamelet:templateId/routeId", + title = "Kamelet", + label = "camel-k") +public class KameletEndpoint extends DefaultEndpoint { + @Metadata(required = true) + @UriPath(description = "The Route Template ID") + private final String templateId; + + @Metadata(required = false) + @UriPath(description = "The Route ID", defaultValueNote = "The ID will be auto-generated if not provided") + private final String routeId; + + private final Map kameletProperties; + private final String kameletUri; + + public KameletEndpoint( + String uri, + KameletComponent component, + String templateId, + String routeId, + Map kameletProperties) { + + super(uri, component); + + this.templateId = templateId; + this.routeId = routeId; + this.kameletProperties = kameletProperties; + this.kameletUri = "direct:" + routeId; + } + + public String getTemplateId() { + return templateId; + } + + public String getRouteId() { + return routeId; + } + + @Override + public Producer createProducer() throws Exception { + return new KameletProducer(); + } + + @Override + public Consumer createConsumer(Processor processor) throws Exception { + Consumer answer = new KemeletConsumer(processor); + configureConsumer(answer); + + return answer; + } + + @Override + protected void doStart() throws Exception { + try { + // Add a route to the camel context from the given template + // TODO: add validation (requires: https://issues.apache.org/jira/browse/CAMEL-15312) + getCamelContext().addRouteFromTemplate(routeId, templateId, kameletProperties); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + + super.doStart(); + } + + // ********************************* + // + // Helpers + // + // ********************************* + + private class KemeletConsumer extends DefaultConsumer { + private volatile Endpoint endpoint; + private volatile Consumer consumer; + + public KemeletConsumer(Processor processor) { + super(KameletEndpoint.this, processor); + } + + @Override + protected void doStart() throws Exception { + endpoint = getCamelContext().getEndpoint(kameletUri); + consumer = endpoint.createConsumer(getProcessor()); + + ServiceHelper.startService(endpoint); + ServiceHelper.startService(consumer); + + super.doStart(); + } + + @Override + protected void doStop() throws Exception { + ServiceHelper.stopService(endpoint); + ServiceHelper.stopService(consumer); + + super.doStop(); + } + } + + private class KameletProducer extends DefaultProducer { + private volatile Endpoint endpoint; + private volatile Producer producer; + + public KameletProducer() { + super(KameletEndpoint.this); + } + + @Override + public void process(Exchange exchange) throws Exception { + if (producer != null) { + producer.process(exchange); + } + } + + @Override + protected void doStart() throws Exception { + endpoint = getCamelContext().getEndpoint(kameletUri); + producer = endpoint.createProducer(); + + ServiceHelper.startService(endpoint); + ServiceHelper.startService(producer); + + super.doStart(); + } + + @Override + protected void doStop() throws Exception { + ServiceHelper.stopService(endpoint); + ServiceHelper.stopService(producer); + + super.doStop(); + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..8f95084cf --- /dev/null +++ b/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletTest.java @@ -0,0 +1,74 @@ +/* + * 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") + .to("kamelet:setBody/test?bodyValue=bv") + .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 new file mode 100644 index 000000000..486a0f041 --- /dev/null +++ b/camel-kamelet/src/test/resources/log4j2-test.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index a7ab99aa8..405ad8868 100644 --- a/pom.xml +++ b/pom.xml @@ -38,7 +38,7 @@ 11 11 - 3.4.2 + 3.5.0-SNAPSHOT 1.0.0 5.6.2 0.9.0 @@ -274,6 +274,7 @@ tooling camel-knative + camel-kamelet camel-k-runtime-core camel-k-main @@ -380,6 +381,11 @@ camel-knative-test ${project.version} + + org.apache.camel.k + camel-kamelet + ${project.version} +