This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-25978-controlbus-allowed-languages in repository https://gitbox.apache.org/repos/asf/camel.git
commit 34794467d9a943cc3243299ef3417d564e019d74 Author: Claus Ibsen <[email protected]> AuthorDate: Thu Nov 12 20:10:22 2026 +0100 CAMEL-25978: camel-controlbus - the language command is off by default, with an option that lists the languages it may use The language command (controlbus:language:<name>) evaluates the message body as an expression of the named language, and it worked for every language on the classpath without anything turning it on. New option allowedLanguages on the component and the endpoint: the names of the languages the language command may use, separated by comma. It is empty by default, so a controlbus:language endpoint whose language is not listed fails when it is created, with an error that names the option. The endpoint list takes precedence over the component list. The route command is not affected. Docs show the option in the language examples, and the changed default is in the 4.24 upgrade guide. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../camel/catalog/components/controlbus.json | 6 +- .../camel/catalog/docs/controlbus-component.adoc | 26 +++++++- .../controlbus/ControlBusComponentConfigurer.java | 6 ++ .../controlbus/ControlBusEndpointConfigurer.java | 6 ++ .../controlbus/ControlBusEndpointUriFactory.java | 3 +- .../camel/component/controlbus/controlbus.json | 6 +- .../src/main/docs/controlbus-component.adoc | 26 +++++++- .../component/controlbus/ControlBusComponent.java | 40 ++++++++++- .../component/controlbus/ControlBusEndpoint.java | 16 +++++ .../controlbus/ControlBusLanguageAllowedTest.java | 78 ++++++++++++++++++++++ .../ControlBusLanguageSimpleStartRouteTest.java | 10 ++- .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 12 ++++ .../dsl/ControlbusComponentBuilderFactory.java | 21 ++++++ .../dsl/ControlBusEndpointBuilderFactory.java | 19 ++++++ 14 files changed, 264 insertions(+), 11 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/controlbus.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/controlbus.json index 63dda925923f..2c0db8d547b4 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/controlbus.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/controlbus.json @@ -25,7 +25,8 @@ }, "componentProperties": { "lazyStartProducer": { "index": 0, "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 [...] - "autowiredEnabled": { "index": 1, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...] + "autowiredEnabled": { "index": 1, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...] + "allowedLanguages": { "index": 2, "kind": "property", "displayName": "Allowed Languages", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The names of the languages the language command may use, separated by comma, such as simple. The language command evaluates the message body as an expression, so it is off until the language is in this list: a con [...] }, "bodyTypes": { "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": "command", "value": "language", "carriers": [ "text", "bytes", "stream", "file" ] } ], "description": "The route actions do not read the body. The language command evaluates the body as its expression, as text" }, @@ -39,6 +40,7 @@ "loggingLevel": { "index": 4, "kind": "parameter", "displayName": "Logging Level", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "INFO", "description": "Logging level used for logging when task is done, or if any exceptions occurred during processing the task." }, "restartDelay": { "index": 5, "kind": "parameter", "displayName": "Restart Delay", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "The delay in millis to use when restarting a route." }, "routeId": { "index": 6, "kind": "parameter", "displayName": "Route Id", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To specify a route by its id. The special keyword current indicates the current route." }, - "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "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 produc [...] + "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "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 produc [...] + "allowedLanguages": { "index": 8, "kind": "parameter", "displayName": "Allowed Languages", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The names of the languages the language command may use, separated by comma, such as simple. The language command evaluates the message body as an expression, so it is off until the language is in this list: a co [...] } } diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/controlbus-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/controlbus-component.adoc index 05f6af24b0ce..e691bba99b5e 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/controlbus-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/controlbus-component.adoc @@ -180,7 +180,29 @@ String xml = template.requestBody("controlbus:route?action=stats", null, String. === Using Simple language You can use the xref:languages:simple-language.adoc[Simple] language with the control bus. -For example, to stop a specific route, you can send a message to the `"controlbus:language:simple"` endpoint containing the following message: + +The `language` command evaluates the message body as an expression, so it is off by default. +To turn it on, list the languages it may use in the `allowedLanguages` option, separated by comma. +Set it on the component to apply to every endpoint: + +[source,properties] +---- +camel.component.controlbus.allowed-languages = simple +---- + +Or set it on the endpoint itself, which takes precedence over the list of the component: + +[source,text] +---- +controlbus:language:simple?allowedLanguages=simple +---- + +A `controlbus:language` endpoint whose language is not listed fails when it is created, with an error that names the `allowedLanguages` option. +The `route` command does not need this option. + +IMPORTANT: The body is evaluated as an expression with the full power of the language: only send it text that comes from the route, never the content of a message from an untrusted sender. + +With `simple` allowed on the component, to stop a specific route, you can send a message to the `"controlbus:language:simple"` endpoint containing the following message: ._Java-only: Java test API (ProducerTemplate)_ [source,java] @@ -216,5 +238,5 @@ message we sent to the control bus component. [TIP] ==== -You can also use other languages such as xref:languages:groovy-language.adoc[Groovy], etc. +You can also use other languages such as xref:languages:groovy-language.adoc[Groovy], etc., when they are listed in the `allowedLanguages` option. ==== diff --git a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusComponentConfigurer.java b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusComponentConfigurer.java index 0cef7a0fe01d..b06ee6bd19f4 100644 --- a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusComponentConfigurer.java +++ b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusComponentConfigurer.java @@ -24,6 +24,8 @@ public class ControlBusComponentConfigurer extends PropertyConfigurerSupport imp public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { ControlBusComponent target = (ControlBusComponent) obj; switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) { + case "allowedlanguages": + case "allowedLanguages": target.setAllowedLanguages(property(camelContext, java.lang.String.class, value)); return true; case "autowiredenabled": case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true; case "lazystartproducer": @@ -35,6 +37,8 @@ public class ControlBusComponentConfigurer extends PropertyConfigurerSupport imp @Override public Class<?> getOptionType(String name, boolean ignoreCase) { switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) { + case "allowedlanguages": + case "allowedLanguages": return java.lang.String.class; case "autowiredenabled": case "autowiredEnabled": return boolean.class; case "lazystartproducer": @@ -47,6 +51,8 @@ public class ControlBusComponentConfigurer extends PropertyConfigurerSupport imp public Object getOptionValue(Object obj, String name, boolean ignoreCase) { ControlBusComponent target = (ControlBusComponent) obj; switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) { + case "allowedlanguages": + case "allowedLanguages": return target.getAllowedLanguages(); case "autowiredenabled": case "autowiredEnabled": return target.isAutowiredEnabled(); case "lazystartproducer": diff --git a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointConfigurer.java b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointConfigurer.java index 93dba1587fe4..47cb752b4a8b 100644 --- a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointConfigurer.java +++ b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointConfigurer.java @@ -25,6 +25,8 @@ public class ControlBusEndpointConfigurer extends PropertyConfigurerSupport impl ControlBusEndpoint target = (ControlBusEndpoint) obj; switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) { case "action": target.setAction(property(camelContext, java.lang.String.class, value)); return true; + case "allowedlanguages": + case "allowedLanguages": target.setAllowedLanguages(property(camelContext, java.lang.String.class, value)); return true; case "async": target.setAsync(property(camelContext, boolean.class, value)); return true; case "lazystartproducer": case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; @@ -42,6 +44,8 @@ public class ControlBusEndpointConfigurer extends PropertyConfigurerSupport impl public Class<?> getOptionType(String name, boolean ignoreCase) { switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) { case "action": return java.lang.String.class; + case "allowedlanguages": + case "allowedLanguages": return java.lang.String.class; case "async": return boolean.class; case "lazystartproducer": case "lazyStartProducer": return boolean.class; @@ -60,6 +64,8 @@ public class ControlBusEndpointConfigurer extends PropertyConfigurerSupport impl ControlBusEndpoint target = (ControlBusEndpoint) obj; switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) { case "action": return target.getAction(); + case "allowedlanguages": + case "allowedLanguages": return target.getAllowedLanguages(); case "async": return target.isAsync(); case "lazystartproducer": case "lazyStartProducer": return target.isLazyStartProducer(); diff --git a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointUriFactory.java b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointUriFactory.java index 41ba4fbd1e87..9c27494504bc 100644 --- a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointUriFactory.java +++ b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointUriFactory.java @@ -24,8 +24,9 @@ public class ControlBusEndpointUriFactory extends org.apache.camel.support.compo private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES; private static final Map<String, String> MULTI_VALUE_PREFIXES; static { - Set<String> props = new HashSet<>(8); + Set<String> props = new HashSet<>(9); props.add("action"); + props.add("allowedLanguages"); props.add("async"); props.add("command"); props.add("language"); diff --git a/components/camel-controlbus/src/generated/resources/META-INF/org/apache/camel/component/controlbus/controlbus.json b/components/camel-controlbus/src/generated/resources/META-INF/org/apache/camel/component/controlbus/controlbus.json index 63dda925923f..2c0db8d547b4 100644 --- a/components/camel-controlbus/src/generated/resources/META-INF/org/apache/camel/component/controlbus/controlbus.json +++ b/components/camel-controlbus/src/generated/resources/META-INF/org/apache/camel/component/controlbus/controlbus.json @@ -25,7 +25,8 @@ }, "componentProperties": { "lazyStartProducer": { "index": 0, "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 [...] - "autowiredEnabled": { "index": 1, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...] + "autowiredEnabled": { "index": 1, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...] + "allowedLanguages": { "index": 2, "kind": "property", "displayName": "Allowed Languages", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The names of the languages the language command may use, separated by comma, such as simple. The language command evaluates the message body as an expression, so it is off until the language is in this list: a con [...] }, "bodyTypes": { "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": "command", "value": "language", "carriers": [ "text", "bytes", "stream", "file" ] } ], "description": "The route actions do not read the body. The language command evaluates the body as its expression, as text" }, @@ -39,6 +40,7 @@ "loggingLevel": { "index": 4, "kind": "parameter", "displayName": "Logging Level", "group": "producer", "label": "", "required": false, "type": "enum", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false, "defaultValue": "INFO", "description": "Logging level used for logging when task is done, or if any exceptions occurred during processing the task." }, "restartDelay": { "index": 5, "kind": "parameter", "displayName": "Restart Delay", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 1000, "description": "The delay in millis to use when restarting a route." }, "routeId": { "index": 6, "kind": "parameter", "displayName": "Route Id", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "To specify a route by its id. The special keyword current indicates the current route." }, - "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "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 produc [...] + "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "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 produc [...] + "allowedLanguages": { "index": 8, "kind": "parameter", "displayName": "Allowed Languages", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The names of the languages the language command may use, separated by comma, such as simple. The language command evaluates the message body as an expression, so it is off until the language is in this list: a co [...] } } diff --git a/components/camel-controlbus/src/main/docs/controlbus-component.adoc b/components/camel-controlbus/src/main/docs/controlbus-component.adoc index 05f6af24b0ce..e691bba99b5e 100644 --- a/components/camel-controlbus/src/main/docs/controlbus-component.adoc +++ b/components/camel-controlbus/src/main/docs/controlbus-component.adoc @@ -180,7 +180,29 @@ String xml = template.requestBody("controlbus:route?action=stats", null, String. === Using Simple language You can use the xref:languages:simple-language.adoc[Simple] language with the control bus. -For example, to stop a specific route, you can send a message to the `"controlbus:language:simple"` endpoint containing the following message: + +The `language` command evaluates the message body as an expression, so it is off by default. +To turn it on, list the languages it may use in the `allowedLanguages` option, separated by comma. +Set it on the component to apply to every endpoint: + +[source,properties] +---- +camel.component.controlbus.allowed-languages = simple +---- + +Or set it on the endpoint itself, which takes precedence over the list of the component: + +[source,text] +---- +controlbus:language:simple?allowedLanguages=simple +---- + +A `controlbus:language` endpoint whose language is not listed fails when it is created, with an error that names the `allowedLanguages` option. +The `route` command does not need this option. + +IMPORTANT: The body is evaluated as an expression with the full power of the language: only send it text that comes from the route, never the content of a message from an untrusted sender. + +With `simple` allowed on the component, to stop a specific route, you can send a message to the `"controlbus:language:simple"` endpoint containing the following message: ._Java-only: Java test API (ProducerTemplate)_ [source,java] @@ -216,5 +238,5 @@ message we sent to the control bus component. [TIP] ==== -You can also use other languages such as xref:languages:groovy-language.adoc[Groovy], etc. +You can also use other languages such as xref:languages:groovy-language.adoc[Groovy], etc., when they are listed in the `allowedLanguages` option. ==== diff --git a/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusComponent.java b/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusComponent.java index 592cd4f3ca36..fbcf01850955 100644 --- a/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusComponent.java +++ b/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusComponent.java @@ -20,8 +20,10 @@ import java.util.Map; import java.util.concurrent.ExecutorService; import org.apache.camel.Endpoint; +import org.apache.camel.spi.Metadata; import org.apache.camel.spi.annotations.Component; import org.apache.camel.support.DefaultComponent; +import org.apache.camel.util.ObjectHelper; /** * The <a href="http://camel.apache.org/controlbus.html">Control Bus component</a> allows sending messages to a @@ -30,25 +32,61 @@ import org.apache.camel.support.DefaultComponent; @Component("controlbus") public class ControlBusComponent extends DefaultComponent { + @Metadata(label = "security") + private String allowedLanguages; + private ExecutorService executorService; public ControlBusComponent() { } + public String getAllowedLanguages() { + return allowedLanguages; + } + + /** + * The names of the languages the language command may use, separated by comma, such as simple. The language command + * evaluates the message body as an expression, so it is off until the language is in this list: a + * controlbus:language endpoint whose language is not listed fails when it is created. The route actions are not + * affected. An endpoint can set its own list. + */ + public void setAllowedLanguages(String allowedLanguages) { + this.allowedLanguages = allowedLanguages; + } + @Override protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { ControlBusEndpoint answer = new ControlBusEndpoint(uri, this); + answer.setAllowedLanguages(allowedLanguages); + setProperties(answer, parameters); // does the control bus use a language if (remaining != null && remaining.startsWith("language:")) { String lan = remaining.substring(9); + if (!isAllowedLanguage(answer.getAllowedLanguages(), lan)) { + throw new IllegalArgumentException( + "The language command of the control bus may not use the " + lan + " language: add " + lan + + " to the allowedLanguages option of the controlbus component or" + + " endpoint to turn it on"); + } answer.setLanguage(getCamelContext().resolveLanguage(lan)); } - setProperties(answer, parameters); return answer; } + private static boolean isAllowedLanguage(String allowedLanguages, String language) { + if (ObjectHelper.isEmpty(allowedLanguages) || ObjectHelper.isEmpty(language)) { + return false; + } + for (String name : allowedLanguages.split(",")) { + if (name.trim().equals(language)) { + return true; + } + } + return false; + } + ExecutorService getExecutorService() { lock.lock(); try { diff --git a/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusEndpoint.java b/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusEndpoint.java index c15c643a35b3..9b7ca3b779bc 100644 --- a/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusEndpoint.java +++ b/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusEndpoint.java @@ -78,6 +78,8 @@ public class ControlBusEndpoint extends DefaultEndpoint { private boolean async; @UriParam(defaultValue = "INFO") private LoggingLevel loggingLevel = LoggingLevel.INFO; + @UriParam(label = "security") + private String allowedLanguages; private transient CamelLogger logger; @@ -185,4 +187,18 @@ public class ControlBusEndpoint extends DefaultEndpoint { public void setLoggingLevel(LoggingLevel loggingLevel) { this.loggingLevel = loggingLevel; } + + public String getAllowedLanguages() { + return allowedLanguages; + } + + /** + * The names of the languages the language command may use, separated by comma, such as simple. The language command + * evaluates the message body as an expression, so it is off until the language is in this list: a + * controlbus:language endpoint whose language is not listed fails when it is created. The route actions are not + * affected. When not set, the list of the component is used. + */ + public void setAllowedLanguages(String allowedLanguages) { + this.allowedLanguages = allowedLanguages; + } } diff --git a/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageAllowedTest.java b/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageAllowedTest.java new file mode 100644 index 000000000000..81c186c86179 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageAllowedTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.controlbus; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.ResolveEndpointFailedException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The language command of the control bus is off until its language is in the allowedLanguages option. + */ +public class ControlBusLanguageAllowedTest extends ContextTestSupport { + + @Test + public void testLanguageOffByDefault() { + Exception e = assertThrows(Exception.class, + () -> context.getEndpoint("controlbus:language:simple")); + assertAllowedLanguagesError(e, "simple"); + } + + @Test + public void testLanguageNotInList() { + Exception e = assertThrows(Exception.class, + () -> context.getEndpoint("controlbus:language:simple?allowedLanguages=groovy")); + assertAllowedLanguagesError(e, "simple"); + } + + @Test + public void testLanguageInEndpointList() { + String name = template.requestBody("controlbus:language:simple?allowedLanguages=groovy, simple", + "${camelContext.name}", String.class); + assertEquals(context.getName(), name); + } + + @Test + public void testLanguageInComponentList() { + context.getComponent("controlbus", ControlBusComponent.class).setAllowedLanguages("simple"); + + String name = template.requestBody("controlbus:language:simple", "${camelContext.name}", String.class); + assertEquals(context.getName(), name); + } + + @Test + public void testEndpointListOverridesComponentList() { + context.getComponent("controlbus", ControlBusComponent.class).setAllowedLanguages("simple"); + + Exception e = assertThrows(Exception.class, + () -> context.getEndpoint("controlbus:language:simple?allowedLanguages=constant")); + assertAllowedLanguagesError(e, "simple"); + } + + private static void assertAllowedLanguagesError(Exception e, String language) { + ResolveEndpointFailedException re = assertInstanceOf(ResolveEndpointFailedException.class, e); + Throwable cause = re.getCause(); + assertInstanceOf(IllegalArgumentException.class, cause); + assertTrue(cause.getMessage().contains("allowedLanguages"), cause.getMessage()); + assertTrue(cause.getMessage().contains(" " + language + " "), cause.getMessage()); + } +} diff --git a/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageSimpleStartRouteTest.java b/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageSimpleStartRouteTest.java index d6f81414b311..4e5a1aba6dd4 100644 --- a/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageSimpleStartRouteTest.java +++ b/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageSimpleStartRouteTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.component.controlbus; +import org.apache.camel.CamelContext; import org.apache.camel.ContextTestSupport; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; @@ -23,7 +24,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** - * + * The language command, turned on for simple with the allowedLanguages option of the component. */ public class ControlBusLanguageSimpleStartRouteTest extends ContextTestSupport { @@ -62,6 +63,13 @@ public class ControlBusLanguageSimpleStartRouteTest extends ContextTestSupport { assertEquals("Started", status); } + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext context = super.createCamelContext(); + context.getComponent("controlbus", ControlBusComponent.class).setAllowedLanguages("simple"); + return context; + } + @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc index 93adc90a66f1..1bceea3a2c07 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc @@ -13,6 +13,18 @@ See the xref:camel-upgrade-recipes-tool.adoc[documentation] page for details. == Upgrading Camel 4.23 to 4.24 +=== camel-controlbus - the language command is off by default + +The `language` command of the control bus (`controlbus:language:<name>`) evaluates the message body as an expression of the named language. +It is now off by default: a `controlbus:language` endpoint fails when it is created unless its language is listed in the new `allowedLanguages` option, on the component or on the endpoint: + +[source,properties] +---- +camel.component.controlbus.allowed-languages = simple +---- + +The `route` command (`controlbus:route?action=...`) is not affected. + === camel-observability-services - promoted to Stable The `camel-observability-services` component has been promoted from _Preview_ to _Stable_ support level. diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ControlbusComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ControlbusComponentBuilderFactory.java index 61c69d7dbe84..d185bc06feb2 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ControlbusComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ControlbusComponentBuilderFactory.java @@ -96,6 +96,26 @@ public interface ControlbusComponentBuilderFactory { doSetProperty("autowiredEnabled", autowiredEnabled); return this; } + + /** + * The names of the languages the language command may use, separated by + * comma, such as simple. The language command evaluates the message + * body as an expression, so it is off until the language is in this + * list: a controlbus:language endpoint whose language is not listed + * fails when it is created. The route actions are not affected. An + * endpoint can set its own list. + * + * The option is a: <code>java.lang.String</code> type. + * + * Group: security + * + * @param allowedLanguages the value to set + * @return the dsl builder + */ + default ControlbusComponentBuilder allowedLanguages(java.lang.String allowedLanguages) { + doSetProperty("allowedLanguages", allowedLanguages); + return this; + } } class ControlbusComponentBuilderImpl @@ -113,6 +133,7 @@ public interface ControlbusComponentBuilderFactory { switch (name) { case "lazyStartProducer": ((ControlBusComponent) component).setLazyStartProducer((boolean) value); return true; case "autowiredEnabled": ((ControlBusComponent) component).setAutowiredEnabled((boolean) value); return true; + case "allowedLanguages": ((ControlBusComponent) component).setAllowedLanguages((java.lang.String) value); return true; default: return false; } } diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ControlBusEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ControlBusEndpointBuilderFactory.java index 4ab4b21fa479..480ec18aa0bb 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ControlBusEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ControlBusEndpointBuilderFactory.java @@ -178,6 +178,25 @@ public interface ControlBusEndpointBuilderFactory { doSetProperty("routeId", routeId); return this; } + /** + * The names of the languages the language command may use, separated by + * comma, such as simple. The language command evaluates the message + * body as an expression, so it is off until the language is in this + * list: a controlbus:language endpoint whose language is not listed + * fails when it is created. The route actions are not affected. When + * not set, the list of the component is used. + * + * The option is a: <code>java.lang.String</code> type. + * + * Group: security + * + * @param allowedLanguages the value to set + * @return the dsl builder + */ + default ControlBusEndpointBuilder allowedLanguages(String allowedLanguages) { + doSetProperty("allowedLanguages", allowedLanguages); + return this; + } } /**
