You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by gn...@apache.org on 2026/03/13 10:41:20 UTC

(camel) 01/01: CAMEL-23173: Add camel-event component for subscribing to Camel events

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

gnodet pushed a commit to branch camel-23173-event-component
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 31bf87b8d18d25daed78605c9d3a0c333dc3722f
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Fri Mar 13 11:41:00 2026 +0100

    CAMEL-23173: Add camel-event component for subscribing to Camel events
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 .../org/apache/camel/catalog/components/event.json |  37 +++
 components/camel-event/pom.xml                     |  49 ++++
 .../component/event/EventComponentConfigurer.java  |  57 +++++
 .../component/event/EventEndpointConfigurer.java   |  66 ++++++
 .../component/event/EventEndpointUriFactory.java   |  74 ++++++
 .../org/apache/camel/component/event/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 | 150 ++++++++++++
 .../camel/component/event/EventComponent.java      |  39 ++++
 .../camel/component/event/EventConsumer.java       | 259 +++++++++++++++++++++
 .../camel/component/event/EventEndpoint.java       | 157 +++++++++++++
 .../camel/component/event/EventComponentTest.java  | 176 ++++++++++++++
 components/pom.xml                                 |   1 +
 17 files changed, 1117 insertions(+)

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..596c2843b713
--- /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.event.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/event/EventComponentConfigurer.java b/components/camel-event/src/generated/java/org/apache/camel/component/event/EventComponentConfigurer.java
new file mode 100644
index 000000000000..a1bbc2e5328f
--- /dev/null
+++ b/components/camel-event/src/generated/java/org/apache/camel/component/event/EventComponentConfigurer.java
@@ -0,0 +1,57 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.event;
+
+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/event/EventEndpointConfigurer.java b/components/camel-event/src/generated/java/org/apache/camel/component/event/EventEndpointConfigurer.java
new file mode 100644
index 000000000000..2bd2f7b737fb
--- /dev/null
+++ b/components/camel-event/src/generated/java/org/apache/camel/component/event/EventEndpointConfigurer.java
@@ -0,0 +1,66 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.event;
+
+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/event/EventEndpointUriFactory.java b/components/camel-event/src/generated/java/org/apache/camel/component/event/EventEndpointUriFactory.java
new file mode 100644
index 000000000000..2cab07fb1c72
--- /dev/null
+++ b/components/camel-event/src/generated/java/org/apache/camel/component/event/EventEndpointUriFactory.java
@@ -0,0 +1,74 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.component.event;
+
+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/event/event.json b/components/camel-event/src/generated/resources/META-INF/org/apache/camel/component/event/event.json
new file mode 100644
index 000000000000..596c2843b713
--- /dev/null
+++ b/components/camel-event/src/generated/resources/META-INF/org/apache/camel/component/event/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.event.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..6a428e460ebd
--- /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.event.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..cbe980bd59a8
--- /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.event.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..fb6d7352afd9
--- /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.event.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..a24a2a6bfdd1
--- /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.event.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..b044d5e200b1
--- /dev/null
+++ b/components/camel-event/src/main/docs/event-component.adoc
@@ -0,0 +1,150 @@
+= 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");
+----
diff --git a/components/camel-event/src/main/java/org/apache/camel/component/event/EventComponent.java b/components/camel-event/src/main/java/org/apache/camel/component/event/EventComponent.java
new file mode 100644
index 000000000000..8f351547465d
--- /dev/null
+++ b/components/camel-event/src/main/java/org/apache/camel/component/event/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.event;
+
+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.
+ */
+@org.apache.camel.spi.annotations.Component("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/event/EventConsumer.java b/components/camel-event/src/main/java/org/apache/camel/component/event/EventConsumer.java
new file mode 100644
index 000000000000..4e04f11f008e
--- /dev/null
+++ b/components/camel-event/src/main/java/org/apache/camel/component/event/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.event;
+
+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/event/EventEndpoint.java b/components/camel-event/src/main/java/org/apache/camel/component/event/EventEndpoint.java
new file mode 100644
index 000000000000..f179b53b55b7
--- /dev/null
+++ b/components/camel-event/src/main/java/org/apache/camel/component/event/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.event;
+
+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/event/EventComponentTest.java b/components/camel-event/src/test/java/org/apache/camel/component/event/EventComponentTest.java
new file mode 100644
index 000000000000..02daa81ee3a0
--- /dev/null
+++ b/components/camel-event/src/test/java/org/apache/camel/component/event/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.event;
+
+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>