This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 3da7cd13e050 CAMEL-23173: Add camel-event component for subscribing to
Camel events (#21983)
3da7cd13e050 is described below
commit 3da7cd13e050ac651d49dbb88960176ef0842c21
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Mar 16 06:48:59 2026 +0100
CAMEL-23173: Add camel-event component for subscribing to Camel events
(#21983)
* CAMEL-23173: Add camel-event component for subscribing to Camel events
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* CAMEL-23173: Add distributed events documentation section
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* CAMEL-23173: Rename event component package to avoid conflict with
spring-event
The existing spring-event component uses org.apache.camel.component.event,
causing a build conflict. Rename to org.apache.camel.component.camelevent.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* Regenerate DSL files after package rename
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* Regenerate all generated files after package rename
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* Fix flaky docs gulp build by excluding target directories from glob
scanning
The gulp `symlink:asciidoc:others` task uses a `**` glob pattern to scan
DSL module directories. During parallel CI builds, temp directories under
`target/` (e.g. `templates-tmp87842628`) can appear and disappear while
the glob is scanning, causing ENOENT race conditions.
Exclude `**/target/**` from `gulp.src()` calls in both `createSymlinks`
and `createExampleSymlinks` functions, since build output directories
should never contain documentation source files.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
---
bom/camel-bom/pom.xml | 5 +
catalog/camel-allcomponents/pom.xml | 5 +
.../org/apache/camel/catalog/components.properties | 1 +
.../org/apache/camel/catalog/components/event.json | 37 +++
components/camel-event/pom.xml | 49 ++++
.../camelevent/EventComponentConfigurer.java | 57 +++++
.../camelevent/EventEndpointConfigurer.java | 66 ++++++
.../camelevent/EventEndpointUriFactory.java | 74 ++++++
.../apache/camel/component/camelevent/event.json | 37 +++
.../services/org/apache/camel/component.properties | 7 +
.../services/org/apache/camel/component/event | 2 +
.../org/apache/camel/configurer/event-component | 2 +
.../org/apache/camel/configurer/event-endpoint | 2 +
.../org/apache/camel/urifactory/event-endpoint | 2 +
.../camel-event/src/main/docs/event-component.adoc | 171 ++++++++++++++
.../camel/component/camelevent/EventComponent.java | 39 ++++
.../camel/component/camelevent/EventConsumer.java | 259 +++++++++++++++++++++
.../camel/component/camelevent/EventEndpoint.java | 157 +++++++++++++
.../component/camelevent/EventComponentTest.java | 176 ++++++++++++++
components/pom.xml | 1 +
.../org/apache/camel/main/components.properties | 1 +
.../modules/ROOT/examples/json/event.json | 1 +
docs/components/modules/ROOT/nav.adoc | 1 +
.../modules/ROOT/pages/event-component.adoc | 1 +
docs/gulpfile.js | 4 +-
.../component/ComponentsBuilderFactory.java | 14 ++
.../dsl/EventComponentBuilderFactory.java | 125 ++++++++++
.../builder/endpoint/StaticEndpointBuilders.java | 46 ++++
.../camel-component-known-dependencies.properties | 1 +
parent/pom.xml | 5 +
30 files changed, 1346 insertions(+), 2 deletions(-)
diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index 8a873be29d48..1bcc50338c79 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -832,6 +832,11 @@
<artifactId>camel-endpointdsl-support</artifactId>
<version>4.19.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-event</artifactId>
+ <version>4.19.0-SNAPSHOT</version>
+ </dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-exec</artifactId>
diff --git a/catalog/camel-allcomponents/pom.xml
b/catalog/camel-allcomponents/pom.xml
index 2974d4124386..f8080d5e1246 100644
--- a/catalog/camel-allcomponents/pom.xml
+++ b/catalog/camel-allcomponents/pom.xml
@@ -702,6 +702,11 @@
<artifactId>camel-elytron</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-event</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-exec</artifactId>
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties
index a88d76a89dd5..60ab2e4e8a74 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components.properties
@@ -109,6 +109,7 @@ dynamic-router-control
ehcache
elasticsearch
elasticsearch-rest-client
+event
exec
fhir
file
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/event.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/event.json
new file mode 100644
index 000000000000..abed8d130c0c
--- /dev/null
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/event.json
@@ -0,0 +1,37 @@
+{
+ "component": {
+ "kind": "component",
+ "name": "event",
+ "title": "Event",
+ "description": "Subscribe to Camel internal events such as route
started\/stopped and exchange completed\/failed.",
+ "deprecated": false,
+ "firstVersion": "4.19.0",
+ "label": "core,monitoring",
+ "javaType": "org.apache.camel.component.camelevent.EventComponent",
+ "supportLevel": "Preview",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-event",
+ "version": "4.19.0-SNAPSHOT",
+ "scheme": "event",
+ "extendsScheme": "",
+ "syntax": "event:events",
+ "async": false,
+ "api": false,
+ "consumerOnly": true,
+ "producerOnly": false,
+ "lenientProperties": false,
+ "browsable": false,
+ "remote": false
+ },
+ "componentProperties": {
+ "bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Allows for bridging the consumer to the Camel routing Error Handler, which
mean any exceptions (if possible) occurred while the Camel consumer is trying
to pickup incoming messages, or the like [...]
+ "autowiredEnabled": { "index": 1, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching t [...]
+ },
+ "properties": {
+ "events": { "index": 0, "kind": "path", "displayName": "Events", "group":
"consumer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Comma-separated list of event types to
subscribe to. Event types correspond to CamelEvent.Type enum values
(case-insensitive), for example: RouteStarted, RouteStopped, ExchangeCompleted,
ExchangeFailed." },
+ "filter": { "index": 1, "kind": "parameter", "displayName": "Filter",
"group": "consumer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "Comma-separated list of filters to narrow down
events. For route events, this filters by route ID. For exchange events, this
filters by the route ID of the exchange." },
+ "bridgeErrorHandler": { "index": 2, "kind": "parameter", "displayName":
"Bridge Error Handler", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Allows for bridging the consumer to the
Camel routing Error Handler, which mean any exceptions (if possible) occurred
while the Camel consumer is trying to pickup incoming [...]
+ "exceptionHandler": { "index": 3, "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, "autowired": 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 def [...]
+ "exchangePattern": { "index": 4, "kind": "parameter", "displayName":
"Exchange Pattern", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "enum", "javaType":
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ],
"deprecated": false, "autowired": false, "secret": false, "description": "Sets
the exchange pattern when the consumer creates an exchange." }
+ }
+}
diff --git a/components/camel-event/pom.xml b/components/camel-event/pom.xml
new file mode 100644
index 000000000000..7c2b44f1fd06
--- /dev/null
+++ b/components/camel-event/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>components</artifactId>
+ <version>4.19.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-event</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Camel :: Event</name>
+ <description>Camel Event component for subscribing to Camel internal
events</description>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-support</artifactId>
+ </dependency>
+
+ <!-- testing -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-junit5</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git
a/components/camel-event/src/generated/java/org/apache/camel/component/camelevent/EventComponentConfigurer.java
b/components/camel-event/src/generated/java/org/apache/camel/component/camelevent/EventComponentConfigurer.java
new file mode 100644
index 000000000000..32ef19838423
--- /dev/null
+++
b/components/camel-event/src/generated/java/org/apache/camel/component/camelevent/EventComponentConfigurer.java
@@ -0,0 +1,57 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.camelevent;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo")
+@SuppressWarnings("unchecked")
+public class EventComponentConfigurer extends PropertyConfigurerSupport
implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+ @Override
+ public boolean configure(CamelContext camelContext, Object obj, String
name, Object value, boolean ignoreCase) {
+ EventComponent target = (EventComponent) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "autowiredenabled":
+ case "autowiredEnabled":
target.setAutowiredEnabled(property(camelContext, boolean.class, value));
return true;
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler":
target.setBridgeErrorHandler(property(camelContext, boolean.class, value));
return true;
+ default: return false;
+ }
+ }
+
+ @Override
+ public Class<?> getOptionType(String name, boolean ignoreCase) {
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "autowiredenabled":
+ case "autowiredEnabled": return boolean.class;
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler": return boolean.class;
+ default: return null;
+ }
+ }
+
+ @Override
+ public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+ EventComponent target = (EventComponent) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "autowiredenabled":
+ case "autowiredEnabled": return target.isAutowiredEnabled();
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler": return target.isBridgeErrorHandler();
+ default: return null;
+ }
+ }
+}
+
diff --git
a/components/camel-event/src/generated/java/org/apache/camel/component/camelevent/EventEndpointConfigurer.java
b/components/camel-event/src/generated/java/org/apache/camel/component/camelevent/EventEndpointConfigurer.java
new file mode 100644
index 000000000000..c24a00316051
--- /dev/null
+++
b/components/camel-event/src/generated/java/org/apache/camel/component/camelevent/EventEndpointConfigurer.java
@@ -0,0 +1,66 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.camelevent;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.support.component.PropertyConfigurerSupport;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo")
+@SuppressWarnings("unchecked")
+public class EventEndpointConfigurer extends PropertyConfigurerSupport
implements GeneratedPropertyConfigurer, PropertyConfigurerGetter {
+
+ @Override
+ public boolean configure(CamelContext camelContext, Object obj, String
name, Object value, boolean ignoreCase) {
+ EventEndpoint target = (EventEndpoint) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ 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 "filter": target.setFilter(property(camelContext,
java.lang.String.class, value)); return true;
+ default: return false;
+ }
+ }
+
+ @Override
+ public Class<?> getOptionType(String name, boolean ignoreCase) {
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler": return boolean.class;
+ case "exceptionhandler":
+ case "exceptionHandler": return
org.apache.camel.spi.ExceptionHandler.class;
+ case "exchangepattern":
+ case "exchangePattern": return org.apache.camel.ExchangePattern.class;
+ case "filter": return java.lang.String.class;
+ default: return null;
+ }
+ }
+
+ @Override
+ public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+ EventEndpoint target = (EventEndpoint) obj;
+ switch (ignoreCase ? name.toLowerCase() : name) {
+ case "bridgeerrorhandler":
+ case "bridgeErrorHandler": return target.isBridgeErrorHandler();
+ case "exceptionhandler":
+ case "exceptionHandler": return target.getExceptionHandler();
+ case "exchangepattern":
+ case "exchangePattern": return target.getExchangePattern();
+ case "filter": return target.getFilter();
+ default: return null;
+ }
+ }
+}
+
diff --git
a/components/camel-event/src/generated/java/org/apache/camel/component/camelevent/EventEndpointUriFactory.java
b/components/camel-event/src/generated/java/org/apache/camel/component/camelevent/EventEndpointUriFactory.java
new file mode 100644
index 000000000000..9154e8d456f1
--- /dev/null
+++
b/components/camel-event/src/generated/java/org/apache/camel/component/camelevent/EventEndpointUriFactory.java
@@ -0,0 +1,74 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.camelevent;
+
+import javax.annotation.processing.Generated;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.spi.EndpointUriFactory;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.GenerateEndpointUriFactoryMojo")
+public class EventEndpointUriFactory extends
org.apache.camel.support.component.EndpointUriFactorySupport implements
EndpointUriFactory {
+
+ private static final String BASE = ":events";
+
+ private static final Set<String> PROPERTY_NAMES;
+ private static final Set<String> SECRET_PROPERTY_NAMES;
+ private static final Map<String, String> MULTI_VALUE_PREFIXES;
+ static {
+ Set<String> props = new HashSet<>(5);
+ props.add("bridgeErrorHandler");
+ props.add("events");
+ props.add("exceptionHandler");
+ props.add("exchangePattern");
+ props.add("filter");
+ PROPERTY_NAMES = Collections.unmodifiableSet(props);
+ SECRET_PROPERTY_NAMES = Collections.emptySet();
+ MULTI_VALUE_PREFIXES = Collections.emptyMap();
+ }
+
+ @Override
+ public boolean isEnabled(String scheme) {
+ return "event".equals(scheme);
+ }
+
+ @Override
+ public String buildUri(String scheme, Map<String, Object> properties,
boolean encode) throws URISyntaxException {
+ String syntax = scheme + BASE;
+ String uri = syntax;
+
+ Map<String, Object> copy = new HashMap<>(properties);
+
+ uri = buildPathParameter(syntax, uri, "events", null, true, copy);
+ uri = buildQueryParameters(uri, copy, encode);
+ return uri;
+ }
+
+ @Override
+ public Set<String> propertyNames() {
+ return PROPERTY_NAMES;
+ }
+
+ @Override
+ public Set<String> secretPropertyNames() {
+ return SECRET_PROPERTY_NAMES;
+ }
+
+ @Override
+ public Map<String, String> multiValuePrefixes() {
+ return MULTI_VALUE_PREFIXES;
+ }
+
+ @Override
+ public boolean isLenientProperties() {
+ return false;
+ }
+}
+
diff --git
a/components/camel-event/src/generated/resources/META-INF/org/apache/camel/component/camelevent/event.json
b/components/camel-event/src/generated/resources/META-INF/org/apache/camel/component/camelevent/event.json
new file mode 100644
index 000000000000..abed8d130c0c
--- /dev/null
+++
b/components/camel-event/src/generated/resources/META-INF/org/apache/camel/component/camelevent/event.json
@@ -0,0 +1,37 @@
+{
+ "component": {
+ "kind": "component",
+ "name": "event",
+ "title": "Event",
+ "description": "Subscribe to Camel internal events such as route
started\/stopped and exchange completed\/failed.",
+ "deprecated": false,
+ "firstVersion": "4.19.0",
+ "label": "core,monitoring",
+ "javaType": "org.apache.camel.component.camelevent.EventComponent",
+ "supportLevel": "Preview",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-event",
+ "version": "4.19.0-SNAPSHOT",
+ "scheme": "event",
+ "extendsScheme": "",
+ "syntax": "event:events",
+ "async": false,
+ "api": false,
+ "consumerOnly": true,
+ "producerOnly": false,
+ "lenientProperties": false,
+ "browsable": false,
+ "remote": false
+ },
+ "componentProperties": {
+ "bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Allows for bridging the consumer to the Camel routing Error Handler, which
mean any exceptions (if possible) occurred while the Camel consumer is trying
to pickup incoming messages, or the like [...]
+ "autowiredEnabled": { "index": 1, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching t [...]
+ },
+ "properties": {
+ "events": { "index": 0, "kind": "path", "displayName": "Events", "group":
"consumer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "Comma-separated list of event types to
subscribe to. Event types correspond to CamelEvent.Type enum values
(case-insensitive), for example: RouteStarted, RouteStopped, ExchangeCompleted,
ExchangeFailed." },
+ "filter": { "index": 1, "kind": "parameter", "displayName": "Filter",
"group": "consumer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "Comma-separated list of filters to narrow down
events. For route events, this filters by route ID. For exchange events, this
filters by the route ID of the exchange." },
+ "bridgeErrorHandler": { "index": 2, "kind": "parameter", "displayName":
"Bridge Error Handler", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Allows for bridging the consumer to the
Camel routing Error Handler, which mean any exceptions (if possible) occurred
while the Camel consumer is trying to pickup incoming [...]
+ "exceptionHandler": { "index": 3, "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, "autowired": 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 def [...]
+ "exchangePattern": { "index": 4, "kind": "parameter", "displayName":
"Exchange Pattern", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "enum", "javaType":
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ],
"deprecated": false, "autowired": false, "secret": false, "description": "Sets
the exchange pattern when the consumer creates an exchange." }
+ }
+}
diff --git
a/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/component.properties
b/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/component.properties
new file mode 100644
index 000000000000..9e42b877a346
--- /dev/null
+++
b/components/camel-event/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=event
+groupId=org.apache.camel
+artifactId=camel-event
+version=4.19.0-SNAPSHOT
+projectName=Camel :: Event
+projectDescription=Camel Event component for subscribing to Camel internal
events
diff --git
a/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/component/event
b/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/component/event
new file mode 100644
index 000000000000..cc244fdefe8d
--- /dev/null
+++
b/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/component/event
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.camelevent.EventComponent
diff --git
a/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/configurer/event-component
b/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/configurer/event-component
new file mode 100644
index 000000000000..378de5b36791
--- /dev/null
+++
b/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/configurer/event-component
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.camelevent.EventComponentConfigurer
diff --git
a/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/configurer/event-endpoint
b/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/configurer/event-endpoint
new file mode 100644
index 000000000000..49a7f6655c4e
--- /dev/null
+++
b/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/configurer/event-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.camelevent.EventEndpointConfigurer
diff --git
a/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/urifactory/event-endpoint
b/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/urifactory/event-endpoint
new file mode 100644
index 000000000000..c614cd9e3af2
--- /dev/null
+++
b/components/camel-event/src/generated/resources/META-INF/services/org/apache/camel/urifactory/event-endpoint
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.component.camelevent.EventEndpointUriFactory
diff --git a/components/camel-event/src/main/docs/event-component.adoc
b/components/camel-event/src/main/docs/event-component.adoc
new file mode 100644
index 000000000000..9f914c50a6ee
--- /dev/null
+++ b/components/camel-event/src/main/docs/event-component.adoc
@@ -0,0 +1,171 @@
+= Event Component
+:doctitle: Event
+:shortname: event
+:artifactid: camel-event
+:description: Subscribe to Camel internal events such as route started/stopped
and exchange completed/failed.
+:since: 4.19
+:supportlevel: Preview
+:tabs-sync-option:
+:component-header: Only consumer is supported
+:core:
+//Manually maintained attributes
+:camel-spring-boot-name: event
+
+*Since Camel {since}*
+
+*{component-header}*
+
+The Event component allows you to subscribe to Camel internal events such as
route lifecycle events
+and exchange events. The component leverages Camel's existing `EventNotifier`
mechanism to receive events
+and dispatch them as exchanges into a route.
+
+== URI format
+
+----
+event:eventTypes[?options]
+----
+
+Where `eventTypes` is a comma-separated list of event types to subscribe to.
+Event types correspond to `CamelEvent.Type` enum values (case-insensitive).
+
+For example:
+
+----
+event:RouteStarted,RouteStopped
+event:ExchangeCompleted,ExchangeFailed?filter=myRoute
+----
+
+// component-configure options: START
+// component-configure options: END
+
+// component options: START
+include::partial$component-configure-options.adoc[]
+include::partial$component-endpoint-options.adoc[]
+include::partial$component-endpoint-headers.adoc[]
+// component options: END
+
+// endpoint options: START
+// endpoint options: END
+
+== Event Types
+
+The following event types are available (corresponding to
`org.apache.camel.spi.CamelEvent.Type`):
+
+=== CamelContext Events
+- `CamelContextInitializing`
+- `CamelContextInitialized`
+- `CamelContextStarting`
+- `CamelContextStarted`
+- `CamelContextStopping`
+- `CamelContextStopped`
+- `CamelContextResuming`
+- `CamelContextResumed`
+- `CamelContextSuspending`
+- `CamelContextSuspended`
+- `CamelContextReloading`
+- `CamelContextReloaded`
+- `CamelContextStartupFailure`
+- `CamelContextStopFailure`
+- `CamelContextResumeFailure`
+- `CamelContextReloadFailure`
+
+=== Route Events
+- `RouteAdded`
+- `RouteRemoved`
+- `RouteStarting`
+- `RouteStarted`
+- `RouteStopping`
+- `RouteStopped`
+- `RouteReloaded`
+- `RouteRestarting`
+- `RouteRestartingFailure`
+- `RoutesStarting`
+- `RoutesStarted`
+- `RoutesStopping`
+- `RoutesStopped`
+
+=== Exchange Events
+- `ExchangeCreated`
+- `ExchangeCompleted`
+- `ExchangeFailed`
+- `ExchangeFailureHandling`
+- `ExchangeFailureHandled`
+- `ExchangeRedelivery`
+- `ExchangeSending`
+- `ExchangeSent`
+
+=== Step Events
+- `StepStarted`
+- `StepCompleted`
+- `StepFailed`
+
+=== Service Events
+- `ServiceStartupFailure`
+- `ServiceStopFailure`
+
+== Filtering
+
+You can filter events using the `filter` option. For route events, this
filters by route ID.
+For exchange events, this filters by the route ID of the exchange (from route).
+
+For example, to only receive events for routes `myRoute1` and `myRoute2`:
+
+----
+event:RouteStarted,RouteStopped?filter=myRoute1,myRoute2
+----
+
+== Message Headers
+
+The following headers are set on exchanges created by this component:
+
+[width="100%",cols="10%,10%,80%",options="header",]
+|===
+| Header | Type | Description
+| CamelEventType | String | The event type name (e.g., `RouteStarted`)
+| CamelEventTimestamp | Long | The event timestamp (if available)
+| CamelEventRouteId | String | The route ID (for route events only)
+|===
+
+== Message Body
+
+The message body contains the `CamelEvent` object, which can be cast to the
specific event
+sub-interface for additional details.
+
+== Examples
+
+Subscribe to route started and stopped events:
+
+[source,java]
+----
+from("event:RouteStarted,RouteStopped")
+ .log("Route ${header.CamelEventRouteId} event: ${header.CamelEventType}");
+----
+
+Subscribe to exchange completed events for a specific route:
+
+[source,java]
+----
+from("event:ExchangeCompleted?filter=myRoute")
+ .log("Exchange completed on route myRoute");
+----
+
+== Distributed Events
+
+The Event component captures events within a single CamelContext (JVM). To
propagate events across
+multiple Camel instances, use standard Camel routes to forward events to a
messaging system:
+
+[source,java]
+----
+// Producer side: capture local events and publish to Kafka
+from("event:RouteStarted,RouteStopped,ExchangeFailed")
+ .setBody(simple("${header.CamelEventType}: ${header.CamelEventRouteId} at
${header.CamelEventTimestamp}"))
+ .to("kafka:camel-events");
+
+// Consumer side (same or different JVM): process distributed events
+from("kafka:camel-events")
+ .to("log:distributed-events");
+----
+
+This approach is transport-agnostic — you can use Kafka, JMS, AMQP, NATS, or
any other
+Camel messaging component. It gives you full control over event serialization,
filtering,
+and routing logic.
diff --git
a/components/camel-event/src/main/java/org/apache/camel/component/camelevent/EventComponent.java
b/components/camel-event/src/main/java/org/apache/camel/component/camelevent/EventComponent.java
new file mode 100644
index 000000000000..7190293ef5f4
--- /dev/null
+++
b/components/camel-event/src/main/java/org/apache/camel/component/camelevent/EventComponent.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.camelevent;
+
+import java.util.Map;
+
+import org.apache.camel.Endpoint;
+import org.apache.camel.support.DefaultComponent;
+
+/**
+ * The Event component allows subscribing to Camel internal events such as
route events and exchange events.
+ */
[email protected]("event")
+public class EventComponent extends DefaultComponent {
+
+ public EventComponent() {
+ }
+
+ @Override
+ protected Endpoint createEndpoint(String uri, String remaining,
Map<String, Object> parameters) throws Exception {
+ EventEndpoint endpoint = new EventEndpoint(uri, this, remaining);
+ setProperties(endpoint, parameters);
+ return endpoint;
+ }
+}
diff --git
a/components/camel-event/src/main/java/org/apache/camel/component/camelevent/EventConsumer.java
b/components/camel-event/src/main/java/org/apache/camel/component/camelevent/EventConsumer.java
new file mode 100644
index 000000000000..20b934772c1e
--- /dev/null
+++
b/components/camel-event/src/main/java/org/apache/camel/component/camelevent/EventConsumer.java
@@ -0,0 +1,259 @@
+/*
+ * 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.camelevent;
+
+import java.util.Set;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.Route;
+import org.apache.camel.spi.CamelEvent;
+import org.apache.camel.support.DefaultConsumer;
+import org.apache.camel.support.EventNotifierSupport;
+
+/**
+ * Consumer that registers as an {@link org.apache.camel.spi.EventNotifier} to
receive Camel internal events and
+ * dispatches them as exchanges to the route.
+ */
+public class EventConsumer extends DefaultConsumer {
+
+ private static final ThreadLocal<Boolean> PROCESSING =
ThreadLocal.withInitial(() -> Boolean.FALSE);
+
+ private final EventNotifierSupport eventNotifier;
+
+ public EventConsumer(EventEndpoint endpoint, Processor processor) {
+ super(endpoint, processor);
+ this.eventNotifier = new EventNotifierSupport() {
+ @Override
+ public void notify(CamelEvent event) throws Exception {
+ onEvent(event);
+ }
+
+ @Override
+ public boolean isEnabled(CamelEvent event) {
+ // Guard against recursive event notifications
+ if (PROCESSING.get()) {
+ return false;
+ }
+ return isEventAccepted(event);
+ }
+ };
+ // Configure the notifier to only receive events we care about
+ configureEventNotifier();
+ }
+
+ @Override
+ public EventEndpoint getEndpoint() {
+ return (EventEndpoint) super.getEndpoint();
+ }
+
+ @Override
+ protected void doStart() throws Exception {
+ super.doStart();
+
getEndpoint().getCamelContext().getManagementStrategy().addEventNotifier(eventNotifier);
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+
getEndpoint().getCamelContext().getManagementStrategy().removeEventNotifier(eventNotifier);
+ super.doStop();
+ }
+
+ /**
+ * Configures the event notifier to enable only the event categories that
are needed based on the subscribed event
+ * types.
+ */
+ private void configureEventNotifier() {
+ Set<CamelEvent.Type> types = getEndpoint().getEventTypes();
+ if (types == null || types.isEmpty()) {
+ return;
+ }
+
+ // Start by ignoring everything
+ eventNotifier.setIgnoreCamelContextInitEvents(true);
+ eventNotifier.setIgnoreCamelContextEvents(true);
+ eventNotifier.setIgnoreRouteEvents(true);
+ eventNotifier.setIgnoreServiceEvents(true);
+ eventNotifier.setIgnoreExchangeEvents(true);
+ eventNotifier.setIgnoreExchangeCreatedEvent(true);
+ eventNotifier.setIgnoreExchangeCompletedEvent(true);
+ eventNotifier.setIgnoreExchangeFailedEvents(true);
+ eventNotifier.setIgnoreExchangeRedeliveryEvents(true);
+ eventNotifier.setIgnoreExchangeSendingEvents(true);
+ eventNotifier.setIgnoreExchangeSentEvents(true);
+ eventNotifier.setIgnoreStepEvents(true);
+
+ // Enable only the categories that contain the requested event types
+ for (CamelEvent.Type type : types) {
+ switch (type) {
+ case CamelContextInitializing:
+ case CamelContextInitialized:
+ eventNotifier.setIgnoreCamelContextInitEvents(false);
+ break;
+ case CamelContextResumed:
+ case CamelContextResumeFailure:
+ case CamelContextResuming:
+ case CamelContextStarted:
+ case CamelContextStarting:
+ case CamelContextStartupFailure:
+ case CamelContextStopFailure:
+ case CamelContextStopped:
+ case CamelContextStopping:
+ case CamelContextSuspended:
+ case CamelContextSuspending:
+ case CamelContextReloading:
+ case CamelContextReloaded:
+ case CamelContextReloadFailure:
+ case RoutesStarting:
+ case RoutesStarted:
+ case RoutesStopping:
+ case RoutesStopped:
+ eventNotifier.setIgnoreCamelContextEvents(false);
+ break;
+ case RouteAdded:
+ case RouteRemoved:
+ case RouteReloaded:
+ case RouteStarting:
+ case RouteStarted:
+ case RouteStopping:
+ case RouteStopped:
+ case RouteRestarting:
+ case RouteRestartingFailure:
+ eventNotifier.setIgnoreRouteEvents(false);
+ break;
+ case ServiceStartupFailure:
+ case ServiceStopFailure:
+ eventNotifier.setIgnoreServiceEvents(false);
+ break;
+ case ExchangeCreated:
+ eventNotifier.setIgnoreExchangeEvents(false);
+ eventNotifier.setIgnoreExchangeCreatedEvent(false);
+ break;
+ case ExchangeCompleted:
+ eventNotifier.setIgnoreExchangeEvents(false);
+ eventNotifier.setIgnoreExchangeCompletedEvent(false);
+ break;
+ case ExchangeFailed:
+ case ExchangeFailureHandled:
+ case ExchangeFailureHandling:
+ eventNotifier.setIgnoreExchangeEvents(false);
+ eventNotifier.setIgnoreExchangeFailedEvents(false);
+ break;
+ case ExchangeRedelivery:
+ eventNotifier.setIgnoreExchangeEvents(false);
+ eventNotifier.setIgnoreExchangeRedeliveryEvents(false);
+ break;
+ case ExchangeSending:
+ eventNotifier.setIgnoreExchangeEvents(false);
+ eventNotifier.setIgnoreExchangeSendingEvents(false);
+ break;
+ case ExchangeSent:
+ eventNotifier.setIgnoreExchangeEvents(false);
+ eventNotifier.setIgnoreExchangeSentEvents(false);
+ break;
+ case StepStarted:
+ case StepCompleted:
+ case StepFailed:
+ eventNotifier.setIgnoreExchangeEvents(false);
+ eventNotifier.setIgnoreStepEvents(false);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Checks whether the given event matches the subscribed event types and
filters.
+ */
+ boolean isEventAccepted(CamelEvent event) {
+ Set<CamelEvent.Type> types = getEndpoint().getEventTypes();
+ if (types == null || types.isEmpty()) {
+ return false;
+ }
+
+ // Check if the event type matches
+ if (!types.contains(event.getType())) {
+ return false;
+ }
+
+ // Apply filter if configured
+ Set<String> filters = getEndpoint().getFilterValues();
+ if (filters != null && !filters.isEmpty()) {
+ String routeId = extractRouteId(event);
+ if (routeId != null) {
+ return filters.contains(routeId);
+ }
+ // If we can't extract a route ID, don't filter (allow the event
through)
+ }
+
+ return true;
+ }
+
+ /**
+ * Extracts the route ID from a Camel event, if available.
+ */
+ private String extractRouteId(CamelEvent event) {
+ if (event instanceof CamelEvent.RouteEvent routeEvent) {
+ Route route = routeEvent.getRoute();
+ return route != null ? route.getRouteId() : null;
+ }
+ if (event instanceof CamelEvent.ExchangeEvent exchangeEvent) {
+ Exchange exchange = exchangeEvent.getExchange();
+ if (exchange != null) {
+ return exchange.getFromRouteId();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Called when a matching Camel event is received. Creates an exchange and
processes it.
+ */
+ private void onEvent(CamelEvent event) throws Exception {
+ // Guard against recursive event notifications:
+ // processing this exchange will generate exchange events which would
cause infinite recursion
+ if (PROCESSING.get()) {
+ return;
+ }
+ PROCESSING.set(Boolean.TRUE);
+ try {
+ Exchange exchange = createExchange(true);
+ exchange.getIn().setBody(event);
+ exchange.getIn().setHeader("CamelEventType",
event.getType().name());
+ if (event.getTimestamp() > 0) {
+ exchange.getIn().setHeader("CamelEventTimestamp",
event.getTimestamp());
+ }
+
+ // Add route-specific headers for route events
+ if (event instanceof CamelEvent.RouteEvent routeEvent) {
+ Route route = routeEvent.getRoute();
+ if (route != null) {
+ exchange.getIn().setHeader("CamelEventRouteId",
route.getRouteId());
+ }
+ }
+
+ try {
+ getProcessor().process(exchange);
+ } finally {
+ releaseExchange(exchange, false);
+ }
+ } finally {
+ PROCESSING.set(Boolean.FALSE);
+ }
+ }
+}
diff --git
a/components/camel-event/src/main/java/org/apache/camel/component/camelevent/EventEndpoint.java
b/components/camel-event/src/main/java/org/apache/camel/component/camelevent/EventEndpoint.java
new file mode 100644
index 000000000000..4742304c088c
--- /dev/null
+++
b/components/camel-event/src/main/java/org/apache/camel/component/camelevent/EventEndpoint.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.camelevent;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.camel.Category;
+import org.apache.camel.Component;
+import org.apache.camel.Consumer;
+import org.apache.camel.Processor;
+import org.apache.camel.Producer;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.spi.CamelEvent;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.UriEndpoint;
+import org.apache.camel.spi.UriParam;
+import org.apache.camel.spi.UriPath;
+import org.apache.camel.support.DefaultEndpoint;
+
+/**
+ * Subscribe to Camel internal events such as route started/stopped and
exchange completed/failed.
+ *
+ * The URI path specifies comma-separated event types to subscribe to. Event
types correspond to
+ * {@link org.apache.camel.spi.CamelEvent.Type} enum values
(case-insensitive), for example:
+ * {@code event:RouteStarted,RouteStopped} or {@code
event:ExchangeCompleted?filter=myRouteId}.
+ */
+@UriEndpoint(firstVersion = "4.19.0", scheme = "event", title = "Event",
syntax = "event:events",
+ consumerOnly = true, remote = false,
+ category = { Category.CORE, Category.MONITORING })
+public class EventEndpoint extends DefaultEndpoint {
+
+ @UriPath(description = "Comma-separated list of event types to subscribe
to."
+ + " Event types correspond to CamelEvent.Type enum
values (case-insensitive),"
+ + " for example: RouteStarted, RouteStopped,
ExchangeCompleted, ExchangeFailed.")
+ @Metadata(required = true)
+ private String events;
+
+ @UriParam(description = "Comma-separated list of filters to narrow down
events."
+ + " For route events, this filters by route ID."
+ + " For exchange events, this filters by the route
ID of the exchange.")
+ private String filter;
+
+ private Set<CamelEvent.Type> eventTypes;
+ private Set<String> filterValues;
+
+ public EventEndpoint() {
+ }
+
+ public EventEndpoint(String uri, Component component, String events) {
+ super(uri, component);
+ this.events = events;
+ }
+
+ @Override
+ public boolean isRemote() {
+ return false;
+ }
+
+ @Override
+ public Producer createProducer() throws Exception {
+ throw new RuntimeCamelException("Cannot produce to an EventEndpoint: "
+ getEndpointUri());
+ }
+
+ @Override
+ public Consumer createConsumer(Processor processor) throws Exception {
+ EventConsumer consumer = new EventConsumer(this, processor);
+ configureConsumer(consumer);
+ return consumer;
+ }
+
+ @Override
+ protected void doInit() throws Exception {
+ super.doInit();
+ // Parse event types
+ if (events != null && !events.isEmpty()) {
+ eventTypes = Arrays.stream(events.split(","))
+ .map(String::trim)
+ .filter(s -> !s.isEmpty())
+ .map(EventEndpoint::resolveEventType)
+ .collect(Collectors.toSet());
+ } else {
+ eventTypes = Collections.emptySet();
+ }
+ // Parse filter values
+ if (filter != null && !filter.isEmpty()) {
+ filterValues = Arrays.stream(filter.split(","))
+ .map(String::trim)
+ .filter(s -> !s.isEmpty())
+ .collect(Collectors.toSet());
+ } else {
+ filterValues = Collections.emptySet();
+ }
+ }
+
+ /**
+ * Resolves an event type name (case-insensitive) to a {@link
CamelEvent.Type} enum value.
+ */
+ static CamelEvent.Type resolveEventType(String name) {
+ for (CamelEvent.Type type : CamelEvent.Type.values()) {
+ if (type.name().equalsIgnoreCase(name)) {
+ return type;
+ }
+ }
+ throw new IllegalArgumentException(
+ "Unknown event type: " + name + ". Known types: "
+ +
Arrays.toString(CamelEvent.Type.values()));
+ }
+
+ public String getEvents() {
+ return events;
+ }
+
+ /**
+ * Comma-separated list of event types to subscribe to. Event types
correspond to CamelEvent.Type enum values
+ * (case-insensitive), for example: RouteStarted, RouteStopped,
ExchangeCompleted, ExchangeFailed.
+ */
+ public void setEvents(String events) {
+ this.events = events;
+ }
+
+ public String getFilter() {
+ return filter;
+ }
+
+ /**
+ * Comma-separated list of filters to narrow down events. For route
events, this filters by route ID. For exchange
+ * events, this filters by the route ID of the exchange.
+ */
+ public void setFilter(String filter) {
+ this.filter = filter;
+ }
+
+ public Set<CamelEvent.Type> getEventTypes() {
+ return eventTypes;
+ }
+
+ public Set<String> getFilterValues() {
+ return filterValues;
+ }
+}
diff --git
a/components/camel-event/src/test/java/org/apache/camel/component/camelevent/EventComponentTest.java
b/components/camel-event/src/test/java/org/apache/camel/component/camelevent/EventComponentTest.java
new file mode 100644
index 000000000000..c6045520af68
--- /dev/null
+++
b/components/camel-event/src/test/java/org/apache/camel/component/camelevent/EventComponentTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.camelevent;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spi.CamelEvent;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class EventComponentTest extends CamelTestSupport {
+
+ @Test
+ void testRouteEvents() throws Exception {
+ // The event consumer route itself triggers RouteStarted events
+ // during context startup. We need to add a route dynamically
+ // to capture its events.
+ MockEndpoint mock = getMockEndpoint("mock:routeEvents");
+ mock.expectedMinimumMessageCount(2); // at least RouteStarted +
RouteStopped for the dynamic route
+
+ // Add a dynamic route
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+
from("direct:dynamicRoute").routeId("dynamicRoute").to("mock:dynamicResult");
+ }
+ });
+
+ // Stop the dynamic route to trigger RouteStopped
+ context.getRouteController().stopRoute("dynamicRoute");
+
+ mock.assertIsSatisfied();
+
+ // Verify event types
+ boolean hasStarted = mock.getExchanges().stream()
+ .anyMatch(e ->
"RouteStarted".equals(e.getIn().getHeader("CamelEventType")));
+ boolean hasStopped = mock.getExchanges().stream()
+ .anyMatch(e ->
"RouteStopped".equals(e.getIn().getHeader("CamelEventType")));
+ assertEquals(true, hasStarted, "Should have received RouteStarted
event");
+ assertEquals(true, hasStopped, "Should have received RouteStopped
event");
+
+ // Verify the body is the actual CamelEvent
+ CamelEvent event =
mock.getExchanges().get(0).getIn().getBody(CamelEvent.class);
+ assertNotNull(event);
+ }
+
+ @Test
+ void testExchangeEvents() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:exchangeEvents");
+ mock.expectedMinimumMessageCount(1);
+
+ template.sendBody("direct:testExchange", "Hello");
+
+ mock.assertIsSatisfied();
+
+ // Verify event type header
+ String eventType =
mock.getExchanges().get(0).getIn().getHeader("CamelEventType", String.class);
+ assertEquals("ExchangeCompleted", eventType);
+
+ // Verify the body is ExchangeEvent
+ CamelEvent event =
mock.getExchanges().get(0).getIn().getBody(CamelEvent.class);
+ assertInstanceOf(CamelEvent.ExchangeCompletedEvent.class, event);
+ }
+
+ @Test
+ void testFilterByRouteId() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:filteredEvents");
+ mock.expectedMinimumMessageCount(1);
+
+ // Send to the filtered route
+ template.sendBody("direct:filteredRoute", "Hello Filtered");
+
+ // Also send to another route to verify filtering
+ template.sendBody("direct:testExchange", "Hello Other");
+
+ mock.assertIsSatisfied();
+
+ // All received exchanges should be from filteredRoute
+ mock.getExchanges().forEach(e -> {
+ CamelEvent event = e.getIn().getBody(CamelEvent.class);
+ if (event instanceof CamelEvent.ExchangeEvent exchangeEvent) {
+ assertEquals("filteredRoute",
exchangeEvent.getExchange().getFromRouteId());
+ }
+ });
+ }
+
+ @Test
+ void testMultipleEventTypes() throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:multipleEvents");
+ mock.expectedMinimumMessageCount(2);
+
+ // Trigger exchange events
+ template.sendBody("direct:testExchange", "Hello");
+
+ mock.assertIsSatisfied();
+
+ // Should receive both ExchangeCreated and ExchangeCompleted
+ boolean hasCreated = mock.getExchanges().stream()
+ .anyMatch(e ->
"ExchangeCreated".equals(e.getIn().getHeader("CamelEventType")));
+ boolean hasCompleted = mock.getExchanges().stream()
+ .anyMatch(e ->
"ExchangeCompleted".equals(e.getIn().getHeader("CamelEventType")));
+ assertEquals(true, hasCreated, "Should have received ExchangeCreated
event");
+ assertEquals(true, hasCompleted, "Should have received
ExchangeCompleted event");
+ }
+
+ @Test
+ void testInvalidEventType() {
+ assertThrows(IllegalArgumentException.class, () ->
EventEndpoint.resolveEventType("InvalidEventType"));
+ }
+
+ @Test
+ void testConsumerOnly() {
+ assertThrows(Exception.class, () -> {
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:test").to("event:RouteStarted");
+ }
+ });
+ });
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ // Route to capture route events
+ from("event:RouteStarted,RouteStopped")
+ .routeId("routeEventConsumer")
+ .to("mock:routeEvents");
+
+ // Route to capture exchange completed events
+ from("event:ExchangeCompleted")
+ .routeId("exchangeEventConsumer")
+ .to("mock:exchangeEvents");
+
+ // Route with filter
+ from("event:ExchangeCompleted?filter=filteredRoute")
+ .routeId("filteredEventConsumer")
+ .to("mock:filteredEvents");
+
+ // Route with multiple event types
+ from("event:ExchangeCreated,ExchangeCompleted")
+ .routeId("multipleEventConsumer")
+ .to("mock:multipleEvents");
+
+ // Test routes that generate exchange events
+ from("direct:testExchange").routeId("testExchangeRoute")
+ .log("Processing test exchange");
+
+ from("direct:filteredRoute").routeId("filteredRoute")
+ .log("Processing filtered exchange");
+ }
+ };
+ }
+}
diff --git a/components/pom.xml b/components/pom.xml
index a463c53292c2..a5303ece65e6 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -132,6 +132,7 @@
<module>camel-elasticsearch</module>
<module>camel-elasticsearch-rest-client</module>
<module>camel-elytron</module>
+ <module>camel-event</module>
<module>camel-exec</module>
<module>camel-fastjson</module>
<module>camel-fhir</module>
diff --git
a/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties
b/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties
index a88d76a89dd5..60ab2e4e8a74 100644
---
a/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties
+++
b/core/camel-main/src/generated/resources/org/apache/camel/main/components.properties
@@ -109,6 +109,7 @@ dynamic-router-control
ehcache
elasticsearch
elasticsearch-rest-client
+event
exec
fhir
file
diff --git a/docs/components/modules/ROOT/examples/json/event.json
b/docs/components/modules/ROOT/examples/json/event.json
new file mode 120000
index 000000000000..4ecf56f4c129
--- /dev/null
+++ b/docs/components/modules/ROOT/examples/json/event.json
@@ -0,0 +1 @@
+../../../../../../components/camel-event/src/generated/resources/META-INF/org/apache/camel/component/camelevent/event.json
\ No newline at end of file
diff --git a/docs/components/modules/ROOT/nav.adoc
b/docs/components/modules/ROOT/nav.adoc
index db785be79049..3c000c4fc0d7 100644
--- a/docs/components/modules/ROOT/nav.adoc
+++ b/docs/components/modules/ROOT/nav.adoc
@@ -134,6 +134,7 @@
** xref:ehcache-component.adoc[Ehcache]
** xref:elasticsearch-component.adoc[Elasticsearch]
** xref:elasticsearch-rest-client-component.adoc[Elasticsearch Low level Rest
Client]
+** xref:event-component.adoc[Event]
** xref:exec-component.adoc[Exec]
** xref:fhir-component.adoc[FHIR]
** xref:file-component.adoc[File]
diff --git a/docs/components/modules/ROOT/pages/event-component.adoc
b/docs/components/modules/ROOT/pages/event-component.adoc
new file mode 120000
index 000000000000..68c608d5ae93
--- /dev/null
+++ b/docs/components/modules/ROOT/pages/event-component.adoc
@@ -0,0 +1 @@
+../../../../../components/camel-event/src/main/docs/event-component.adoc
\ No newline at end of file
diff --git a/docs/gulpfile.js b/docs/gulpfile.js
index c41af376504e..39d7dc056961 100644
--- a/docs/gulpfile.js
+++ b/docs/gulpfile.js
@@ -326,7 +326,7 @@ const tasks = Array.from(sourcesMap).flatMap(([type,
definition]) => {
}
})
- return gulp.src(source)
+ return gulp.src(source, { ignore: ['**/target/**'] })
.pipe(filterFn)
.pipe(
map((file, done) => {
@@ -409,7 +409,7 @@ const tasks = Array.from(sourcesMap).flatMap(([type,
definition]) => {
return done()
}
- return gulp.src(source) // asciidoc files
+ return gulp.src(source, { ignore: ['**/target/**'] }) // asciidoc files
.pipe(through2.obj(extractExamples)) // extracted example files
// symlink links from a fixed directory, i.e. we could link to
// the example files from `destination`, that would not work for
diff --git
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
index c6cfcc81e042..aa35ad5d4c83 100644
---
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
+++
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/ComponentsBuilderFactory.java
@@ -1520,6 +1520,20 @@ public interface ComponentsBuilderFactory {
static
ElasticsearchRestClientComponentBuilderFactory.ElasticsearchRestClientComponentBuilder
elasticsearchRestClient() {
return
ElasticsearchRestClientComponentBuilderFactory.elasticsearchRestClient();
}
+ /**
+ * Event (camel-event)
+ * Subscribe to Camel internal events such as route started/stopped and
+ * exchange completed/failed.
+ *
+ * Category: core,monitoring
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-event
+ *
+ * @return the dsl builder
+ */
+ static EventComponentBuilderFactory.EventComponentBuilder event() {
+ return EventComponentBuilderFactory.event();
+ }
/**
* Exec (camel-exec)
* Execute commands on the underlying operating system.
diff --git
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/EventComponentBuilderFactory.java
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/EventComponentBuilderFactory.java
new file mode 100644
index 000000000000..8a19821d4997
--- /dev/null
+++
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/EventComponentBuilderFactory.java
@@ -0,0 +1,125 @@
+/* Generated by camel build tools - do NOT edit this file! */
+/*
+ * 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.builder.component.dsl;
+
+import javax.annotation.processing.Generated;
+import org.apache.camel.Component;
+import org.apache.camel.builder.component.AbstractComponentBuilder;
+import org.apache.camel.builder.component.ComponentBuilder;
+import org.apache.camel.component.camelevent.EventComponent;
+
+/**
+ * Subscribe to Camel internal events such as route started/stopped and
exchange
+ * completed/failed.
+ *
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.ComponentDslMojo")
+public interface EventComponentBuilderFactory {
+
+ /**
+ * Event (camel-event)
+ * Subscribe to Camel internal events such as route started/stopped and
+ * exchange completed/failed.
+ *
+ * Category: core,monitoring
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-event
+ *
+ * @return the dsl builder
+ */
+ static EventComponentBuilder event() {
+ return new EventComponentBuilderImpl();
+ }
+
+ /**
+ * Builder for the Event component.
+ */
+ interface EventComponentBuilder extends ComponentBuilder<EventComponent> {
+
+
+ /**
+ * Allows for bridging the consumer to the Camel routing Error Handler,
+ * which mean any exceptions (if possible) occurred while the Camel
+ * consumer is trying to pickup incoming messages, or the likes, will
+ * now be processed as a message and handled by the routing Error
+ * Handler. Important: This is only possible if the 3rd party component
+ * allows Camel to be alerted if an exception was thrown. Some
+ * components handle this internally only, and therefore
+ * bridgeErrorHandler is not possible. In other situations we may
+ * improve the Camel component to hook into the 3rd party component and
+ * make this possible for future releases. 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.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: false
+ * Group: consumer
+ *
+ * @param bridgeErrorHandler the value to set
+ * @return the dsl builder
+ */
+ default EventComponentBuilder bridgeErrorHandler(boolean
bridgeErrorHandler) {
+ doSetProperty("bridgeErrorHandler", bridgeErrorHandler);
+ return this;
+ }
+
+
+ /**
+ * Whether autowiring is enabled. This is used for automatic autowiring
+ * options (the option must be marked as autowired) by looking up in
the
+ * registry to find if there is a single instance of matching type,
+ * which then gets configured on the component. This can be used for
+ * automatic configuring JDBC data sources, JMS connection factories,
+ * AWS Clients, etc.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: advanced
+ *
+ * @param autowiredEnabled the value to set
+ * @return the dsl builder
+ */
+ default EventComponentBuilder autowiredEnabled(boolean
autowiredEnabled) {
+ doSetProperty("autowiredEnabled", autowiredEnabled);
+ return this;
+ }
+ }
+
+ class EventComponentBuilderImpl
+ extends AbstractComponentBuilder<EventComponent>
+ implements EventComponentBuilder {
+ @Override
+ protected EventComponent buildConcreteComponent() {
+ return new EventComponent();
+ }
+ @Override
+ protected boolean setPropertyOnComponent(
+ Component component,
+ String name,
+ Object value) {
+ switch (name) {
+ case "bridgeErrorHandler": ((EventComponent)
component).setBridgeErrorHandler((boolean) value); return true;
+ case "autowiredEnabled": ((EventComponent)
component).setAutowiredEnabled((boolean) value); return true;
+ default: return false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
index 56ff87a30ad5..b5a207337253 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/StaticEndpointBuilders.java
@@ -4861,6 +4861,52 @@ public class StaticEndpointBuilders {
public static
ElasticsearchRestClientEndpointBuilderFactory.ElasticsearchRestClientEndpointBuilder
elasticsearchRestClient(String componentName, String path) {
return
ElasticsearchRestClientEndpointBuilderFactory.endpointBuilder(componentName,
path);
}
+ /**
+ * Event (camel-event)
+ * Subscribe to Camel internal events such as route started/stopped and
+ * exchange completed/failed.
+ *
+ * Category: core,monitoring
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-event
+ *
+ * Syntax: <code>event:events</code>
+ *
+ * Path parameter: events (required)
+ * Comma-separated list of event types to subscribe to. Event types
+ * correspond to CamelEvent.Type enum values (case-insensitive), for
+ * example: RouteStarted, RouteStopped, ExchangeCompleted, ExchangeFailed.
+ *
+ * @param path events
+ * @return the dsl builder
+ */
+ public static EventEndpointBuilderFactory.EventEndpointBuilder
event(String path) {
+ return event("event", path);
+ }
+ /**
+ * Event (camel-event)
+ * Subscribe to Camel internal events such as route started/stopped and
+ * exchange completed/failed.
+ *
+ * Category: core,monitoring
+ * Since: 4.19
+ * Maven coordinates: org.apache.camel:camel-event
+ *
+ * Syntax: <code>event:events</code>
+ *
+ * Path parameter: events (required)
+ * Comma-separated list of event types to subscribe to. Event types
+ * correspond to CamelEvent.Type enum values (case-insensitive), for
+ * example: RouteStarted, RouteStopped, ExchangeCompleted, ExchangeFailed.
+ *
+ * @param componentName to use a custom component name for the endpoint
+ * instead of the default name
+ * @param path events
+ * @return the dsl builder
+ */
+ public static EventEndpointBuilderFactory.EventEndpointBuilder
event(String componentName, String path) {
+ return EventEndpointBuilderFactory.endpointBuilder(componentName,
path);
+ }
/**
* Exec (camel-exec)
* Execute commands on the underlying operating system.
diff --git
a/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties
b/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties
index a2a0b4a89393..709a87950be4 100644
---
a/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties
+++
b/dsl/camel-kamelet-main/src/generated/resources/camel-component-known-dependencies.properties
@@ -80,6 +80,7 @@
org.apache.camel.component.braintree.BraintreeComponent=camel:braintree
org.apache.camel.component.browse.BrowseComponent=camel:browse
org.apache.camel.component.caffeine.cache.CaffeineCacheComponent=camel:caffeine
org.apache.camel.component.caffeine.load.CaffeineLoadCacheComponent=camel:caffeine
+org.apache.camel.component.camelevent.EventComponent=camel:event
org.apache.camel.component.cassandra.CassandraComponent=camel:cassandraql
org.apache.camel.component.chatscript.ChatScriptComponent=camel:chatscript
org.apache.camel.component.chunk.ChunkComponent=camel:chunk
diff --git a/parent/pom.xml b/parent/pom.xml
index e5622efc18df..ce9604ee16b7 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -1380,6 +1380,11 @@
<artifactId>camel-elytron</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-event</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-exec</artifactId>