This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch backport/25366-to-camel-4.14.x in repository https://gitbox.apache.org/repos/asf/camel.git
commit 5ede7a824b68f150b6502dc546e480098a553be2 Author: Andrea Cosentino <[email protected]> AuthorDate: Thu Aug 6 12:57:46 2026 +0200 CAMEL-24359: camel-atmosphere-websocket - align Exchange header constant names with Camel naming convention Rename the Exchange header string values in WebsocketConstants from the dotted websocket.* prefix to the project-wide Camel<Component><Feature> convention documented in design/headers.adoc. The Java field names are unchanged, so code referencing the constants symbolically keeps working. Adds WebsocketConstantsTest covering the new values and asserting all five headers are filtered by the inherited HttpHeaderFilterStrategy. Includes a 4.22 upgrade-guide entry and regenerated catalog/DSL metadata. Closes #25366 Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> --- .../catalog/components/atmosphere-websocket.json | 8 +-- components/camel-atmosphere-websocket/pom.xml | 11 ++++ .../atmosphere/websocket/atmosphere-websocket.json | 8 +-- .../atmosphere/websocket/WebsocketConstants.java | 10 ++-- .../websocket/WebsocketConstantsTest.java | 63 ++++++++++++++++++++++ .../ROOT/pages/camel-4x-upgrade-guide-4_14.adoc | 61 +++++++++++++++++++++ .../AtmosphereWebsocketEndpointBuilderFactory.java | 26 ++++----- 7 files changed, 162 insertions(+), 25 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/atmosphere-websocket.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/atmosphere-websocket.json index 6ddc12147550..45f93c1fbaaa 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/atmosphere-websocket.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/atmosphere-websocket.json @@ -39,10 +39,10 @@ "headerFilterStrategy": { "index": 11, "kind": "property", "displayName": "Header Filter Strategy", "group": "filter", "label": "filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel message." } }, "headers": { - "websocket.connectionKey": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The connection key.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY" }, - "websocket.connectionKey.list": { "index": 1, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list of connection keys.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY_LIST" }, - "websocket.eventType": { "index": 2, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of event received. It can be ONOPEN_EVENT_TYPE, ONERROR_EVENT_TYPE or ONCLOSE_EVENT_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#EVENT_TYPE" }, - "websocket.errorType": { "index": 3, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of error that occurred. It can be MESSAGE_NOT_SENT_ERROR_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#ERROR_TYPE" } + "CamelAtmosphereWebsocketConnectionKey": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The connection key.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY" }, + "CamelAtmosphereWebsocketConnectionKeyList": { "index": 1, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list of connection keys.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY_LIST" }, + "CamelAtmosphereWebsocketEventType": { "index": 2, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of event received. It can be ONOPEN_EVENT_TYPE, ONERROR_EVENT_TYPE or ONCLOSE_EVENT_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#EVENT_TYPE" }, + "CamelAtmosphereWebsocketErrorType": { "index": 3, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of error that occurred. It can be MESSAGE_NOT_SENT_ERROR_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#ERROR_TYPE" } }, "properties": { "servicePath": { "index": 0, "kind": "path", "displayName": "Service Path", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of websocket endpoint" }, diff --git a/components/camel-atmosphere-websocket/pom.xml b/components/camel-atmosphere-websocket/pom.xml index cf269ed9ea5f..f176cfe2f9e9 100644 --- a/components/camel-atmosphere-websocket/pom.xml +++ b/components/camel-atmosphere-websocket/pom.xml @@ -71,6 +71,17 @@ <version>${mockito-version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <version>${awaitility-version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> <!-- test infra --> <dependency> diff --git a/components/camel-atmosphere-websocket/src/generated/resources/META-INF/org/apache/camel/component/atmosphere/websocket/atmosphere-websocket.json b/components/camel-atmosphere-websocket/src/generated/resources/META-INF/org/apache/camel/component/atmosphere/websocket/atmosphere-websocket.json index 6ddc12147550..45f93c1fbaaa 100644 --- a/components/camel-atmosphere-websocket/src/generated/resources/META-INF/org/apache/camel/component/atmosphere/websocket/atmosphere-websocket.json +++ b/components/camel-atmosphere-websocket/src/generated/resources/META-INF/org/apache/camel/component/atmosphere/websocket/atmosphere-websocket.json @@ -39,10 +39,10 @@ "headerFilterStrategy": { "index": 11, "kind": "property", "displayName": "Header Filter Strategy", "group": "filter", "label": "filter", "required": false, "type": "object", "javaType": "org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel message." } }, "headers": { - "websocket.connectionKey": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The connection key.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY" }, - "websocket.connectionKey.list": { "index": 1, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list of connection keys.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY_LIST" }, - "websocket.eventType": { "index": 2, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of event received. It can be ONOPEN_EVENT_TYPE, ONERROR_EVENT_TYPE or ONCLOSE_EVENT_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#EVENT_TYPE" }, - "websocket.errorType": { "index": 3, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of error that occurred. It can be MESSAGE_NOT_SENT_ERROR_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#ERROR_TYPE" } + "CamelAtmosphereWebsocketConnectionKey": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The connection key.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY" }, + "CamelAtmosphereWebsocketConnectionKeyList": { "index": 1, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list of connection keys.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY_LIST" }, + "CamelAtmosphereWebsocketEventType": { "index": 2, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of event received. It can be ONOPEN_EVENT_TYPE, ONERROR_EVENT_TYPE or ONCLOSE_EVENT_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#EVENT_TYPE" }, + "CamelAtmosphereWebsocketErrorType": { "index": 3, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of error that occurred. It can be MESSAGE_NOT_SENT_ERROR_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#ERROR_TYPE" } }, "properties": { "servicePath": { "index": 0, "kind": "path", "displayName": "Service Path", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of websocket endpoint" }, diff --git a/components/camel-atmosphere-websocket/src/main/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstants.java b/components/camel-atmosphere-websocket/src/main/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstants.java index dadeb529e12e..a283346ffca6 100644 --- a/components/camel-atmosphere-websocket/src/main/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstants.java +++ b/components/camel-atmosphere-websocket/src/main/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstants.java @@ -24,17 +24,17 @@ import org.apache.camel.spi.Metadata; public final class WebsocketConstants { @Metadata(description = "The connection key.", javaType = "java.lang.String") - public static final String CONNECTION_KEY = "websocket.connectionKey"; + public static final String CONNECTION_KEY = "CamelAtmosphereWebsocketConnectionKey"; @Metadata(description = "The list of connection keys.", javaType = "java.util.List") - public static final String CONNECTION_KEY_LIST = "websocket.connectionKey.list"; - public static final String SEND_TO_ALL = "websocket.sendToAll"; + public static final String CONNECTION_KEY_LIST = "CamelAtmosphereWebsocketConnectionKeyList"; + public static final String SEND_TO_ALL = "CamelAtmosphereWebsocketSendToAll"; @Metadata(label = "consumer", description = "The type of event received. It can be `ONOPEN_EVENT_TYPE`, `ONERROR_EVENT_TYPE` or `ONCLOSE_EVENT_TYPE`.", javaType = "int") - public static final String EVENT_TYPE = "websocket.eventType"; + public static final String EVENT_TYPE = "CamelAtmosphereWebsocketEventType"; @Metadata(label = "consumer", description = "The type of error that occurred. It can be `MESSAGE_NOT_SENT_ERROR_TYPE`.", javaType = "int") - public static final String ERROR_TYPE = "websocket.errorType"; + public static final String ERROR_TYPE = "CamelAtmosphereWebsocketErrorType"; public static final int ONOPEN_EVENT_TYPE = 1; public static final int ONCLOSE_EVENT_TYPE = 0; diff --git a/components/camel-atmosphere-websocket/src/test/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstantsTest.java b/components/camel-atmosphere-websocket/src/test/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstantsTest.java new file mode 100644 index 000000000000..cdd27b93792a --- /dev/null +++ b/components/camel-atmosphere-websocket/src/test/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstantsTest.java @@ -0,0 +1,63 @@ +/* + * 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.atmosphere.websocket; + +import org.apache.camel.http.base.HttpHeaderFilterStrategy; +import org.apache.camel.spi.HeaderFilterStrategy; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Verifies that the Exchange header constants follow the {@code Camel<Component><Feature>} naming convention, and + * therefore sit inside the {@code Camel} namespace that the inherited {@link HttpHeaderFilterStrategy} handles. + */ +class WebsocketConstantsTest { + + private static final String[] HEADERS = { + WebsocketConstants.CONNECTION_KEY, + WebsocketConstants.CONNECTION_KEY_LIST, + WebsocketConstants.SEND_TO_ALL, + WebsocketConstants.EVENT_TYPE, + WebsocketConstants.ERROR_TYPE + }; + + private final HeaderFilterStrategy strategy = new HttpHeaderFilterStrategy(); + + @Test + void testHeaderNamesFollowCamelNamingConvention() { + assertThat(WebsocketConstants.CONNECTION_KEY).isEqualTo("CamelAtmosphereWebsocketConnectionKey"); + assertThat(WebsocketConstants.CONNECTION_KEY_LIST).isEqualTo("CamelAtmosphereWebsocketConnectionKeyList"); + assertThat(WebsocketConstants.SEND_TO_ALL).isEqualTo("CamelAtmosphereWebsocketSendToAll"); + assertThat(WebsocketConstants.EVENT_TYPE).isEqualTo("CamelAtmosphereWebsocketEventType"); + assertThat(WebsocketConstants.ERROR_TYPE).isEqualTo("CamelAtmosphereWebsocketErrorType"); + } + + @Test + void testHeadersAreFilteredByInheritedHttpHeaderFilterStrategy() { + // WebsocketEndpoint extends ServletEndpoint, so it inherits HttpHeaderFilterStrategy, which filters + // the Camel namespace case-insensitively in both directions + for (String header : HEADERS) { + assertThat(strategy.applyFilterToExternalHeaders(header, "aValue", null)) + .as("%s should be filtered when mapping external headers in", header) + .isTrue(); + assertThat(strategy.applyFilterToCamelHeaders(header, "aValue", null)) + .as("%s should be filtered when mapping Camel headers out", header) + .isTrue(); + } + } +} diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_14.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_14.adoc index 146640138815..3c6b22f5727d 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_14.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_14.adoc @@ -4,6 +4,67 @@ This document is for helping you upgrade your Apache Camel application from Camel 4.x to 4.y. For example, if you are upgrading Camel 4.0 to 4.2, then you should follow the guides from both 4.0 to 4.1 and 4.1 to 4.2. +== Upgrading from 4.14.8 to 4.14.9 + +=== camel-atmosphere-websocket - potential breaking change + +The Exchange header constants in `WebsocketConstants` have been renamed to follow the +Camel naming convention used across the rest of the component catalog. The Java field +names are unchanged; only the header string values have changed: + +[options="header"] +|=== +| Constant | Previous value | New value +| `WebsocketConstants.CONNECTION_KEY` | `websocket.connectionKey` | `CamelAtmosphereWebsocketConnectionKey` +| `WebsocketConstants.CONNECTION_KEY_LIST` | `websocket.connectionKey.list` | `CamelAtmosphereWebsocketConnectionKeyList` +| `WebsocketConstants.SEND_TO_ALL` | `websocket.sendToAll` | `CamelAtmosphereWebsocketSendToAll` +| `WebsocketConstants.EVENT_TYPE` | `websocket.eventType` | `CamelAtmosphereWebsocketEventType` +| `WebsocketConstants.ERROR_TYPE` | `websocket.errorType` | `CamelAtmosphereWebsocketErrorType` +|=== + +Routes that reference the constant symbolically (for example +`setHeader(WebsocketConstants.CONNECTION_KEY, ...)`) continue to work without changes. +Routes that set the header by its literal string value must be updated to use the new +value: + +._Java-only: updated atmosphere-websocket header names in route_ +[source,java] +---- +// before +from("direct:next") + .setHeader("websocket.connectionKey", header("myKey")) + .to("atmosphere-websocket:///servicepath"); + +// after +from("direct:next") + .setHeader("CamelAtmosphereWebsocketConnectionKey", header("myKey")) + .to("atmosphere-websocket:///servicepath"); +---- + +The sibling websocket components are not affected by this change: `camel-undertow` +keeps the `websocket.*` values in `UndertowConstants` (as documented in the 4.18 +upgrade guide), and `camel-vertx-websocket` keeps its `CamelVertxWebsocket.*` values. + +Note that `WebsocketConstants.SEND_TO_ALL` is not read by this component. Broadcast is +selected through the `sendToAll` endpoint option, not through the header; the constant +is renamed here only to keep the class internally consistent. + +==== Behaviour change: cross-transport propagation of the dispatch headers + +Because the renamed header values now begin with `Camel`, they are filtered by the +standard transport `HeaderFilterStrategy` (`HttpHeaderFilterStrategy`, +`JmsHeaderFilterStrategy`, etc.) when crossing a transport boundary, by design — +`Camel*` headers are framework-internal and are not propagated over the wire. + +Routes that bridge an external transport (HTTP, JMS, ...) into an +`atmosphere-websocket:` producer and let the sender choose the dispatch target via +headers must therefore carry those values in non-`Camel`-prefixed application headers +and map them to the corresponding `WebsocketConstants` value in the route between the +transport `from` and the `atmosphere-websocket:` `to`. Allowing untrusted senders to +drive `WebsocketConstants.CONNECTION_KEY_LIST`, which selects the target peers and +takes precedence over `CONNECTION_KEY`, without such a mapping step is not the intended +use of the component. + == Upgrading from 4.14.7 to 4.14.8 === camel-spring-ws - potential breaking change diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AtmosphereWebsocketEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AtmosphereWebsocketEndpointBuilderFactory.java index 8ee72b517e7d..c917839012ed 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AtmosphereWebsocketEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AtmosphereWebsocketEndpointBuilderFactory.java @@ -1606,10 +1606,11 @@ public interface AtmosphereWebsocketEndpointBuilderFactory { * * Group: common * - * @return the name of the header {@code websocket.connectionKey}. + * @return the name of the header {@code + * AtmosphereWebsocketConnectionKey}. */ - public String websocketConnectionkey() { - return "websocket.connectionKey"; + public String atmosphereWebsocketConnectionKey() { + return "CamelAtmosphereWebsocketConnectionKey"; } /** * The list of connection keys. @@ -1618,10 +1619,11 @@ public interface AtmosphereWebsocketEndpointBuilderFactory { * * Group: common * - * @return the name of the header {@code websocket.connectionKey.list}. + * @return the name of the header {@code + * AtmosphereWebsocketConnectionKeyList}. */ - public String websocketConnectionkeyList() { - return "websocket.connectionKey.list"; + public String atmosphereWebsocketConnectionKeyList() { + return "CamelAtmosphereWebsocketConnectionKeyList"; } /** * The type of event received. It can be ONOPEN_EVENT_TYPE, @@ -1631,10 +1633,10 @@ public interface AtmosphereWebsocketEndpointBuilderFactory { * * Group: consumer * - * @return the name of the header {@code websocket.eventType}. + * @return the name of the header {@code AtmosphereWebsocketEventType}. */ - public String websocketEventtype() { - return "websocket.eventType"; + public String atmosphereWebsocketEventType() { + return "CamelAtmosphereWebsocketEventType"; } /** * The type of error that occurred. It can be @@ -1644,10 +1646,10 @@ public interface AtmosphereWebsocketEndpointBuilderFactory { * * Group: consumer * - * @return the name of the header {@code websocket.errorType}. + * @return the name of the header {@code AtmosphereWebsocketErrorType}. */ - public String websocketErrortype() { - return "websocket.errorType"; + public String atmosphereWebsocketErrorType() { + return "CamelAtmosphereWebsocketErrorType"; } } static AtmosphereWebsocketEndpointBuilder endpointBuilder(String componentName, String path) {
