From fbdf44fd68e87f37d17a7dbb25fe6483769b17b4 Mon Sep 17 00:00:00 2001 From: Adriano Machado <60320+ammachado@users.noreply.github.com> Date: Mon, 11 May 2026 15:38:01 -0400 Subject: [PATCH 1/4] CAMEL-23302: camel-xslt - Auto-disable content cache in routes-reload (dev) mode Introduce `ContentCacheAware` SPI so resource-based components can opt into having their content cache set to false when `camel.main.routesReloadEnabled` is set to `true` (set automatically by `camel run --dev`). A new `DevModeContentCacheStrategy` applies the setting on component registration, and explicit user settings on the component, endpoint or URI are always respected. `camel-xslt` is the first adopter: `XsltComponent` now implements `ContentCacheAware` and stores `contentCache` as a Boolean (null = unset). Other resource-based components can be migrated in a follow-up. Co-Authored-By: Claude Opus 4.7 rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED --- .../apache/camel/catalog/components/xj.json | 2 +- .../camel/catalog/components/xslt-saxon.json | 2 +- .../apache/camel/catalog/components/xslt.json | 2 +- .../org/apache/camel/component/xj/xj.json | 2 +- .../component/xslt/saxon/xslt-saxon.json | 2 +- .../xslt/XsltComponentConfigurer.java | 6 +- .../org/apache/camel/component/xslt/xslt.json | 2 +- .../src/main/docs/xslt-component.adoc | 8 ++ .../camel/component/xslt/XsltComponent.java | 13 +- .../apache/camel/spi/ContentCacheAware.java | 42 +++++++ .../main/DefaultConfigurationConfigurer.java | 3 + .../main/DevModeContentCacheStrategy.java | 45 +++++++ .../main/MainDevModeContentCacheTest.java | 117 ++++++++++++++++++ .../modules/ROOT/pages/camel-jbang.adoc | 10 ++ .../modules/ROOT/pages/route-reload.adoc | 10 ++ 15 files changed, 254 insertions(+), 12 deletions(-) create mode 100644 core/camel-api/src/main/java/org/apache/camel/spi/ContentCacheAware.java create mode 100644 core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java create mode 100644 core/camel-main/src/test/java/org/apache/camel/main/MainDevModeContentCacheTest.java diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xj.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xj.json index 0b86c2bdd7fcf..011459040bab1 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xj.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xj.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "saxonConfiguration": { "index": 4, "kind": "property", "displayName": "Saxon Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "net.sf.saxon.Configuration", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom Saxon configuration" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt-saxon.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt-saxon.json index fbeb505cee6a5..605844f4ac9cf 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt-saxon.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt-saxon.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "saxonConfiguration": { "index": 4, "kind": "property", "displayName": "Saxon Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "net.sf.saxon.Configuration", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom Saxon configuration" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json index 10e2171a90844..9fe93d9c1e98b 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "transformerFactoryClass": { "index": 4, "kind": "property", "displayName": "Transformer Factory Class", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom XSLT transformer factory, specified as a FQN class name" }, diff --git a/components/camel-xj/src/generated/resources/META-INF/org/apache/camel/component/xj/xj.json b/components/camel-xj/src/generated/resources/META-INF/org/apache/camel/component/xj/xj.json index 0b86c2bdd7fcf..011459040bab1 100644 --- a/components/camel-xj/src/generated/resources/META-INF/org/apache/camel/component/xj/xj.json +++ b/components/camel-xj/src/generated/resources/META-INF/org/apache/camel/component/xj/xj.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "saxonConfiguration": { "index": 4, "kind": "property", "displayName": "Saxon Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "net.sf.saxon.Configuration", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom Saxon configuration" }, diff --git a/components/camel-xslt-saxon/src/generated/resources/META-INF/org/apache/camel/component/xslt/saxon/xslt-saxon.json b/components/camel-xslt-saxon/src/generated/resources/META-INF/org/apache/camel/component/xslt/saxon/xslt-saxon.json index fbeb505cee6a5..605844f4ac9cf 100644 --- a/components/camel-xslt-saxon/src/generated/resources/META-INF/org/apache/camel/component/xslt/saxon/xslt-saxon.json +++ b/components/camel-xslt-saxon/src/generated/resources/META-INF/org/apache/camel/component/xslt/saxon/xslt-saxon.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "saxonConfiguration": { "index": 4, "kind": "property", "displayName": "Saxon Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "net.sf.saxon.Configuration", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom Saxon configuration" }, diff --git a/components/camel-xslt/src/generated/java/org/apache/camel/component/xslt/XsltComponentConfigurer.java b/components/camel-xslt/src/generated/java/org/apache/camel/component/xslt/XsltComponentConfigurer.java index 14dea08d8b6ba..ade6fce7d8696 100644 --- a/components/camel-xslt/src/generated/java/org/apache/camel/component/xslt/XsltComponentConfigurer.java +++ b/components/camel-xslt/src/generated/java/org/apache/camel/component/xslt/XsltComponentConfigurer.java @@ -28,7 +28,7 @@ public boolean configure(CamelContext camelContext, Object obj, String name, Obj case "autowiredenabled": case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true; case "contentcache": - case "contentCache": target.setContentCache(property(camelContext, boolean.class, value)); return true; + case "contentCache": target.setContentCache(property(camelContext, java.lang.Boolean.class, value)); return true; case "lazystartproducer": case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; case "transformerfactoryclass": @@ -53,7 +53,7 @@ public Class getOptionType(String name, boolean ignoreCase) { case "autowiredenabled": case "autowiredEnabled": return boolean.class; case "contentcache": - case "contentCache": return boolean.class; + case "contentCache": return java.lang.Boolean.class; case "lazystartproducer": case "lazyStartProducer": return boolean.class; case "transformerfactoryclass": @@ -79,7 +79,7 @@ public Object getOptionValue(Object obj, String name, boolean ignoreCase) { case "autowiredenabled": case "autowiredEnabled": return target.isAutowiredEnabled(); case "contentcache": - case "contentCache": return target.isContentCache(); + case "contentCache": return target.getContentCache(); case "lazystartproducer": case "lazyStartProducer": return target.isLazyStartProducer(); case "transformerfactoryclass": diff --git a/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json b/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json index 10e2171a90844..9fe93d9c1e98b 100644 --- a/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json +++ b/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "transformerFactoryClass": { "index": 4, "kind": "property", "displayName": "Transformer Factory Class", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom XSLT transformer factory, specified as a FQN class name" }, diff --git a/components/camel-xslt/src/main/docs/xslt-component.adoc b/components/camel-xslt/src/main/docs/xslt-component.adoc index 85cfc8f6f17b6..3c0b81664537a 100644 --- a/components/camel-xslt/src/main/docs/xslt-component.adoc +++ b/components/camel-xslt/src/main/docs/xslt-component.adoc @@ -185,6 +185,14 @@ TIP: You can set `contentCache=false` and refer to a non-existing template, such as this will tell Camel to not load `dummy.xsl` on startup but to load the stylesheet on demand. And because you provide the stylesheet via headers, then it is fully dynamic. +== Live reload in dev mode + +When routes-reload is enabled (`camel.main.routesReloadEnabled=true`, automatically set by `camel run --dev`), +Camel disables `contentCache` on the XSLT component (via the `org.apache.camel.spi.ContentCacheAware` SPI) +so that edits to resource files are picked up on the next message without having to restart the route. +Explicit user settings (on the component, endpoint, or URI) are always respected: set `contentCache=true` +to keep caching even in dev mode. + == Accessing warnings, errors and fatalErrors from XSLT ErrorListener Any warning/error or fatalError is stored on diff --git a/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltComponent.java b/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltComponent.java index 07eb427092811..ee052812955b1 100644 --- a/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltComponent.java +++ b/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltComponent.java @@ -21,6 +21,7 @@ import javax.xml.transform.URIResolver; import org.apache.camel.Endpoint; +import org.apache.camel.spi.ContentCacheAware; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.annotations.Component; import org.apache.camel.support.DefaultComponent; @@ -32,7 +33,7 @@ * The XSLT Component is for performing XSLT transformations of messages */ @Component("xslt") -public class XsltComponent extends DefaultComponent { +public class XsltComponent extends DefaultComponent implements ContentCacheAware { private static final Logger LOG = LoggerFactory.getLogger(XsltComponent.class); @@ -45,7 +46,7 @@ public class XsltComponent extends DefaultComponent { @Metadata private boolean allowTemplateFromHeader; @Metadata(defaultValue = "true") - private boolean contentCache = true; + private Boolean contentCache; @Metadata(label = "advanced") private TransformerFactoryConfigurationStrategy transformerFactoryConfigurationStrategy; @Metadata(label = "advanced") @@ -94,6 +95,11 @@ public void setAllowTemplateFromHeader(boolean allowTemplateFromHeader) { } public boolean isContentCache() { + return contentCache != null ? contentCache : true; + } + + @Override + public Boolean getContentCache() { return contentCache; } @@ -102,7 +108,8 @@ public boolean isContentCache() { * stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to * reload at runtime via JMX using the clearCachedStylesheet operation. */ - public void setContentCache(boolean contentCache) { + @Override + public void setContentCache(Boolean contentCache) { this.contentCache = contentCache; } diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ContentCacheAware.java b/core/camel-api/src/main/java/org/apache/camel/spi/ContentCacheAware.java new file mode 100644 index 0000000000000..a5978a951007d --- /dev/null +++ b/core/camel-api/src/main/java/org/apache/camel/spi/ContentCacheAware.java @@ -0,0 +1,42 @@ +/* + * 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.spi; + +/** + * Represents a resource-based component (or endpoint) whose loaded resource content can be cached. Implementations + * expose the tri-state {@code contentCache} setting: {@code null} indicates the user has not explicitly configured the + * option (and the implementation will resolve to its own default), while {@code true} or {@code false} are explicit + * configurations. + * + * The tri-state form allows runtime infrastructure (e.g. Camel Main) to safely flip the default in dev/reload scenarios + * without overriding user-supplied values. + */ +public interface ContentCacheAware { + + /** + * The raw content-cache setting. + * + * @return {@code null} if not explicitly configured, otherwise the configured value. + */ + Boolean getContentCache(); + + /** + * Sets the content-cache option. Pass {@code null} to mark the option as unset (so callers using + * {@link #getContentCache()} can distinguish "not configured" from an explicit {@code true}/{@code false}). + */ + void setContentCache(Boolean contentCache); +} diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java index a7b580fe897ad..84793d6100f30 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/DefaultConfigurationConfigurer.java @@ -298,6 +298,9 @@ public static void configure(CamelContext camelContext, DefaultConfigurationProp reloader.setPattern(config.getRoutesReloadPattern()); reloader.setRemoveAllRoutes(config.isRoutesReloadRemoveAllRoutes()); camelContext.addService(reloader); + // disable contentCache on resource-based components so that resource files (e.g. XSLT + // stylesheets, templates) are reloaded live without restarting routes + camelContext.addLifecycleStrategy(new DevModeContentCacheStrategy()); } if (config.getDumpRoutes() != null) { DumpRoutesStrategy drs = camelContext.getCamelContextExtension().getContextPlugin(DumpRoutesStrategy.class); diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java b/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java new file mode 100644 index 0000000000000..a2c2d441fea70 --- /dev/null +++ b/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.main; + +import org.apache.camel.Component; +import org.apache.camel.spi.ContentCacheAware; +import org.apache.camel.support.LifecycleStrategySupport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Lifecycle strategy that disables content caching on resource-based components when routes-reload is enabled. Lets + * users edit a resource file (e.g. an XSLT stylesheet) and see the change applied live, without having to set + * {@code contentCache=false} on every endpoint. + * + * Only flips components that have not been explicitly configured by the user (i.e. + * {@link ContentCacheAware#getContentCache()} returns {@code null}); explicit user settings are preserved. + */ +public class DevModeContentCacheStrategy extends LifecycleStrategySupport { + + private static final Logger LOG = LoggerFactory.getLogger(DevModeContentCacheStrategy.class); + + @Override + public void onComponentAdd(String name, Component component) { + if (component instanceof ContentCacheAware aware && aware.getContentCache() == null) { + aware.setContentCache(Boolean.FALSE); + LOG.info("Routes-reload is enabled: disabling contentCache on component '{}' for live resource reload", + name); + } + } +} diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainDevModeContentCacheTest.java b/core/camel-main/src/test/java/org/apache/camel/main/MainDevModeContentCacheTest.java new file mode 100644 index 0000000000000..fc3af4f960f05 --- /dev/null +++ b/core/camel-main/src/test/java/org/apache/camel/main/MainDevModeContentCacheTest.java @@ -0,0 +1,117 @@ +/* + * 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.main; + +import java.util.Map; + +import org.apache.camel.Endpoint; +import org.apache.camel.spi.ContentCacheAware; +import org.apache.camel.support.DefaultComponent; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class MainDevModeContentCacheTest { + + @Test + public void shouldDisableContentCacheWhenRoutesReloadEnabled() { + Main main = new Main(); + main.configure().withRoutesReloadEnabled(true); + main.bind("dummy", new TestContentCacheComponent()); + + main.start(); + try { + TestContentCacheComponent c = main.getCamelContext().getComponent("dummy", TestContentCacheComponent.class); + assertEquals(Boolean.FALSE, c.getContentCache(), + "contentCache should be auto-disabled when routesReloadEnabled=true"); + } finally { + main.stop(); + } + } + + @Test + public void shouldLeaveContentCacheUnsetWhenRoutesReloadDisabled() { + Main main = new Main(); + // routesReloadEnabled defaults to false + main.bind("dummy", new TestContentCacheComponent()); + + main.start(); + try { + TestContentCacheComponent c = main.getCamelContext().getComponent("dummy", TestContentCacheComponent.class); + assertNull(c.getContentCache(), + "contentCache should remain unset when routesReloadEnabled is not active"); + } finally { + main.stop(); + } + } + + @Test + public void shouldRespectExplicitContentCacheTrue() { + Main main = new Main(); + main.configure().withRoutesReloadEnabled(true); + TestContentCacheComponent component = new TestContentCacheComponent(); + component.setContentCache(Boolean.TRUE); + main.bind("dummy", component); + + main.start(); + try { + TestContentCacheComponent c = main.getCamelContext().getComponent("dummy", TestContentCacheComponent.class); + assertEquals(Boolean.TRUE, c.getContentCache(), + "explicit user setting must not be overridden by dev-mode auto-flip"); + } finally { + main.stop(); + } + } + + @Test + public void shouldRespectExplicitContentCacheFalse() { + Main main = new Main(); + main.configure().withRoutesReloadEnabled(true); + TestContentCacheComponent component = new TestContentCacheComponent(); + component.setContentCache(Boolean.FALSE); + main.bind("dummy", component); + + main.start(); + try { + TestContentCacheComponent c = main.getCamelContext().getComponent("dummy", TestContentCacheComponent.class); + assertEquals(Boolean.FALSE, c.getContentCache()); + } finally { + main.stop(); + } + } + + public static final class TestContentCacheComponent extends DefaultComponent implements ContentCacheAware { + + private Boolean contentCache; + + @Override + public Boolean getContentCache() { + return contentCache; + } + + @Override + public void setContentCache(Boolean contentCache) { + this.contentCache = contentCache; + } + + @Override + protected Endpoint createEndpoint(String uri, String remaining, Map parameters) { + return null; + } + } +} diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc index de578f12c467a..1b59c5e72b66c 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc @@ -529,6 +529,16 @@ and reloaded. You can also delete files to remove routes. NOTE: You cannot use both files and source dir together. The following is not allowed: `camel run abc.java --source-dir=mycode`. +==== Live reload of resource files + +When `--dev` is used, Camel also disables `contentCache` on resource-based components that +implement the `org.apache.camel.spi.ContentCacheAware` interface, so that edits to resource files +are picked up on the next message without having to restart the route. + +This is driven by `camel.main.routesReloadEnabled` (set automatically by `--dev`). Explicit user +settings on the component, endpoint or URI are always respected: set `contentCache=true` to keep +caching even in dev mode. + ==== Loading new routes into existing Camel *Available as of Camel 4.17* diff --git a/docs/user-manual/modules/ROOT/pages/route-reload.adoc b/docs/user-manual/modules/ROOT/pages/route-reload.adoc index 03f2c7e745dd5..5b1944b8414f8 100644 --- a/docs/user-manual/modules/ROOT/pages/route-reload.adoc +++ b/docs/user-manual/modules/ROOT/pages/route-reload.adoc @@ -70,6 +70,16 @@ This is necessary because Apache Camel must stop the existing routes from runnin And adding new routes is therefore possible as they would have a new unique route id specified. +=== Live reload of resource files + +In addition to reloading routes, when `routesReloadEnabled=true` Camel also disables the +`contentCache` option on resource-based components that implement the +`org.apache.camel.spi.ContentCacheAware` interface. This way, edits to resource files are picked +up on the next message without having to restart the route. + +Explicit user settings (configured on the component, endpoint, or URI) are always respected: set +`contentCache=true` to keep caching even when route reload is enabled. + == See Also See related xref:context-reload.adoc[]. From 21d8339b97a1ebfa7bb2bf866a091d4bd34f558e Mon Sep 17 00:00:00 2001 From: Adriano Machado <60320+ammachado@users.noreply.github.com> Date: Tue, 12 May 2026 10:01:21 -0400 Subject: [PATCH 2/4] CAMEL-23302: adding missing generated files rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED --- .../builder/component/dsl/XjComponentBuilderFactory.java | 6 +++--- .../builder/component/dsl/XsltComponentBuilderFactory.java | 6 +++--- .../component/dsl/XsltSaxonComponentBuilderFactory.java | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XjComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XjComponentBuilderFactory.java index 9291ba0ff2bfd..203d7631d8fc5 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XjComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XjComponentBuilderFactory.java @@ -79,7 +79,7 @@ default XjComponentBuilder allowTemplateFromHeader(boolean allowTemplateFromHead * can be forced to reload at runtime via JMX using the * clearCachedStylesheet operation. * - * The option is a: <code>boolean</code> type. + * The option is a: <code>java.lang.Boolean</code> type. * * Default: true * Group: producer @@ -87,7 +87,7 @@ default XjComponentBuilder allowTemplateFromHeader(boolean allowTemplateFromHead * @param contentCache the value to set * @return the dsl builder */ - default XjComponentBuilder contentCache(boolean contentCache) { + default XjComponentBuilder contentCache(java.lang.Boolean contentCache) { doSetProperty("contentCache", contentCache); return this; } @@ -312,7 +312,7 @@ protected boolean setPropertyOnComponent( Object value) { switch (name) { case "allowTemplateFromHeader": ((XJComponent) component).setAllowTemplateFromHeader((boolean) value); return true; - case "contentCache": ((XJComponent) component).setContentCache((boolean) value); return true; + case "contentCache": ((XJComponent) component).setContentCache((java.lang.Boolean) value); return true; case "lazyStartProducer": ((XJComponent) component).setLazyStartProducer((boolean) value); return true; case "autowiredEnabled": ((XJComponent) component).setAutowiredEnabled((boolean) value); return true; case "saxonConfiguration": ((XJComponent) component).setSaxonConfiguration((net.sf.saxon.Configuration) value); return true; diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltComponentBuilderFactory.java index 6390d5d75578a..ab067201d078a 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltComponentBuilderFactory.java @@ -79,7 +79,7 @@ default XsltComponentBuilder allowTemplateFromHeader(boolean allowTemplateFromHe * can be forced to reload at runtime via JMX using the * clearCachedStylesheet operation. * - * The option is a: <code>boolean</code> type. + * The option is a: <code>java.lang.Boolean</code> type. * * Default: true * Group: producer @@ -87,7 +87,7 @@ default XsltComponentBuilder allowTemplateFromHeader(boolean allowTemplateFromHe * @param contentCache the value to set * @return the dsl builder */ - default XsltComponentBuilder contentCache(boolean contentCache) { + default XsltComponentBuilder contentCache(java.lang.Boolean contentCache) { doSetProperty("contentCache", contentCache); return this; } @@ -241,7 +241,7 @@ protected boolean setPropertyOnComponent( Object value) { switch (name) { case "allowTemplateFromHeader": ((XsltComponent) component).setAllowTemplateFromHeader((boolean) value); return true; - case "contentCache": ((XsltComponent) component).setContentCache((boolean) value); return true; + case "contentCache": ((XsltComponent) component).setContentCache((java.lang.Boolean) value); return true; case "lazyStartProducer": ((XsltComponent) component).setLazyStartProducer((boolean) value); return true; case "autowiredEnabled": ((XsltComponent) component).setAutowiredEnabled((boolean) value); return true; case "transformerFactoryClass": ((XsltComponent) component).setTransformerFactoryClass((java.lang.String) value); return true; diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltSaxonComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltSaxonComponentBuilderFactory.java index 9ae7c4cb9298c..75b04bb6a9dcc 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltSaxonComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltSaxonComponentBuilderFactory.java @@ -79,7 +79,7 @@ default XsltSaxonComponentBuilder allowTemplateFromHeader(boolean allowTemplateF * can be forced to reload at runtime via JMX using the * clearCachedStylesheet operation. * - * The option is a: <code>boolean</code> type. + * The option is a: <code>java.lang.Boolean</code> type. * * Default: true * Group: producer @@ -87,7 +87,7 @@ default XsltSaxonComponentBuilder allowTemplateFromHeader(boolean allowTemplateF * @param contentCache the value to set * @return the dsl builder */ - default XsltSaxonComponentBuilder contentCache(boolean contentCache) { + default XsltSaxonComponentBuilder contentCache(java.lang.Boolean contentCache) { doSetProperty("contentCache", contentCache); return this; } @@ -312,7 +312,7 @@ protected boolean setPropertyOnComponent( Object value) { switch (name) { case "allowTemplateFromHeader": ((XsltSaxonComponent) component).setAllowTemplateFromHeader((boolean) value); return true; - case "contentCache": ((XsltSaxonComponent) component).setContentCache((boolean) value); return true; + case "contentCache": ((XsltSaxonComponent) component).setContentCache((java.lang.Boolean) value); return true; case "lazyStartProducer": ((XsltSaxonComponent) component).setLazyStartProducer((boolean) value); return true; case "autowiredEnabled": ((XsltSaxonComponent) component).setAutowiredEnabled((boolean) value); return true; case "saxonConfiguration": ((XsltSaxonComponent) component).setSaxonConfiguration((net.sf.saxon.Configuration) value); return true; From 130cfc6809b7d0a35c5177d5cc4945644a0d48ef Mon Sep 17 00:00:00 2001 From: Adriano Machado <60320+ammachado@users.noreply.github.com> Date: Tue, 12 May 2026 10:06:37 -0400 Subject: [PATCH 3/4] CAMEL-23302: Make `DevModeContentCacheStrategy` package-private visibility and log level. rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED --- .../org/apache/camel/main/DevModeContentCacheStrategy.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java b/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java index a2c2d441fea70..cdc32a3159c4a 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java @@ -30,7 +30,7 @@ * Only flips components that have not been explicitly configured by the user (i.e. * {@link ContentCacheAware#getContentCache()} returns {@code null}); explicit user settings are preserved. */ -public class DevModeContentCacheStrategy extends LifecycleStrategySupport { +class DevModeContentCacheStrategy extends LifecycleStrategySupport { private static final Logger LOG = LoggerFactory.getLogger(DevModeContentCacheStrategy.class); @@ -38,7 +38,7 @@ public class DevModeContentCacheStrategy extends LifecycleStrategySupport { public void onComponentAdd(String name, Component component) { if (component instanceof ContentCacheAware aware && aware.getContentCache() == null) { aware.setContentCache(Boolean.FALSE); - LOG.info("Routes-reload is enabled: disabling contentCache on component '{}' for live resource reload", + LOG.debug("Routes-reload is enabled: disabling contentCache on component '{}' for live resource reload", name); } } From 431bae0854b2d08427e0338b8d0ba84c16967740 Mon Sep 17 00:00:00 2001 From: Adriano Machado <60320+ammachado@users.noreply.github.com> Date: Tue, 12 May 2026 10:45:02 -0400 Subject: [PATCH 4/4] CAMEL-23302: Moving implementation from `ContentCacheAware` SPI to `PropertyConfigurer` rh-pre-commit.version: 2.3.2 rh-pre-commit.check-secrets: ENABLED --- .../apache/camel/catalog/components/xj.json | 2 +- .../camel/catalog/components/xslt-saxon.json | 2 +- .../apache/camel/catalog/components/xslt.json | 2 +- .../org/apache/camel/component/xj/xj.json | 2 +- .../component/xslt/saxon/xslt-saxon.json | 2 +- .../xslt/XsltComponentConfigurer.java | 6 +- .../org/apache/camel/component/xslt/xslt.json | 2 +- .../src/main/docs/xslt-component.adoc | 8 +- .../camel/component/xslt/XsltComponent.java | 13 +- .../apache/camel/spi/ContentCacheAware.java | 42 ------ .../main/DevModeContentCacheStrategy.java | 24 ++-- .../main/MainDevModeContentCacheTest.java | 133 +++++++++++++++--- .../pages/camel-4x-upgrade-guide-4_21.adoc | 6 + .../modules/ROOT/pages/camel-jbang.adoc | 12 +- .../modules/ROOT/pages/route-reload.adoc | 10 +- .../dsl/XjComponentBuilderFactory.java | 6 +- .../dsl/XsltComponentBuilderFactory.java | 6 +- .../dsl/XsltSaxonComponentBuilderFactory.java | 6 +- 18 files changed, 168 insertions(+), 116 deletions(-) delete mode 100644 core/camel-api/src/main/java/org/apache/camel/spi/ContentCacheAware.java diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xj.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xj.json index 011459040bab1..0b86c2bdd7fcf 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xj.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xj.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "saxonConfiguration": { "index": 4, "kind": "property", "displayName": "Saxon Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "net.sf.saxon.Configuration", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom Saxon configuration" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt-saxon.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt-saxon.json index 605844f4ac9cf..fbeb505cee6a5 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt-saxon.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt-saxon.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "saxonConfiguration": { "index": 4, "kind": "property", "displayName": "Saxon Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "net.sf.saxon.Configuration", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom Saxon configuration" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json index 9fe93d9c1e98b..10e2171a90844 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/xslt.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "transformerFactoryClass": { "index": 4, "kind": "property", "displayName": "Transformer Factory Class", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom XSLT transformer factory, specified as a FQN class name" }, diff --git a/components/camel-xj/src/generated/resources/META-INF/org/apache/camel/component/xj/xj.json b/components/camel-xj/src/generated/resources/META-INF/org/apache/camel/component/xj/xj.json index 011459040bab1..0b86c2bdd7fcf 100644 --- a/components/camel-xj/src/generated/resources/META-INF/org/apache/camel/component/xj/xj.json +++ b/components/camel-xj/src/generated/resources/META-INF/org/apache/camel/component/xj/xj.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "saxonConfiguration": { "index": 4, "kind": "property", "displayName": "Saxon Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "net.sf.saxon.Configuration", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom Saxon configuration" }, diff --git a/components/camel-xslt-saxon/src/generated/resources/META-INF/org/apache/camel/component/xslt/saxon/xslt-saxon.json b/components/camel-xslt-saxon/src/generated/resources/META-INF/org/apache/camel/component/xslt/saxon/xslt-saxon.json index 605844f4ac9cf..fbeb505cee6a5 100644 --- a/components/camel-xslt-saxon/src/generated/resources/META-INF/org/apache/camel/component/xslt/saxon/xslt-saxon.json +++ b/components/camel-xslt-saxon/src/generated/resources/META-INF/org/apache/camel/component/xslt/saxon/xslt-saxon.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "saxonConfiguration": { "index": 4, "kind": "property", "displayName": "Saxon Configuration", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "net.sf.saxon.Configuration", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom Saxon configuration" }, diff --git a/components/camel-xslt/src/generated/java/org/apache/camel/component/xslt/XsltComponentConfigurer.java b/components/camel-xslt/src/generated/java/org/apache/camel/component/xslt/XsltComponentConfigurer.java index ade6fce7d8696..14dea08d8b6ba 100644 --- a/components/camel-xslt/src/generated/java/org/apache/camel/component/xslt/XsltComponentConfigurer.java +++ b/components/camel-xslt/src/generated/java/org/apache/camel/component/xslt/XsltComponentConfigurer.java @@ -28,7 +28,7 @@ public boolean configure(CamelContext camelContext, Object obj, String name, Obj case "autowiredenabled": case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true; case "contentcache": - case "contentCache": target.setContentCache(property(camelContext, java.lang.Boolean.class, value)); return true; + case "contentCache": target.setContentCache(property(camelContext, boolean.class, value)); return true; case "lazystartproducer": case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; case "transformerfactoryclass": @@ -53,7 +53,7 @@ public Class getOptionType(String name, boolean ignoreCase) { case "autowiredenabled": case "autowiredEnabled": return boolean.class; case "contentcache": - case "contentCache": return java.lang.Boolean.class; + case "contentCache": return boolean.class; case "lazystartproducer": case "lazyStartProducer": return boolean.class; case "transformerfactoryclass": @@ -79,7 +79,7 @@ public Object getOptionValue(Object obj, String name, boolean ignoreCase) { case "autowiredenabled": case "autowiredEnabled": return target.isAutowiredEnabled(); case "contentcache": - case "contentCache": return target.getContentCache(); + case "contentCache": return target.isContentCache(); case "lazystartproducer": case "lazyStartProducer": return target.isLazyStartProducer(); case "transformerfactoryclass": diff --git a/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json b/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json index 9fe93d9c1e98b..10e2171a90844 100644 --- a/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json +++ b/components/camel-xslt/src/generated/resources/META-INF/org/apache/camel/component/xslt/xslt.json @@ -25,7 +25,7 @@ }, "componentProperties": { "allowTemplateFromHeader": { "index": 0, "kind": "property", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." }, - "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, + "contentCache": { "index": 1, "kind": "property", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Cache for the resource content (the stylesheet file) when it is loaded. If set to false Camel will reload the stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to reload at runtime via JMX using the clearCachedStylesheet operation." }, "lazyStartProducer": { "index": 2, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, "autowiredEnabled": { "index": 3, "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." }, "transformerFactoryClass": { "index": 4, "kind": "property", "displayName": "Transformer Factory Class", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom XSLT transformer factory, specified as a FQN class name" }, diff --git a/components/camel-xslt/src/main/docs/xslt-component.adoc b/components/camel-xslt/src/main/docs/xslt-component.adoc index 3c0b81664537a..18bbad4fea25c 100644 --- a/components/camel-xslt/src/main/docs/xslt-component.adoc +++ b/components/camel-xslt/src/main/docs/xslt-component.adoc @@ -188,10 +188,10 @@ provide the stylesheet via headers, then it is fully dynamic. == Live reload in dev mode When routes-reload is enabled (`camel.main.routesReloadEnabled=true`, automatically set by `camel run --dev`), -Camel disables `contentCache` on the XSLT component (via the `org.apache.camel.spi.ContentCacheAware` SPI) -so that edits to resource files are picked up on the next message without having to restart the route. -Explicit user settings (on the component, endpoint, or URI) are always respected: set `contentCache=true` -to keep caching even in dev mode. +Camel disables `contentCache` on the XSLT component so that edits to the stylesheet file are picked up +on the next message without having to restart the route. User properties (e.g. +`camel.component.xslt.contentCache=true`) and explicit endpoint/URI settings are always respected: +set `contentCache=true` to keep caching even in dev mode. == Accessing warnings, errors and fatalErrors from XSLT ErrorListener diff --git a/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltComponent.java b/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltComponent.java index ee052812955b1..07eb427092811 100644 --- a/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltComponent.java +++ b/components/camel-xslt/src/main/java/org/apache/camel/component/xslt/XsltComponent.java @@ -21,7 +21,6 @@ import javax.xml.transform.URIResolver; import org.apache.camel.Endpoint; -import org.apache.camel.spi.ContentCacheAware; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.annotations.Component; import org.apache.camel.support.DefaultComponent; @@ -33,7 +32,7 @@ * The XSLT Component is for performing XSLT transformations of messages */ @Component("xslt") -public class XsltComponent extends DefaultComponent implements ContentCacheAware { +public class XsltComponent extends DefaultComponent { private static final Logger LOG = LoggerFactory.getLogger(XsltComponent.class); @@ -46,7 +45,7 @@ public class XsltComponent extends DefaultComponent implements ContentCacheAware @Metadata private boolean allowTemplateFromHeader; @Metadata(defaultValue = "true") - private Boolean contentCache; + private boolean contentCache = true; @Metadata(label = "advanced") private TransformerFactoryConfigurationStrategy transformerFactoryConfigurationStrategy; @Metadata(label = "advanced") @@ -95,11 +94,6 @@ public void setAllowTemplateFromHeader(boolean allowTemplateFromHeader) { } public boolean isContentCache() { - return contentCache != null ? contentCache : true; - } - - @Override - public Boolean getContentCache() { return contentCache; } @@ -108,8 +102,7 @@ public Boolean getContentCache() { * stylesheet file on each message processing. This is good for development. A cached stylesheet can be forced to * reload at runtime via JMX using the clearCachedStylesheet operation. */ - @Override - public void setContentCache(Boolean contentCache) { + public void setContentCache(boolean contentCache) { this.contentCache = contentCache; } diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/ContentCacheAware.java b/core/camel-api/src/main/java/org/apache/camel/spi/ContentCacheAware.java deleted file mode 100644 index a5978a951007d..0000000000000 --- a/core/camel-api/src/main/java/org/apache/camel/spi/ContentCacheAware.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.spi; - -/** - * Represents a resource-based component (or endpoint) whose loaded resource content can be cached. Implementations - * expose the tri-state {@code contentCache} setting: {@code null} indicates the user has not explicitly configured the - * option (and the implementation will resolve to its own default), while {@code true} or {@code false} are explicit - * configurations. - * - * The tri-state form allows runtime infrastructure (e.g. Camel Main) to safely flip the default in dev/reload scenarios - * without overriding user-supplied values. - */ -public interface ContentCacheAware { - - /** - * The raw content-cache setting. - * - * @return {@code null} if not explicitly configured, otherwise the configured value. - */ - Boolean getContentCache(); - - /** - * Sets the content-cache option. Pass {@code null} to mark the option as unset (so callers using - * {@link #getContentCache()} can distinguish "not configured" from an explicit {@code true}/{@code false}). - */ - void setContentCache(Boolean contentCache); -} diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java b/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java index cdc32a3159c4a..8dc4ebde5fb97 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/DevModeContentCacheStrategy.java @@ -17,27 +17,35 @@ package org.apache.camel.main; import org.apache.camel.Component; -import org.apache.camel.spi.ContentCacheAware; +import org.apache.camel.spi.PropertyConfigurer; +import org.apache.camel.spi.PropertyConfigurerGetter; import org.apache.camel.support.LifecycleStrategySupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Lifecycle strategy that disables content caching on resource-based components when routes-reload is enabled. Lets - * users edit a resource file (e.g. an XSLT stylesheet) and see the change applied live, without having to set - * {@code contentCache=false} on every endpoint. + * Lifecycle strategy that disables {@code contentCache} on resource-based components when routes-reload is enabled, so + * users can edit a resource file (e.g. an XSLT stylesheet, FreeMarker template) and see the change applied live. * - * Only flips components that have not been explicitly configured by the user (i.e. - * {@link ContentCacheAware#getContentCache()} returns {@code null}); explicit user settings are preserved. + * Components are detected via their generated {@link PropertyConfigurer}: any component whose configurer exposes a + * {@code contentCache} option currently evaluating to {@link Boolean#TRUE} is flipped to {@code false}. User properties + * applied later (e.g. {@code camel.component..contentCache=true}) will override this default. */ class DevModeContentCacheStrategy extends LifecycleStrategySupport { private static final Logger LOG = LoggerFactory.getLogger(DevModeContentCacheStrategy.class); + private static final String CONTENT_CACHE = "contentCache"; + @Override public void onComponentAdd(String name, Component component) { - if (component instanceof ContentCacheAware aware && aware.getContentCache() == null) { - aware.setContentCache(Boolean.FALSE); + PropertyConfigurer configurer = component.getComponentPropertyConfigurer(); + if (!(configurer instanceof PropertyConfigurerGetter getter)) { + return; + } + Object value = getter.getOptionValue(component, CONTENT_CACHE, true); + if (Boolean.TRUE.equals(value) + && configurer.configure(component.getCamelContext(), component, CONTENT_CACHE, Boolean.FALSE, true)) { LOG.debug("Routes-reload is enabled: disabling contentCache on component '{}' for live resource reload", name); } diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainDevModeContentCacheTest.java b/core/camel-main/src/test/java/org/apache/camel/main/MainDevModeContentCacheTest.java index fc3af4f960f05..dd97a841fcccb 100644 --- a/core/camel-main/src/test/java/org/apache/camel/main/MainDevModeContentCacheTest.java +++ b/core/camel-main/src/test/java/org/apache/camel/main/MainDevModeContentCacheTest.java @@ -18,13 +18,20 @@ import java.util.Map; +import org.apache.camel.CamelContext; +import org.apache.camel.Consumer; import org.apache.camel.Endpoint; -import org.apache.camel.spi.ContentCacheAware; +import org.apache.camel.Processor; +import org.apache.camel.Producer; +import org.apache.camel.spi.PropertyConfigurer; +import org.apache.camel.spi.PropertyConfigurerGetter; import org.apache.camel.support.DefaultComponent; +import org.apache.camel.support.DefaultEndpoint; +import org.apache.camel.support.component.PropertyConfigurerSupport; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class MainDevModeContentCacheTest { @@ -37,7 +44,7 @@ public void shouldDisableContentCacheWhenRoutesReloadEnabled() { main.start(); try { TestContentCacheComponent c = main.getCamelContext().getComponent("dummy", TestContentCacheComponent.class); - assertEquals(Boolean.FALSE, c.getContentCache(), + assertFalse(c.isContentCache(), "contentCache should be auto-disabled when routesReloadEnabled=true"); } finally { main.stop(); @@ -45,7 +52,7 @@ public void shouldDisableContentCacheWhenRoutesReloadEnabled() { } @Test - public void shouldLeaveContentCacheUnsetWhenRoutesReloadDisabled() { + public void shouldKeepContentCacheEnabledWhenRoutesReloadDisabled() { Main main = new Main(); // routesReloadEnabled defaults to false main.bind("dummy", new TestContentCacheComponent()); @@ -53,65 +60,145 @@ public void shouldLeaveContentCacheUnsetWhenRoutesReloadDisabled() { main.start(); try { TestContentCacheComponent c = main.getCamelContext().getComponent("dummy", TestContentCacheComponent.class); - assertNull(c.getContentCache(), - "contentCache should remain unset when routesReloadEnabled is not active"); + assertTrue(c.isContentCache(), + "contentCache should remain at its default (true) when routesReloadEnabled is not active"); } finally { main.stop(); } } @Test - public void shouldRespectExplicitContentCacheTrue() { + public void shouldRespectExplicitContentCacheFalse() { Main main = new Main(); main.configure().withRoutesReloadEnabled(true); TestContentCacheComponent component = new TestContentCacheComponent(); - component.setContentCache(Boolean.TRUE); + component.setContentCache(false); main.bind("dummy", component); main.start(); try { TestContentCacheComponent c = main.getCamelContext().getComponent("dummy", TestContentCacheComponent.class); - assertEquals(Boolean.TRUE, c.getContentCache(), - "explicit user setting must not be overridden by dev-mode auto-flip"); + assertFalse(c.isContentCache(), + "explicit user setting (false) must not be touched by dev-mode auto-flip"); } finally { main.stop(); } } @Test - public void shouldRespectExplicitContentCacheFalse() { + public void shouldHonorMainPropertyOverride() { Main main = new Main(); main.configure().withRoutesReloadEnabled(true); - TestContentCacheComponent component = new TestContentCacheComponent(); - component.setContentCache(Boolean.FALSE); - main.bind("dummy", component); + main.addInitialProperty("camel.component.dummy.contentCache", "true"); + main.bind("dummy", new TestContentCacheComponent()); main.start(); try { TestContentCacheComponent c = main.getCamelContext().getComponent("dummy", TestContentCacheComponent.class); - assertEquals(Boolean.FALSE, c.getContentCache()); + assertTrue(c.isContentCache(), + "camel.component..contentCache=true must override the dev-mode auto-flip"); } finally { main.stop(); } } - public static final class TestContentCacheComponent extends DefaultComponent implements ContentCacheAware { + @Test + public void shouldRespectExplicitContentCacheOnUri() { + Main main = new Main(); + main.configure().withRoutesReloadEnabled(true); + main.bind("dummy", new TestContentCacheComponent()); - private Boolean contentCache; + main.start(); + try { + TestContentCacheComponent c = main.getCamelContext().getComponent("dummy", TestContentCacheComponent.class); + assertFalse(c.isContentCache(), "component-level contentCache should be auto-disabled"); - @Override - public Boolean getContentCache() { + TestContentCacheEndpoint endpoint + = (TestContentCacheEndpoint) main.getCamelContext().getEndpoint("dummy:foo?contentCache=true"); + assertTrue(endpoint.isContentCache(), + "explicit contentCache=true on URI must override the component-level auto-flip"); + } finally { + main.stop(); + } + } + + static final class TestContentCacheComponent extends DefaultComponent { + + private boolean contentCache = true; + + public boolean isContentCache() { return contentCache; } - @Override - public void setContentCache(Boolean contentCache) { + public void setContentCache(boolean contentCache) { this.contentCache = contentCache; } @Override - protected Endpoint createEndpoint(String uri, String remaining, Map parameters) { + public PropertyConfigurer getComponentPropertyConfigurer() { + return new TestContentCacheComponentConfigurer(); + } + + @Override + protected Endpoint createEndpoint(String uri, String remaining, Map parameters) throws Exception { + TestContentCacheEndpoint endpoint = new TestContentCacheEndpoint(uri, this); + endpoint.setContentCache(contentCache); + setProperties(endpoint, parameters); + return endpoint; + } + } + + static final class TestContentCacheComponentConfigurer + extends PropertyConfigurerSupport + implements PropertyConfigurer, PropertyConfigurerGetter { + + @Override + public boolean configure(CamelContext camelContext, Object target, String name, Object value, boolean ignoreCase) { + if ("contentcache".equalsIgnoreCase(name)) { + ((TestContentCacheComponent) target).setContentCache(property(camelContext, boolean.class, value)); + return true; + } + return false; + } + + @Override + public Object getOptionValue(Object target, String name, boolean ignoreCase) { + if ("contentcache".equalsIgnoreCase(name)) { + return ((TestContentCacheComponent) target).isContentCache(); + } return null; } + + @Override + public Class getOptionType(String name, boolean ignoreCase) { + return "contentcache".equalsIgnoreCase(name) ? boolean.class : null; + } + } + + static final class TestContentCacheEndpoint extends DefaultEndpoint { + + private boolean contentCache; + + TestContentCacheEndpoint(String uri, TestContentCacheComponent component) { + super(uri, component); + } + + public boolean isContentCache() { + return contentCache; + } + + public void setContentCache(boolean contentCache) { + this.contentCache = contentCache; + } + + @Override + public Producer createProducer() { + throw new UnsupportedOperationException(); + } + + @Override + public Consumer createConsumer(Processor processor) { + throw new UnsupportedOperationException(); + } } } diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc index d1e3ccb2cc161..ffd96ad7aad7f 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc @@ -46,6 +46,12 @@ and dev consoles for nodes inside Choice EIP branches. The `camel wrapper` command now installs the scripts as `camel` instead of `camelw`. You can use the `--command-name=camelw` to use the old name. +When `camel.main.routesReloadEnabled=true` (automatically set by `camel run --dev`), Camel now +auto-disables `contentCache` on resource-based components (such as `xslt`) whose default is +`true`, so that edits to the resource file are picked up on the next message without restarting +the route. Set `camel.component..contentCache=true` (or pass `?contentCache=true` on the +URI) to opt back in to caching during dev mode. + === camel-yaml-dsl A new canonical JSON Schema variant (`camelYamlDsl-canonical.json`) has been added alongside the existing classic diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc index 1b59c5e72b66c..0fc137256bcc8 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc @@ -531,13 +531,13 @@ The following is not allowed: `camel run abc.java --source-dir=mycode`. ==== Live reload of resource files -When `--dev` is used, Camel also disables `contentCache` on resource-based components that -implement the `org.apache.camel.spi.ContentCacheAware` interface, so that edits to resource files -are picked up on the next message without having to restart the route. +When `--dev` is used, Camel also disables `contentCache` on resource-based components (such as +`xslt`) whose default is `true`, so that edits to resource files are picked up on the next message +without having to restart the route. -This is driven by `camel.main.routesReloadEnabled` (set automatically by `--dev`). Explicit user -settings on the component, endpoint or URI are always respected: set `contentCache=true` to keep -caching even in dev mode. +This is driven by `camel.main.routesReloadEnabled` (set automatically by `--dev`). User properties +(e.g. `camel.component.xslt.contentCache=true`) and explicit endpoint/URI settings are always +respected: set `contentCache=true` to keep caching even in dev mode. ==== Loading new routes into existing Camel diff --git a/docs/user-manual/modules/ROOT/pages/route-reload.adoc b/docs/user-manual/modules/ROOT/pages/route-reload.adoc index 5b1944b8414f8..ad80593a7b25b 100644 --- a/docs/user-manual/modules/ROOT/pages/route-reload.adoc +++ b/docs/user-manual/modules/ROOT/pages/route-reload.adoc @@ -73,12 +73,12 @@ And adding new routes is therefore possible as they would have a new unique rout === Live reload of resource files In addition to reloading routes, when `routesReloadEnabled=true` Camel also disables the -`contentCache` option on resource-based components that implement the -`org.apache.camel.spi.ContentCacheAware` interface. This way, edits to resource files are picked -up on the next message without having to restart the route. +`contentCache` option on resource-based components (such as `xslt`) whose default is `true`. This +way, edits to resource files are picked up on the next message without having to restart the +route. -Explicit user settings (configured on the component, endpoint, or URI) are always respected: set -`contentCache=true` to keep caching even when route reload is enabled. +User properties (e.g. `camel.component.xslt.contentCache=true`) and explicit endpoint/URI settings +are always respected: set `contentCache=true` to keep caching even when route reload is enabled. == See Also diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XjComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XjComponentBuilderFactory.java index 203d7631d8fc5..9291ba0ff2bfd 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XjComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XjComponentBuilderFactory.java @@ -79,7 +79,7 @@ default XjComponentBuilder allowTemplateFromHeader(boolean allowTemplateFromHead * can be forced to reload at runtime via JMX using the * clearCachedStylesheet operation. * - * The option is a: <code>java.lang.Boolean</code> type. + * The option is a: <code>boolean</code> type. * * Default: true * Group: producer @@ -87,7 +87,7 @@ default XjComponentBuilder allowTemplateFromHeader(boolean allowTemplateFromHead * @param contentCache the value to set * @return the dsl builder */ - default XjComponentBuilder contentCache(java.lang.Boolean contentCache) { + default XjComponentBuilder contentCache(boolean contentCache) { doSetProperty("contentCache", contentCache); return this; } @@ -312,7 +312,7 @@ protected boolean setPropertyOnComponent( Object value) { switch (name) { case "allowTemplateFromHeader": ((XJComponent) component).setAllowTemplateFromHeader((boolean) value); return true; - case "contentCache": ((XJComponent) component).setContentCache((java.lang.Boolean) value); return true; + case "contentCache": ((XJComponent) component).setContentCache((boolean) value); return true; case "lazyStartProducer": ((XJComponent) component).setLazyStartProducer((boolean) value); return true; case "autowiredEnabled": ((XJComponent) component).setAutowiredEnabled((boolean) value); return true; case "saxonConfiguration": ((XJComponent) component).setSaxonConfiguration((net.sf.saxon.Configuration) value); return true; diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltComponentBuilderFactory.java index ab067201d078a..6390d5d75578a 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltComponentBuilderFactory.java @@ -79,7 +79,7 @@ default XsltComponentBuilder allowTemplateFromHeader(boolean allowTemplateFromHe * can be forced to reload at runtime via JMX using the * clearCachedStylesheet operation. * - * The option is a: <code>java.lang.Boolean</code> type. + * The option is a: <code>boolean</code> type. * * Default: true * Group: producer @@ -87,7 +87,7 @@ default XsltComponentBuilder allowTemplateFromHeader(boolean allowTemplateFromHe * @param contentCache the value to set * @return the dsl builder */ - default XsltComponentBuilder contentCache(java.lang.Boolean contentCache) { + default XsltComponentBuilder contentCache(boolean contentCache) { doSetProperty("contentCache", contentCache); return this; } @@ -241,7 +241,7 @@ protected boolean setPropertyOnComponent( Object value) { switch (name) { case "allowTemplateFromHeader": ((XsltComponent) component).setAllowTemplateFromHeader((boolean) value); return true; - case "contentCache": ((XsltComponent) component).setContentCache((java.lang.Boolean) value); return true; + case "contentCache": ((XsltComponent) component).setContentCache((boolean) value); return true; case "lazyStartProducer": ((XsltComponent) component).setLazyStartProducer((boolean) value); return true; case "autowiredEnabled": ((XsltComponent) component).setAutowiredEnabled((boolean) value); return true; case "transformerFactoryClass": ((XsltComponent) component).setTransformerFactoryClass((java.lang.String) value); return true; diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltSaxonComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltSaxonComponentBuilderFactory.java index 75b04bb6a9dcc..9ae7c4cb9298c 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltSaxonComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/XsltSaxonComponentBuilderFactory.java @@ -79,7 +79,7 @@ default XsltSaxonComponentBuilder allowTemplateFromHeader(boolean allowTemplateF * can be forced to reload at runtime via JMX using the * clearCachedStylesheet operation. * - * The option is a: <code>java.lang.Boolean</code> type. + * The option is a: <code>boolean</code> type. * * Default: true * Group: producer @@ -87,7 +87,7 @@ default XsltSaxonComponentBuilder allowTemplateFromHeader(boolean allowTemplateF * @param contentCache the value to set * @return the dsl builder */ - default XsltSaxonComponentBuilder contentCache(java.lang.Boolean contentCache) { + default XsltSaxonComponentBuilder contentCache(boolean contentCache) { doSetProperty("contentCache", contentCache); return this; } @@ -312,7 +312,7 @@ protected boolean setPropertyOnComponent( Object value) { switch (name) { case "allowTemplateFromHeader": ((XsltSaxonComponent) component).setAllowTemplateFromHeader((boolean) value); return true; - case "contentCache": ((XsltSaxonComponent) component).setContentCache((java.lang.Boolean) value); return true; + case "contentCache": ((XsltSaxonComponent) component).setContentCache((boolean) value); return true; case "lazyStartProducer": ((XsltSaxonComponent) component).setLazyStartProducer((boolean) value); return true; case "autowiredEnabled": ((XsltSaxonComponent) component).setAutowiredEnabled((boolean) value); return true; case "saxonConfiguration": ((XsltSaxonComponent) component).setSaxonConfiguration((net.sf.saxon.Configuration) value); return true;