diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index c5e11e83c5c63..8cfe8024e9098 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -797,6 +797,11 @@
camel-flink
4.9.0-SNAPSHOT
+
+ org.apache.camel
+ camel-flowable
+ 4.9.0-SNAPSHOT
+
org.apache.camel
camel-fop
diff --git a/catalog/camel-allcomponents/pom.xml b/catalog/camel-allcomponents/pom.xml
index fcb5049fc8b7b..1be613cbc73a4 100644
--- a/catalog/camel-allcomponents/pom.xml
+++ b/catalog/camel-allcomponents/pom.xml
@@ -671,6 +671,11 @@
camel-flink
${project.version}
+
+ org.apache.camel
+ camel-flowable
+ ${project.version}
+
org.apache.camel
camel-fop
diff --git a/components/camel-flowable/pom.xml b/components/camel-flowable/pom.xml
new file mode 100644
index 0000000000000..8e15ca6d806c4
--- /dev/null
+++ b/components/camel-flowable/pom.xml
@@ -0,0 +1,110 @@
+
+
+
+
+ 4.0.0
+
+
+ org.apache.camel
+ components
+ 4.9.0-SNAPSHOT
+
+
+ camel-flowable
+ jar
+ Camel :: Flowable
+ Camel Flowable support
+
+
+ UTF-8
+ UTF-8
+
+
+
+
+ org.apache.camel
+ camel-core
+
+
+ org.apache.camel
+ camel-spring
+
+
+
+
+ org.apache.camel
+ camel-test-spring-junit5
+ test
+
+
+
+
+ org.flowable
+ flowable-engine
+ ${flowable-version}
+
+
+ org.flowable
+ flowable-spring
+ ${flowable-version}
+
+
+ org.flowable
+ flowable-event-registry-spring
+ ${flowable-version}
+
+
+ org.apache.logging.log4j
+ log4j-slf4j-impl
+ ${log4j2-version}
+ test
+
+
+ org.springframework
+ spring-test
+ provided
+ ${spring-version}
+
+
+ org.springframework
+ spring-jdbc
+ provided
+ ${spring-version}
+
+
+ com.zaxxer
+ HikariCP
+ test
+ ${hikari-version}
+
+
+ com.h2database
+ h2
+ test
+ ${h2-version}
+
+
+ junit
+ junit
+ test
+ ${junit-version}
+
+
+
diff --git a/components/camel-flowable/src/generated/resources/META-INF/services/org/apache/camel/component.properties b/components/camel-flowable/src/generated/resources/META-INF/services/org/apache/camel/component.properties
new file mode 100644
index 0000000000000..9b4c23e637471
--- /dev/null
+++ b/components/camel-flowable/src/generated/resources/META-INF/services/org/apache/camel/component.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+components=flowable
+groupId=org.apache.camel
+artifactId=camel-flowable
+version=4.9.0-SNAPSHOT
+projectName=Camel :: Flowable
+projectDescription=Camel Flowable support
diff --git a/components/camel-flowable/src/generated/resources/META-INF/services/org/apache/camel/component/flowable b/components/camel-flowable/src/generated/resources/META-INF/services/org/apache/camel/component/flowable
new file mode 100644
index 0000000000000..e7e59e963fad9
--- /dev/null
+++ b/components/camel-flowable/src/generated/resources/META-INF/services/org/apache/camel/component/flowable
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.flowable.FlowableComponent
diff --git a/components/camel-flowable/src/main/docs/flowable-component.adoc b/components/camel-flowable/src/main/docs/flowable-component.adoc
new file mode 100644
index 0000000000000..c01bf35c70d75
--- /dev/null
+++ b/components/camel-flowable/src/main/docs/flowable-component.adoc
@@ -0,0 +1,71 @@
+= Flowable Component
+:doctitle: Flowable
+:shortname: flowable
+:artifactid: camel-flowable
+:description: Communicate with the Flowable Event Registry for sending and receiving messages from the Flowable BPMN and CMMN engines.
+:since: 4.9.0
+:supportlevel: Stable
+:tabs-sync-option:
+:component-header: Both producer and consumer are supported
+//Manually maintained attributes
+
+*Since Camel {since}*
+
+*{component-header}*
+
+Communicate with the Flowable Event Registry for sending and receiving messages from the Flowable BPMN and CMMN engines.
+
+== URI format
+
+------------------------------
+flowable-event://[channelKey]?[options]
+------------------------------
+
+Where *channelKey* is the unique key value of the channel model deployed in the Flowable Event Registry.
+
+// component-configure options: START
+
+// component-configure options: END
+
+// component options: START
+include::partial$component-configure-options.adoc[]
+include::partial$component-endpoint-options.adoc[]
+// component options: END
+
+// endpoint options: START
+
+// endpoint options: END
+
+== Usage
+
+=== Body content
+
+The body content is defined by the event model that is deployed to the Flowable Event Registry.
+The event message can then be mapped to process or case variables in the process BPMN or case CMMN model.
+
+== Examples
+
+The following example sends an event to the Flowable event registry for further processing:
+
+[source,java]
+----------------------------------------------------------------------
+from("direct:start").to("flowable:exampleChannel");
+----------------------------------------------------------------------
+
+== Dependencies
+
+To use Flowable in your Camel routes, you need to add a dependency on
+*camel-flowable*, which implements the component.
+
+If you use Maven, you can add the following to your pom.xml,
+substituting the version number for the latest and greatest release (see
+the download page for the latest versions).
+
+[source,xml]
+-------------------------------------
+
+ org.apache.camel
+ camel-flowable
+ x.x.x
+
+-------------------------------------
diff --git a/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/CamelChannelModelProcessor.java b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/CamelChannelModelProcessor.java
new file mode 100644
index 0000000000000..4844d583ec2f5
--- /dev/null
+++ b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/CamelChannelModelProcessor.java
@@ -0,0 +1,181 @@
+/*
+ * 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.flowable;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.commons.lang3.StringUtils;
+import org.flowable.common.engine.api.FlowableException;
+import org.flowable.eventregistry.api.ChannelModelProcessor;
+import org.flowable.eventregistry.api.EventRegistry;
+import org.flowable.eventregistry.api.EventRepositoryService;
+import org.flowable.eventregistry.api.OutboundEventChannelAdapter;
+import org.flowable.eventregistry.impl.EventRegistryEngineConfiguration;
+import org.flowable.eventregistry.model.CamelInboundChannelModel;
+import org.flowable.eventregistry.model.CamelOutboundChannelModel;
+import org.flowable.eventregistry.model.ChannelModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.beans.factory.config.EmbeddedValueResolver;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.util.StringValueResolver;
+
+public class CamelChannelModelProcessor
+ implements BeanFactoryAware, ApplicationContextAware, ApplicationListener,
+ ChannelModelProcessor {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ protected CamelContext camelContext;
+
+ protected BeanFactory beanFactory;
+ protected ApplicationContext applicationContext;
+ protected boolean contextRefreshed;
+
+ protected StringValueResolver embeddedValueResolver;
+
+ public CamelChannelModelProcessor(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ public boolean canProcess(ChannelModel channelModel) {
+ return channelModel instanceof CamelInboundChannelModel || channelModel instanceof CamelOutboundChannelModel;
+ }
+
+ @Override
+ public boolean canProcessIfChannelModelAlreadyRegistered(ChannelModel channelModel) {
+ return channelModel instanceof CamelOutboundChannelModel;
+ }
+
+ @Override
+ public void registerChannelModel(
+ ChannelModel channelModel, String tenantId, EventRegistry eventRegistry,
+ EventRepositoryService eventRepositoryService,
+ boolean fallbackToDefaultTenant) {
+
+ if (channelModel instanceof CamelInboundChannelModel) {
+ CamelInboundChannelModel camelInboundChannelModel = (CamelInboundChannelModel) channelModel;
+ logger.info("Starting to register inbound channel {} in tenant {}", channelModel.getKey(), tenantId);
+ processInboundDefinition(camelInboundChannelModel, tenantId);
+ logger.info("Finished registering inbound channel {} in tenant {}", channelModel.getKey(), tenantId);
+
+ } else if (channelModel instanceof CamelOutboundChannelModel) {
+ logger.info("Starting to register outbound channel {} in tenant {}", channelModel.getKey(), tenantId);
+ processOutboundDefinition((CamelOutboundChannelModel) channelModel, tenantId);
+ logger.info("Finished registering outbound channel {} in tenant {}", channelModel.getKey(), tenantId);
+ }
+ }
+
+ protected void processInboundDefinition(CamelInboundChannelModel channelModel, String tenantId) {
+ try (FlowableEndpoint endpoint
+ = new FlowableEndpoint(channelModel, getEventRegistryEngineConfiguration(), camelContext)) {
+
+ camelContext.addEndpoint(endpoint.getEndpointUri(), endpoint);
+ if (StringUtils.isNotEmpty(channelModel.getSourceUri())) {
+ camelContext.addRoutes(new RouteBuilder() {
+
+ @Override
+ public void configure() throws Exception {
+ from(channelModel.getSourceUri()).routeId(channelModel.getKey()).to(endpoint.getEndpointUri());
+ }
+ });
+ }
+
+ } catch (Exception e) {
+ logger.error("Error creating producer for inbound channel {} in tenant {}", channelModel.getKey(), tenantId);
+ throw new FlowableException(
+ "Error creating producer for inbound channel " + channelModel.getKey() + " in tenant " + tenantId);
+ }
+ }
+
+ protected void processOutboundDefinition(CamelOutboundChannelModel channelModel, String tenantId) {
+ String destination = channelModel.getDestination();
+ if (channelModel.getOutboundEventChannelAdapter() == null && StringUtils.isNotEmpty(destination)) {
+ channelModel.setOutboundEventChannelAdapter(createOutboundEventChannelAdapter(channelModel, tenantId));
+ }
+ }
+
+ protected OutboundEventChannelAdapter createOutboundEventChannelAdapter(
+ CamelOutboundChannelModel channelModel, String tenantId) {
+
+ String destination = resolve(channelModel.getDestination());
+ try (FlowableEndpoint endpoint
+ = new FlowableEndpoint(channelModel, getEventRegistryEngineConfiguration(), camelContext)) {
+ camelContext.addEndpoint(endpoint.getEndpointUri(), endpoint);
+ camelContext.addRoutes(new RouteBuilder() {
+
+ @Override
+ public void configure() throws Exception {
+ from(endpoint).routeId(channelModel.getKey()).to(destination);
+ }
+ });
+
+ return new CamelOperationsOutboundEventChannelAdapter(endpoint);
+
+ } catch (Exception e) {
+ logger.error("Error creating route for outbound channel {} in tenant {}", channelModel.getKey(), tenantId);
+ throw new FlowableException(
+ "Error creating route for outbound channel " + channelModel.getKey() + " in tenant " + tenantId);
+ }
+ }
+
+ @Override
+ public void unregisterChannelModel(
+ ChannelModel channelModel, String tenantId, EventRepositoryService eventRepositoryService) {
+
+ }
+
+ protected String resolve(String value) {
+ if (embeddedValueResolver != null) {
+ return embeddedValueResolver.resolveStringValue(value);
+ } else {
+ return value;
+ }
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+ if (beanFactory instanceof ConfigurableBeanFactory) {
+ this.embeddedValueResolver = new EmbeddedValueResolver((ConfigurableBeanFactory) beanFactory);
+ }
+ }
+
+ protected EventRegistryEngineConfiguration getEventRegistryEngineConfiguration() {
+ return applicationContext.getBean(EventRegistryEngineConfiguration.class);
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
+ @Override
+ public void onApplicationEvent(ContextRefreshedEvent event) {
+ if (event.getApplicationContext() == this.applicationContext) {
+ this.contextRefreshed = true;
+ }
+ }
+}
diff --git a/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/CamelOperationsOutboundEventChannelAdapter.java b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/CamelOperationsOutboundEventChannelAdapter.java
new file mode 100644
index 0000000000000..ea117625433ea
--- /dev/null
+++ b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/CamelOperationsOutboundEventChannelAdapter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.flowable;
+
+import java.util.Map;
+
+import org.apache.camel.Exchange;
+import org.flowable.common.engine.api.FlowableException;
+import org.flowable.eventregistry.api.OutboundEventChannelAdapter;
+
+public class CamelOperationsOutboundEventChannelAdapter implements OutboundEventChannelAdapter {
+
+ protected FlowableEndpoint endpoint;
+
+ public CamelOperationsOutboundEventChannelAdapter(FlowableEndpoint endpoint) {
+ this.endpoint = endpoint;
+ }
+
+ @Override
+ public void sendEvent(String rawEvent, Map headerMap) {
+ Exchange exchange = endpoint.createExchange();
+ exchange.getIn().setBody(rawEvent);
+ exchange.getIn().setHeaders(headerMap);
+
+ try {
+ endpoint.process(exchange);
+ } catch (Exception e) {
+ throw new FlowableException("Exception while processing Camel exchange", e);
+ }
+ }
+}
diff --git a/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableComponent.java b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableComponent.java
new file mode 100644
index 0000000000000..01f8851a47bb8
--- /dev/null
+++ b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableComponent.java
@@ -0,0 +1,59 @@
+/*
+ * 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.flowable;
+
+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.flowable.common.engine.api.FlowableException;
+import org.flowable.common.engine.impl.interceptor.EngineConfigurationConstants;
+import org.flowable.engine.ProcessEngine;
+import org.flowable.eventregistry.impl.EventRegistryEngineConfiguration;
+
+@Component("flowable")
+public class FlowableComponent extends DefaultComponent {
+
+ protected EventRegistryEngineConfiguration eventRegistryEngineConfiguration;
+
+ public FlowableComponent() {
+ }
+
+ @Override
+ public void setCamelContext(CamelContext context) {
+ super.setCamelContext(context);
+ ProcessEngine processEngine = getByType(context, ProcessEngine.class);
+ this.eventRegistryEngineConfiguration = (EventRegistryEngineConfiguration) processEngine.getProcessEngineConfiguration()
+ .getEngineConfigurations().get(EngineConfigurationConstants.KEY_EVENT_REGISTRY_CONFIG);
+ }
+
+ private T getByType(CamelContext ctx, Class kls) {
+ Map looked = ctx.getRegistry().findByTypeWithName(kls);
+ if (looked.isEmpty()) {
+ return null;
+ }
+ return looked.values().iterator().next();
+
+ }
+
+ @Override
+ protected Endpoint createEndpoint(String uri, String remaining, Map parameters) throws Exception {
+ throw new FlowableException("not supported");
+ }
+}
diff --git a/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableConsumer.java b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableConsumer.java
new file mode 100644
index 0000000000000..6260f9a3fc674
--- /dev/null
+++ b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableConsumer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.flowable;
+
+import org.apache.camel.Processor;
+import org.apache.camel.support.DefaultConsumer;
+
+public class FlowableConsumer extends DefaultConsumer {
+
+ public FlowableConsumer(FlowableEndpoint endpoint, Processor processor) {
+ super(endpoint, processor);
+ }
+
+ @Override
+ protected void doStart() throws Exception {
+ super.doStart();
+ ((FlowableEndpoint) getEndpoint()).addConsumer(this);
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ super.doStop();
+ ((FlowableEndpoint) getEndpoint()).removeConsumer();
+ }
+}
diff --git a/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableEndpoint.java b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableEndpoint.java
new file mode 100644
index 0000000000000..9fa411213e0cf
--- /dev/null
+++ b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableEndpoint.java
@@ -0,0 +1,103 @@
+/*
+ * 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.flowable;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Consumer;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.support.DefaultEndpoint;
+import org.flowable.common.engine.api.FlowableException;
+import org.flowable.eventregistry.impl.EventRegistryEngineConfiguration;
+import org.flowable.eventregistry.model.CamelInboundChannelModel;
+import org.flowable.eventregistry.model.CamelOutboundChannelModel;
+
+public class FlowableEndpoint extends DefaultEndpoint {
+
+ protected EventRegistryEngineConfiguration eventRegistryEngineConfiguration;
+
+ protected FlowableConsumer flowableConsumer;
+ protected CamelInboundChannelModel camelInboundChannel;
+ protected CamelOutboundChannelModel camelOutboundChannel;
+
+ public FlowableEndpoint(CamelInboundChannelModel camelInboundChannel,
+ EventRegistryEngineConfiguration eventRegistryEngineConfiguration, CamelContext camelContext) {
+
+ super();
+ setCamelContext(camelContext);
+ this.camelInboundChannel = camelInboundChannel;
+ this.eventRegistryEngineConfiguration = eventRegistryEngineConfiguration;
+ setEndpointUri("flowable:" + camelInboundChannel.getKey());
+ }
+
+ public FlowableEndpoint(CamelOutboundChannelModel camelOutboundChannel,
+ EventRegistryEngineConfiguration eventRegistryEngineConfiguration, CamelContext camelContext) {
+
+ super();
+ setCamelContext(camelContext);
+ this.camelOutboundChannel = camelOutboundChannel;
+ this.eventRegistryEngineConfiguration = eventRegistryEngineConfiguration;
+ setEndpointUri("flowable-event:" + camelOutboundChannel.getKey());
+ }
+
+ public void process(Exchange ex) throws Exception {
+ if (flowableConsumer == null) {
+ throw new FlowableException("Consumer not defined for " + getEndpointUri());
+ }
+ flowableConsumer.getProcessor().process(ex);
+ }
+
+ @Override
+ public Producer createProducer() throws Exception {
+ FlowableProducer producer = new FlowableProducer(camelInboundChannel, this, eventRegistryEngineConfiguration);
+ return producer;
+ }
+
+ @Override
+ public Consumer createConsumer(Processor processor) throws Exception {
+ return new FlowableConsumer(this, processor);
+ }
+
+ protected void addConsumer(FlowableConsumer consumer) {
+ if (flowableConsumer != null) {
+ throw new FlowableException("Consumer already defined for " + getEndpointUri() + "!");
+ }
+ flowableConsumer = consumer;
+ }
+
+ protected void removeConsumer() {
+ flowableConsumer = null;
+ }
+
+ @Override
+ public boolean isSingleton() {
+ return true;
+ }
+
+ public void setCamelInboundChannel(CamelInboundChannelModel camelInboundChannel) {
+ this.camelInboundChannel = camelInboundChannel;
+ }
+
+ public void setCamelOutboundChannel(CamelOutboundChannelModel camelOutboundChannel) {
+ this.camelOutboundChannel = camelOutboundChannel;
+ }
+
+ public void setEventRegistryEngineConfiguration(EventRegistryEngineConfiguration eventRegistryEngineConfiguration) {
+ this.eventRegistryEngineConfiguration = eventRegistryEngineConfiguration;
+ }
+}
diff --git a/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableProducer.java b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableProducer.java
new file mode 100644
index 0000000000000..d58eff3e8ec91
--- /dev/null
+++ b/components/camel-flowable/src/main/java/org/apache/camel/component/flowable/FlowableProducer.java
@@ -0,0 +1,46 @@
+/*
+ * 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.flowable;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.support.DefaultProducer;
+import org.flowable.eventregistry.impl.DefaultInboundEvent;
+import org.flowable.eventregistry.impl.EventRegistryEngineConfiguration;
+import org.flowable.eventregistry.model.CamelInboundChannelModel;
+
+public class FlowableProducer extends DefaultProducer {
+
+ protected CamelInboundChannelModel camelInboundChannel;
+ protected EventRegistryEngineConfiguration eventRegistryEngineConfiguration;
+
+ public FlowableProducer(CamelInboundChannelModel camelInboundChannel, Endpoint endpoint,
+ EventRegistryEngineConfiguration eventRegistryEngineConfiguration) {
+
+ super(endpoint);
+ this.camelInboundChannel = camelInboundChannel;
+ this.eventRegistryEngineConfiguration = eventRegistryEngineConfiguration;
+ }
+
+ @Override
+ public void process(Exchange exchange) throws Exception {
+ Message message = exchange.getMessage();
+ DefaultInboundEvent inboundEvent = new DefaultInboundEvent(message.getBody(), message.getHeaders());
+ eventRegistryEngineConfiguration.getEventRegistry().eventReceived(camelInboundChannel, inboundEvent);
+ }
+}
diff --git a/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/CamelFlowableTestCase.java b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/CamelFlowableTestCase.java
new file mode 100644
index 0000000000000..5d0ef23657e89
--- /dev/null
+++ b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/CamelFlowableTestCase.java
@@ -0,0 +1,51 @@
+/*
+ * 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.flowable;
+
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Route;
+import org.flowable.eventregistry.api.EventDeployment;
+import org.flowable.eventregistry.impl.EventRegistryEngineConfiguration;
+import org.flowable.spring.impl.test.SpringFlowableTestCase;
+import org.junit.jupiter.api.AfterEach;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public abstract class CamelFlowableTestCase extends SpringFlowableTestCase {
+
+ @Autowired
+ protected EventRegistryEngineConfiguration eventRegistryEngineConfiguration;
+
+ @Autowired
+ protected CamelContext camelContext;
+
+ @AfterEach
+ public void tearDown() throws Exception {
+ List routes = camelContext.getRoutes();
+ for (Route r : routes) {
+ camelContext.getRouteController().stopRoute(r.getId());
+ camelContext.removeRoute(r.getId());
+ }
+
+ List eventDeployments
+ = eventRegistryEngineConfiguration.getEventRepositoryService().createDeploymentQuery().list();
+ for (EventDeployment eventDeployment : eventDeployments) {
+ eventRegistryEngineConfiguration.getEventRepositoryService().deleteDeployment(eventDeployment.getId());
+ }
+ }
+}
diff --git a/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableInboundChannelHeaderTest.java b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableInboundChannelHeaderTest.java
new file mode 100644
index 0000000000000..0e1ba0ec457cb
--- /dev/null
+++ b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableInboundChannelHeaderTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.flowable;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.camel.Exchange;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.flowable.task.api.Task;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.context.ContextConfiguration;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@ContextConfiguration("classpath:generic-camel-context.xml")
+public class FlowableInboundChannelHeaderTest extends CamelFlowableTestCase {
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ eventRegistryEngineConfiguration.getEventRepositoryService().createDeployment()
+ .addClasspathResource("channel/userHeaderChannel.channel")
+ .addClasspathResource("event/userWithHeaderEvent.event")
+ .deploy();
+
+ camelContext.addRoutes(new RouteBuilder() {
+
+ @Override
+ public void configure() throws Exception {
+ from("direct:start").to("flowable:userHeaderChannel");
+ }
+ });
+ }
+
+ @Test
+ @Deployment(resources = { "process/startWithHeader.bpmn20.xml" })
+ public void testStartProcessWithHeaderEvent() throws Exception {
+ ProducerTemplate tpl = camelContext.createProducerTemplate();
+ ObjectNode bodyNode = new ObjectMapper().createObjectNode();
+ bodyNode.put("name", "John Doe");
+ bodyNode.put("age", 23);
+ Exchange exchange = camelContext.getEndpoint("direct:start").createExchange();
+ exchange.getIn().setBody(bodyNode);
+ exchange.getIn().setHeader("headerProperty1", "headerTestValue");
+ exchange.getIn().setHeader("headerProperty2", 99);
+ tpl.send("direct:start", exchange);
+
+ ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
+ .processDefinitionKey("camelProcess")
+ .singleResult();
+ assertNotNull(processInstance);
+
+ assertEquals("John Doe", runtimeService.getVariable(processInstance.getId(), "name"));
+ assertEquals(23, runtimeService.getVariable(processInstance.getId(), "age"));
+ assertEquals("headerTestValue", runtimeService.getVariable(processInstance.getId(), "header1"));
+ assertEquals(99, runtimeService.getVariable(processInstance.getId(), "header2"));
+
+ Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
+ assertNotNull(task);
+ }
+}
diff --git a/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableInboundChannelTest.java b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableInboundChannelTest.java
new file mode 100644
index 0000000000000..cdfcb30cde630
--- /dev/null
+++ b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableInboundChannelTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.flowable;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.camel.Exchange;
+import org.apache.camel.ProducerTemplate;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.flowable.task.api.Task;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.context.ContextConfiguration;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@ContextConfiguration("classpath:generic-camel-context.xml")
+public class FlowableInboundChannelTest extends CamelFlowableTestCase {
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ eventRegistryEngineConfiguration.getEventRepositoryService().createDeployment()
+ .addClasspathResource("channel/userChannel.channel")
+ .addClasspathResource("event/userEvent.event")
+ .deploy();
+ }
+
+ @Test
+ @Deployment(resources = { "process/start.bpmn20.xml" })
+ public void testStartProcessWithBasicEvent() throws Exception {
+ ProducerTemplate tpl = camelContext.createProducerTemplate();
+ ObjectNode bodyNode = new ObjectMapper().createObjectNode();
+ bodyNode.put("name", "John Doe");
+ bodyNode.put("age", 23);
+ Exchange exchange = camelContext.getEndpoint("direct:start").createExchange();
+ exchange.getIn().setBody(bodyNode);
+ tpl.send("direct:start", exchange);
+
+ ProcessInstance processInstance
+ = runtimeService.createProcessInstanceQuery().processDefinitionKey("camelProcess").singleResult();
+ assertNotNull(processInstance);
+
+ assertEquals("John Doe", runtimeService.getVariable(processInstance.getId(), "name"));
+ assertEquals(23, runtimeService.getVariable(processInstance.getId(), "age"));
+
+ Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
+ assertNotNull(task);
+ }
+}
diff --git a/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableOutboundChannelTest.java b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableOutboundChannelTest.java
new file mode 100644
index 0000000000000..e469c9f9f00f7
--- /dev/null
+++ b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableOutboundChannelTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.flowable;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.flowable.task.api.Task;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.context.ContextConfiguration;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@ContextConfiguration("classpath:generic-camel-context.xml")
+public class FlowableOutboundChannelTest extends CamelFlowableTestCase {
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ eventRegistryEngineConfiguration.getEventRepositoryService().createDeployment()
+ .addClasspathResource("channel/userOutboundChannel.channel")
+ .addClasspathResource("event/userEvent.event")
+ .deploy();
+ }
+
+ @Test
+ @Deployment(resources = { "process/sendEvent.bpmn20.xml" })
+ public void testSendBasicEvent() throws Exception {
+ ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder().processDefinitionKey("camelProcess")
+ .variable("name", "John Doe")
+ .variable("age", 23)
+ .start();
+
+ assertEquals("John Doe", runtimeService.getVariable(processInstance.getId(), "name"));
+ assertEquals(23, runtimeService.getVariable(processInstance.getId(), "age"));
+
+ Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
+ taskService.complete(task.getId());
+
+ MockEndpoint mockEndpoint = (MockEndpoint) camelContext.getEndpoint("mock:testQueue");
+ assertEquals(1, mockEndpoint.getExchanges().size());
+ String bodyString = mockEndpoint.getExchanges().get(0).getIn().getBody(String.class);
+ JsonNode bodyNode = processEngineConfiguration.getObjectMapper().readTree(bodyString);
+ assertEquals("John Doe", bodyNode.get("name").asText());
+ assertEquals(23, bodyNode.get("age").asInt());
+ }
+}
diff --git a/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableRedeployChannelTest.java b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableRedeployChannelTest.java
new file mode 100644
index 0000000000000..ad1c2dd64c9d8
--- /dev/null
+++ b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableRedeployChannelTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.flowable;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.camel.Exchange;
+import org.apache.camel.ProducerTemplate;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.context.ContextConfiguration;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@ContextConfiguration("classpath:generic-camel-context.xml")
+public class FlowableRedeployChannelTest extends CamelFlowableTestCase {
+
+ @Test
+ @Deployment(resources = { "process/start.bpmn20.xml" })
+ public void testUnregisterChannelModel() throws Exception {
+ eventRegistryEngineConfiguration.getEventRepositoryService().createDeployment()
+ .addClasspathResource("channel/userChannel.channel")
+ .addClasspathResource("event/userEvent.event")
+ .deploy();
+
+ ProducerTemplate tpl = camelContext.createProducerTemplate();
+ ObjectNode bodyNode = new ObjectMapper().createObjectNode();
+ bodyNode.put("name", "John Doe");
+ bodyNode.put("age", 23);
+ Exchange exchange = camelContext.getEndpoint("direct:start").createExchange();
+ exchange.getIn().setBody(bodyNode);
+ tpl.send("direct:start", exchange);
+
+ ProcessInstance processInstance
+ = runtimeService.createProcessInstanceQuery().processDefinitionKey("camelProcess").singleResult();
+ assertNotNull(processInstance);
+
+ assertEquals("John Doe", runtimeService.getVariable(processInstance.getId(), "name"));
+ assertEquals(23, runtimeService.getVariable(processInstance.getId(), "age"));
+
+ eventRegistryEngineConfiguration.getEventRepositoryService().createDeployment()
+ .addClasspathResource("channel/userChannelUpdated.channel")
+ .addClasspathResource("event/userEventUpdated.event")
+ .deploy();
+
+ bodyNode = new ObjectMapper().createObjectNode();
+ bodyNode.put("name", "Jane Doe");
+ bodyNode.put("age", 28);
+ bodyNode.put("address", "Main street 1");
+ assertEquals(null, camelContext.hasEndpoint("direct:start"));
+
+ assertEquals(1, runtimeService.createProcessInstanceQuery().processDefinitionKey("camelProcess").count());
+
+ exchange = camelContext.getEndpoint("direct:startUpdated").createExchange();
+ exchange.getIn().setBody(bodyNode);
+ tpl.send("direct:startUpdated", exchange);
+
+ assertEquals(2, runtimeService.createProcessInstanceQuery().processDefinitionKey("camelProcess").count());
+ }
+}
diff --git a/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableSendAndReceiveChannelTest.java b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableSendAndReceiveChannelTest.java
new file mode 100644
index 0000000000000..d61a0e17f7103
--- /dev/null
+++ b/components/camel-flowable/src/test/java/org/apache/camel/component/flowable/FlowableSendAndReceiveChannelTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.flowable;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.camel.Exchange;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.flowable.task.api.Task;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.context.ContextConfiguration;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@ContextConfiguration("classpath:generic-camel-context.xml")
+public class FlowableSendAndReceiveChannelTest extends CamelFlowableTestCase {
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ eventRegistryEngineConfiguration.getEventRepositoryService().createDeployment()
+ .addClasspathResource("channel/userOutboundChannel.channel")
+ .addClasspathResource("event/userEvent.event")
+ .addClasspathResource("channel/userInboundChannel.channel")
+ .addClasspathResource("event/userInboundEvent.event")
+ .deploy();
+
+ camelContext.addRoutes(new RouteBuilder() {
+
+ @Override
+ public void configure() throws Exception {
+ from("direct:start").to("flowable:userInboundChannel");
+ }
+ });
+ }
+
+ @Test
+ @Deployment(resources = { "process/sendAndReceiveEvent.bpmn20.xml" })
+ public void testSendAndReceiveBasicEvent() throws Exception {
+ ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder().processDefinitionKey("camelProcess")
+ .variable("name", "John Doe")
+ .variable("age", 23)
+ .start();
+
+ assertEquals("John Doe", runtimeService.getVariable(processInstance.getId(), "name"));
+ assertEquals(23, runtimeService.getVariable(processInstance.getId(), "age"));
+
+ Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
+ taskService.complete(task.getId());
+
+ MockEndpoint mockEndpoint = (MockEndpoint) camelContext.getEndpoint("mock:testQueue");
+ assertEquals(1, mockEndpoint.getExchanges().size());
+ String bodyString = mockEndpoint.getExchanges().get(0).getIn().getBody(String.class);
+ JsonNode bodyNode = processEngineConfiguration.getObjectMapper().readTree(bodyString);
+ assertEquals("John Doe", bodyNode.get("name").asText());
+ assertEquals(23, bodyNode.get("age").asInt());
+
+ ProducerTemplate tpl = camelContext.createProducerTemplate();
+ ObjectNode sendBodyNode = new ObjectMapper().createObjectNode();
+ sendBodyNode.put("name", "John Doe");
+ sendBodyNode.put("city", "Amsterdam");
+ Exchange exchange = camelContext.getEndpoint("direct:start").createExchange();
+ exchange.getIn().setBody(sendBodyNode);
+ tpl.send("direct:start", exchange);
+
+ assertEquals("John Doe", runtimeService.getVariable(processInstance.getId(), "correlationName"));
+ assertEquals("Amsterdam", runtimeService.getVariable(processInstance.getId(), "city"));
+
+ task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
+ assertEquals("userTask2", task.getTaskDefinitionKey());
+ taskService.complete(task.getId());
+
+ assertEquals(0, runtimeService.createProcessInstanceQuery().processInstanceId(processInstance.getId()).count());
+ }
+}
diff --git a/components/camel-flowable/src/test/resources/channel/userChannel.channel b/components/camel-flowable/src/test/resources/channel/userChannel.channel
new file mode 100644
index 0000000000000..793c6e499f6a4
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/channel/userChannel.channel
@@ -0,0 +1,11 @@
+{
+ "name": "Camel user channel",
+ "key": "userChannel",
+ "channelType": "inbound",
+ "type": "camel",
+ "sourceUri": "direct:start",
+ "deserializerType": "json",
+ "channelEventKeyDetection": {
+ "fixedValue": "userEvent"
+ }
+}
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/channel/userChannelUpdated.channel b/components/camel-flowable/src/test/resources/channel/userChannelUpdated.channel
new file mode 100644
index 0000000000000..10cccf947b46c
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/channel/userChannelUpdated.channel
@@ -0,0 +1,11 @@
+{
+ "name": "Camel user channel",
+ "key": "userChannel",
+ "channelType": "inbound",
+ "type": "camel",
+ "sourceUri": "direct:startUpdated",
+ "deserializerType": "json",
+ "channelEventKeyDetection": {
+ "fixedValue": "userEvent"
+ }
+}
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/channel/userHeaderChannel.channel b/components/camel-flowable/src/test/resources/channel/userHeaderChannel.channel
new file mode 100644
index 0000000000000..b04e71c1d6849
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/channel/userHeaderChannel.channel
@@ -0,0 +1,10 @@
+{
+ "name": "Camel user channel",
+ "key": "userHeaderChannel",
+ "channelType": "inbound",
+ "type": "camel",
+ "deserializerType": "json",
+ "channelEventKeyDetection": {
+ "fixedValue": "userHeaderEvent"
+ }
+}
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/channel/userInboundChannel.channel b/components/camel-flowable/src/test/resources/channel/userInboundChannel.channel
new file mode 100644
index 0000000000000..958a4ba932620
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/channel/userInboundChannel.channel
@@ -0,0 +1,10 @@
+{
+ "name": "Camel user inbound channel",
+ "key": "userInboundChannel",
+ "channelType": "inbound",
+ "type": "camel",
+ "deserializerType": "json",
+ "channelEventKeyDetection": {
+ "fixedValue": "userInboundEvent"
+ }
+}
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/channel/userOutboundChannel.channel b/components/camel-flowable/src/test/resources/channel/userOutboundChannel.channel
new file mode 100644
index 0000000000000..514c9a51f95a3
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/channel/userOutboundChannel.channel
@@ -0,0 +1,8 @@
+{
+ "name": "Camel outbound user channel",
+ "key": "userOutboundChannel",
+ "channelType": "outbound",
+ "type": "camel",
+ "destination": "mock:testQueue",
+ "serializerType": "json"
+}
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/event/userEvent.event b/components/camel-flowable/src/test/resources/event/userEvent.event
new file mode 100644
index 0000000000000..685b69f3844bc
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/event/userEvent.event
@@ -0,0 +1,14 @@
+{
+ "key": "userEvent",
+ "name": "User Event",
+ "payload": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "age",
+ "type": "integer"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/event/userEventUpdated.event b/components/camel-flowable/src/test/resources/event/userEventUpdated.event
new file mode 100644
index 0000000000000..8e546d5119148
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/event/userEventUpdated.event
@@ -0,0 +1,18 @@
+{
+ "key": "userEvent",
+ "name": "User Event",
+ "payload": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "age",
+ "type": "integer"
+ },
+ {
+ "name": "address",
+ "type": "string"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/event/userInboundEvent.event b/components/camel-flowable/src/test/resources/event/userInboundEvent.event
new file mode 100644
index 0000000000000..b6b54bfefd307
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/event/userInboundEvent.event
@@ -0,0 +1,15 @@
+{
+ "key": "userInboundEvent",
+ "name": "User Inbound Event",
+ "payload": [
+ {
+ "name": "name",
+ "correlationParameter":true,
+ "type": "string"
+ },
+ {
+ "name": "city",
+ "type": "string"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/event/userWithHeaderEvent.event b/components/camel-flowable/src/test/resources/event/userWithHeaderEvent.event
new file mode 100644
index 0000000000000..14620f806b8d6
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/event/userWithHeaderEvent.event
@@ -0,0 +1,24 @@
+{
+ "key": "userHeaderEvent",
+ "name": "User Header Event",
+ "payload": [
+ {
+ "name": "headerProperty1",
+ "type": "string",
+ "header": true
+ },
+ {
+ "name": "headerProperty2",
+ "type": "integer",
+ "header": true
+ },
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "age",
+ "type": "integer"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/generic-camel-context.xml b/components/camel-flowable/src/test/resources/generic-camel-context.xml
new file mode 100644
index 0000000000000..8accdeccd06d6
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/generic-camel-context.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/log4j2.properties b/components/camel-flowable/src/test/resources/log4j2.properties
new file mode 100644
index 0000000000000..f9f75591e5724
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/log4j2.properties
@@ -0,0 +1,28 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+appender.console.type = Console
+appender.console.name = console
+appender.console.layout.type = PatternLayout
+appender.console.layout.pattern = %d %-5p %c{1} - %m %n
+appender.file.type = File
+appender.file.name = file
+appender.file.fileName = target/camel-flowable-test.log
+appender.file.layout.type = PatternLayout
+appender.file.layout.pattern = %d %-5p %c{1} - %m %n
+rootLogger.level = info
+rootLogger.appenderRef.file.ref = console
diff --git a/components/camel-flowable/src/test/resources/process/sendAndReceiveEvent.bpmn20.xml b/components/camel-flowable/src/test/resources/process/sendAndReceiveEvent.bpmn20.xml
new file mode 100644
index 0000000000000..4391bd89fc13f
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/process/sendAndReceiveEvent.bpmn20.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/process/sendEvent.bpmn20.xml b/components/camel-flowable/src/test/resources/process/sendEvent.bpmn20.xml
new file mode 100644
index 0000000000000..bf4e153ac5673
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/process/sendEvent.bpmn20.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/process/start.bpmn20.xml b/components/camel-flowable/src/test/resources/process/start.bpmn20.xml
new file mode 100644
index 0000000000000..58f11b963c9ae
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/process/start.bpmn20.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+ userEvent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/camel-flowable/src/test/resources/process/startWithHeader.bpmn20.xml b/components/camel-flowable/src/test/resources/process/startWithHeader.bpmn20.xml
new file mode 100644
index 0000000000000..87a00f1792632
--- /dev/null
+++ b/components/camel-flowable/src/test/resources/process/startWithHeader.bpmn20.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ userHeaderEvent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/pom.xml b/components/pom.xml
index 823dd239e60e4..e865a52f2a49d 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -134,6 +134,7 @@
camel-file-watch
camel-flatpack
camel-flink
+ camel-flowable
camel-fop
camel-freemarker
camel-ftp
diff --git a/docs/components/modules/ROOT/nav.adoc b/docs/components/modules/ROOT/nav.adoc
index d95c26c9b494e..248066e2c8bf0 100644
--- a/docs/components/modules/ROOT/nav.adoc
+++ b/docs/components/modules/ROOT/nav.adoc
@@ -116,6 +116,7 @@
** xref:file-watch-component.adoc[File Watch]
** xref:flatpack-component.adoc[Flatpack]
** xref:flink-component.adoc[Flink]
+** xref:flowable-component.adoc[Flowable]
** xref:fop-component.adoc[FOP]
** xref:freemarker-component.adoc[Freemarker]
** xref:ftp-component.adoc[FTP]
diff --git a/docs/components/modules/ROOT/pages/flowable-component.adoc b/docs/components/modules/ROOT/pages/flowable-component.adoc
new file mode 120000
index 0000000000000..b164c17ef43eb
--- /dev/null
+++ b/docs/components/modules/ROOT/pages/flowable-component.adoc
@@ -0,0 +1 @@
+../../../../../components/camel-flowable/src/main/docs/flowable-component.adoc
\ No newline at end of file
diff --git a/parent/pom.xml b/parent/pom.xml
index d0218ad7905d6..605c618db87ff 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -76,7 +76,7 @@
1.12.0
1.12.0
4.2.2
- 2.28.18
+ 2.28.19
2.18.2
1.2.28
12.0.0-beta.25
@@ -153,6 +153,7 @@
2.2.0
4.0.18
1.20.0
+ 7.1.0
2.10
2.24.1
1.2.0
@@ -203,6 +204,7 @@
2.11.0
33.3.1-jre
3.0
+ 2.2.224
3.4.0
3.0
2.5.1
@@ -212,6 +214,7 @@
2.2.2
8.0.1.Final
6.3.2.Final
+ 5.0.1
2.6.1
2.7.3
1.7
@@ -304,6 +307,7 @@
1.18.1
20.0.7
3.1.13
+ 4.13.2
2.4
5.11.2
2.3.0
@@ -1300,6 +1304,11 @@
camel-flink
${project.version}
+
+ org.apache.camel
+ camel-flowable
+ ${project.version}
+
org.apache.camel
camel-fop
diff --git a/pom.xml b/pom.xml
index 1a7e58fc581c1..a7e6697b242fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -253,8 +253,10 @@
**/*.bin
**/*.cat
**/*.cer
+ **/*.channel
**/*.chtml
**/*.csv
+ **/*.event
**/*.gif
**/*.gpg
**/*.graphql*