diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml index 8a873be29d48c..1bcc50338c794 100644 --- a/bom/camel-bom/pom.xml +++ b/bom/camel-bom/pom.xml @@ -832,6 +832,11 @@ camel-endpointdsl-support 4.19.0-SNAPSHOT + + org.apache.camel + camel-event + 4.19.0-SNAPSHOT + org.apache.camel camel-exec diff --git a/catalog/camel-allcomponents/pom.xml b/catalog/camel-allcomponents/pom.xml index 2974d41243868..f8080d5e12462 100644 --- a/catalog/camel-allcomponents/pom.xml +++ b/catalog/camel-allcomponents/pom.xml @@ -702,6 +702,11 @@ camel-elytron ${project.version} + + org.apache.camel + camel-event + ${project.version} + org.apache.camel camel-exec 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 a88d76a89dd53..60ab2e4e8a747 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 0000000000000..abed8d130c0c6 --- /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 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." }, + "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 type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." } + }, + "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 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." }, + "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 default the consumer will deal with exceptions, that will be logged at WARN or ERROR level and ignored." }, + "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 0000000000000..7c2b44f1fd069 --- /dev/null +++ b/components/camel-event/pom.xml @@ -0,0 +1,49 @@ + + + + 4.0.0 + + + org.apache.camel + components + 4.19.0-SNAPSHOT + + + camel-event + jar + + Camel :: Event + Camel Event component for subscribing to Camel internal events + + + + + org.apache.camel + camel-support + + + + + org.apache.camel + camel-test-junit5 + test + + + 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 0000000000000..32ef198384232 --- /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 0000000000000..c24a00316051e --- /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 0000000000000..9154e8d456f11 --- /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 PROPERTY_NAMES; + private static final Set SECRET_PROPERTY_NAMES; + private static final Map MULTI_VALUE_PREFIXES; + static { + Set 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 properties, boolean encode) throws URISyntaxException { + String syntax = scheme + BASE; + String uri = syntax; + + Map copy = new HashMap<>(properties); + + uri = buildPathParameter(syntax, uri, "events", null, true, copy); + uri = buildQueryParameters(uri, copy, encode); + return uri; + } + + @Override + public Set propertyNames() { + return PROPERTY_NAMES; + } + + @Override + public Set secretPropertyNames() { + return SECRET_PROPERTY_NAMES; + } + + @Override + public Map 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 0000000000000..abed8d130c0c6 --- /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 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." }, + "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 type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." } + }, + "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 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." }, + "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 default the consumer will deal with exceptions, that will be logged at WARN or ERROR level and ignored." }, + "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 0000000000000..9e42b877a3460 --- /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 0000000000000..cc244fdefe8de --- /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 0000000000000..378de5b367914 --- /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 0000000000000..49a7f6655c4ec --- /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 0000000000000..c614cd9e3af26 --- /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 0000000000000..9f914c50a6eeb --- /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 0000000000000..7190293ef5f41 --- /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. + */ +@org.apache.camel.spi.annotations.Component("event") +public class EventComponent extends DefaultComponent { + + public EventComponent() { + } + + @Override + protected Endpoint createEndpoint(String uri, String remaining, Map 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 0000000000000..20b934772c1ea --- /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 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 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 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 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 0000000000000..4742304c088cb --- /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 eventTypes; + private Set 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 getEventTypes() { + return eventTypes; + } + + public Set 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 0000000000000..c6045520af68c --- /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 a463c53292c2c..a5303ece65e61 100644 --- a/components/pom.xml +++ b/components/pom.xml @@ -132,6 +132,7 @@ camel-elasticsearch camel-elasticsearch-rest-client camel-elytron + camel-event camel-exec camel-fastjson camel-fhir 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 a88d76a89dd53..60ab2e4e8a747 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 0000000000000..4ecf56f4c1297 --- /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 db785be79049d..3c000c4fc0d78 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 0000000000000..68c608d5ae93e --- /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 c41af376504ec..39d7dc056961a 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 c6cfcc81e0422..aa35ad5d4c83c 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 @@ static ElasticsearchComponentBuilderFactory.ElasticsearchComponentBuilder elasti 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 0000000000000..8a19821d49970 --- /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 { + + + /** + * 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 + 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 56ff87a30ad50..b5a207337253a 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 static ElasticsearchRestClientEndpointBuilderFactory.ElasticsearchRestCli 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: event:events + * + * 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: event:events + * + * 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 a2a0b4a893939..709a87950be40 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 e5622efc18df3..ce9604ee16b7f 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -1380,6 +1380,11 @@ camel-elytron ${project.version} + + org.apache.camel + camel-event + ${project.version} + org.apache.camel camel-exec