This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new cb73720780f9 CAMEL-25918: camel-as2 - consumers sharing a port use
their own asynchronous MDN settings (#28274)
cb73720780f9 is described below
commit cb73720780f9ef73d8dd753b92ad45dcf2a1502b
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Oct 23 12:51:29 2026 +0200
CAMEL-25918: camel-as2 - consumers sharing a port use their own
asynchronous MDN settings (#28274)
AS2 consumers that share a serverPortNumber delivered their asynchronous
MDNs with the mdnUserName, mdnPassword, mdnAccessToken and
asyncMdnAllowedHosts of the first endpoint created on the port. The MDN
settings are now part of the per-path AS2ConsumerConfiguration: each
consumer registers its own values for its own requestUriPattern, also for
the default "/" pattern, and the asynchronous MDN of a request uses the
settings of the matched path.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Signed-off-by: Andrea Cosentino <[email protected]>
---
.../org/apache/camel/catalog/components/as2.json | 8 +-
.../apache/camel/catalog/docs/as2-component.adoc | 8 +
.../component/as2/api/AS2ServerConnection.java | 90 ++++++++---
.../AS2ConsumerConfigurationMdnSettingsTest.java | 91 +++++++++++
.../org/apache/camel/component/as2/as2.json | 8 +-
.../src/main/docs/as2-component.adoc | 8 +
.../camel/component/as2/AS2Configuration.java | 13 +-
.../apache/camel/component/as2/AS2Consumer.java | 28 ++--
.../AS2ServerTwoConsumerAsyncMdnSettingsTest.java | 170 +++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 15 ++
.../endpoint/dsl/AS2EndpointBuilderFactory.java | 42 +++--
11 files changed, 429 insertions(+), 52 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json
index b348e9105ce7..989fb9a25f74 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json
@@ -68,14 +68,14 @@
"expectContinue": { "index": 32, "kind": "parameter", "displayName":
"Expect Continue", "group": "producer", "label": "producer", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Controls whether the Expect: 100-Continue
header is included in outbound AS2 messages. W [...]
"lazyStartProducer": { "index": 33, "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 produ [...]
"accessToken": { "index": 34, "kind": "parameter", "displayName": "Access
Token", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the client
for bearer authentication." },
- "asyncMdnAllowedHosts": { "index": 35, "kind": "parameter", "displayName":
"Async Mdn Allowed Hosts", "group": "security", "label": "security",
"required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Comma separated list of host names an
asynchronous MDN may be delivered to. The delivery [...]
+ "asyncMdnAllowedHosts": { "index": 35, "kind": "parameter", "displayName":
"Async Mdn Allowed Hosts", "group": "security", "label": "security",
"required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Comma separated list of host names an
asynchronous MDN may be delivered to. The delivery [...]
"decryptingPrivateKey": { "index": 36, "kind": "parameter", "displayName":
"Decrypting Private Key", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated":
false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The key used to encrypt the EDI message." },
"encryptingAlgorithm": { "index": 37, "kind": "parameter", "displayName":
"Encrypting Algorithm", "group": "security", "label": "security", "required":
false, "type": "enum", "javaType":
"org.apache.camel.component.as2.api.AS2EncryptionAlgorithm", "enum": [
"AES128_CBC", "AES192_CBC", "AES256_CBC", "AES128_CCM", "AES192_CCM",
"AES256_CCM", "AES128_GCM", "AES192_GCM", "AES256_GCM", "CAMELLIA128_CBC",
"CAMELLIA192_CBC", "CAMELLIA256_CBC", "CAST5_CBC", "DES_CBC", "DES_EDE3_CBC",
"GOST28 [...]
"encryptingCertificateChain": { "index": 38, "kind": "parameter",
"displayName": "Encrypting Certificate Chain", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The chain of certificates used to encrypt EDI
message." },
"hostnameVerifier": { "index": 39, "kind": "parameter", "displayName":
"Hostname Verifier", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "javax.net.ssl.HostnameVerifier",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Set hostname verifier for SSL session." },
- "mdnAccessToken": { "index": 40, "kind": "parameter", "displayName": "Mdn
Access Token", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the server
when it sends an async MDN." },
- "mdnPassword": { "index": 41, "kind": "parameter", "displayName": "Mdn
Password", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the server for
basic authentication when it sends an async [...]
- "mdnUserName": { "index": 42, "kind": "parameter", "displayName": "Mdn
User Name", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the server for
basic authentication when it sends an asyn [...]
+ "mdnAccessToken": { "index": 40, "kind": "parameter", "displayName": "Mdn
Access Token", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the server
when it sends an async MDN. The optio [...]
+ "mdnPassword": { "index": 41, "kind": "parameter", "displayName": "Mdn
Password", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the server for
basic authentication when it sends an async [...]
+ "mdnUserName": { "index": 42, "kind": "parameter", "displayName": "Mdn
User Name", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the server for
basic authentication when it sends an asyn [...]
"password": { "index": 43, "kind": "parameter", "displayName": "Password",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the client for
basic authentication." },
"signatureVerificationRequired": { "index": 44, "kind": "parameter",
"displayName": "Signature Verification Required", "group": "security", "label":
"security", "required": false, "type": "boolean", "javaType": "boolean",
"deprecated": false, "autowired": false, "secret": false, "defaultValue":
false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration",
"configurationField": "configuration", "description": "Whether to reject an
inbound signed AS2 message that cann [...]
"signedReceiptMicAlgorithms": { "index": 45, "kind": "parameter",
"displayName": "Signed Receipt Mic Algorithms", "group": "security", "label":
"security", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.as2.AS2Configuration",
"configurationField": "configuration", "description": "The list of algorithms,
in order of preference, requested to generate a messa [...]
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/as2-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/as2-component.adoc
index c5aabf3e6aec..8b3988308f01 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/as2-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/as2-component.adoc
@@ -99,3 +99,11 @@ An `as2://server/listen` consumer receives AS2 messages on
`serverPortNumber`, f
A `SIGNED`, `ENCRYPTED` or `SIGNED_ENCRYPTED` message structure also needs the
keys and certificates of the signing
and encrypting options.
+
+Several `as2://server/listen` consumers can share a `serverPortNumber`, each
with its own `requestUriPattern`. Each
+consumer applies its own keys and certificates and its own asynchronous MDN
settings (`mdnUserName`, `mdnPassword`,
+`mdnAccessToken` and `asyncMdnAllowedHosts`) to the messages received on its
own pattern.
+
+The settings of the shared server connection still come from the first
endpoint created on the port:
+`sslContextParameters` and `sslContext` (used for the TLS listener and for the
https delivery of asynchronous MDNs),
+`mdnMessageTemplate`, `server`, `serverFqdn` and `as2Version`. They are per
port, not per consumer.
diff --git
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java
index 1270448dc5ee..57459d9279b2 100644
---
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java
+++
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java
@@ -89,10 +89,6 @@ public class AS2ServerConnection {
private final String as2Version;
private final String originServer;
private final String serverFqdn;
- private final String userName;
- private final String password;
- private final String accessToken;
- private final String asyncMdnAllowedHosts;
private final SSLContext sslContext;
/**
@@ -131,8 +127,10 @@ public class AS2ServerConnection {
= ContextValue.newThreadLocal("AS2ConsumerConfig");
/**
- * Configuration data holding all necessary security material (signing
keys/certs and decryption keys/certs) for a
- * single AS2 consumer endpoint. This immutable object is looked up per
request URI.
+ * Configuration data holding all necessary security material (signing
keys/certs and decryption keys/certs) and the
+ * asynchronous MDN settings (the credentials attached to an asynchronous
MDN and the hosts it may be delivered to)
+ * for a single AS2 consumer endpoint. This immutable object is looked up
per request URI, so the consumers sharing
+ * a server connection each use their own values.
*/
public static class AS2ConsumerConfiguration {
private final Certificate[] signingCertificateChain;
@@ -140,18 +138,49 @@ public class AS2ServerConnection {
private final PrivateKey decryptingPrivateKey;
private final Certificate[] validateSigningCertificateChain;
private final AS2SignatureAlgorithm signingAlgorithm;
+ private final String mdnUserName;
+ private final String mdnPassword;
+ private final String mdnAccessToken;
+ private final String asyncMdnAllowedHosts;
+ /**
+ * Creates a configuration without asynchronous MDN settings: an
asynchronous MDN for a request matched by it is
+ * delivered without credentials, to the delivery address the sender
named.
+ */
public AS2ConsumerConfiguration(
AS2SignatureAlgorithm signingAlgorithm,
Certificate[] signingCertificateChain,
PrivateKey signingPrivateKey,
PrivateKey decryptingPrivateKey,
Certificate[]
validateSigningCertificateChain) {
+ this(signingAlgorithm, signingCertificateChain, signingPrivateKey,
decryptingPrivateKey,
+ validateSigningCertificateChain, null, null, null, null);
+ }
+
+ /**
+ * Creates a configuration with its own asynchronous MDN settings: an
asynchronous MDN for a request matched by
+ * it is delivered with these credentials (a user name and password,
or an access token), and only to a delivery
+ * address in {@code asyncMdnAllowedHosts} when that list is set.
+ */
+ public AS2ConsumerConfiguration(
+ AS2SignatureAlgorithm signingAlgorithm,
+ Certificate[] signingCertificateChain,
+ PrivateKey signingPrivateKey,
+ PrivateKey decryptingPrivateKey,
+ Certificate[]
validateSigningCertificateChain,
+ String mdnUserName,
+ String mdnPassword,
+ String mdnAccessToken,
+ String asyncMdnAllowedHosts) {
this.signingAlgorithm = signingAlgorithm;
this.signingCertificateChain = signingCertificateChain;
this.signingPrivateKey = signingPrivateKey;
this.decryptingPrivateKey = decryptingPrivateKey;
this.validateSigningCertificateChain =
validateSigningCertificateChain;
+ this.mdnUserName = mdnUserName;
+ this.mdnPassword = mdnPassword;
+ this.mdnAccessToken = mdnAccessToken;
+ this.asyncMdnAllowedHosts = asyncMdnAllowedHosts;
}
// Getters
@@ -174,6 +203,22 @@ public class AS2ServerConnection {
public PrivateKey getDecryptingPrivateKey() {
return decryptingPrivateKey;
}
+
+ public String getMdnUserName() {
+ return mdnUserName;
+ }
+
+ public String getMdnPassword() {
+ return mdnPassword;
+ }
+
+ public String getMdnAccessToken() {
+ return mdnAccessToken;
+ }
+
+ public String getAsyncMdnAllowedHosts() {
+ return asyncMdnAllowedHosts;
+ }
}
/**
@@ -549,16 +594,18 @@ public class AS2ServerConnection {
DispositionNotificationMultipartReportEntity
multipartReportEntity = coreContext.getAttribute(
AS2AsynchronousMDNManager.ASYNCHRONOUS_MDN,
DispositionNotificationMultipartReportEntity.class);
+ // The MDN settings come from the matched path's
configuration, like the signing material: the
+ // consumers sharing this connection each deliver their
asynchronous MDNs with their own values.
AS2AsynchronousMDNManager asynchronousMDNManager = new
AS2AsynchronousMDNManager(
AS2ServerConnection.this.as2Version,
AS2ServerConnection.this.originServer,
AS2ServerConnection.this.serverFqdn,
config.getSigningCertificateChain(),
config.getSigningPrivateKey(),
- AS2ServerConnection.this.userName,
- AS2ServerConnection.this.password,
- AS2ServerConnection.this.accessToken,
- AS2ServerConnection.this.asyncMdnAllowedHosts,
+ config.getMdnUserName(),
+ config.getMdnPassword(),
+ config.getMdnAccessToken(),
+ config.getAsyncMdnAllowedHosts(),
AS2ServerConnection.this.sslContext);
HttpRequest request = coreContext.getRequest();
@@ -625,25 +672,27 @@ public class AS2ServerConnection {
String accessToken,
String asyncMdnAllowedHosts)
throws IOException
{
- this.asyncMdnAllowedHosts = asyncMdnAllowedHosts;
this.sslContext = sslContext;
this.as2Version = ObjectHelper.notNull(as2Version, "as2Version");
this.originServer = ObjectHelper.notNull(originServer, "userAgent");
this.serverFqdn = ObjectHelper.notNull(serverFqdn, "serverFqdn");
final Integer parserServerPortNumber =
ObjectHelper.notNull(serverPortNumber, "serverPortNumber");
- this.userName = userName;
- this.password = password;
- this.accessToken = accessToken;
// Create and register a default consumer configuration for the root
path ('/').
- // This ensures that all incoming requests have a fallback
configuration for decryption
- // and MDN signing, even if they don't match a specific Camel route
path.
+ // This ensures that all incoming requests have a fallback
configuration for decryption,
+ // MDN signing and asynchronous MDN delivery, even if they don't match
a specific Camel route path.
+ // The MDN settings given to this constructor belong to that fallback
only: a consumer registers
+ // its own configuration, with its own MDN settings, for its own
request URI pattern.
AS2ServerConnection.AS2ConsumerConfiguration consumerConfig = new
AS2ServerConnection.AS2ConsumerConfiguration(
signingAlgorithm,
signingCertificateChain,
signingPrivateKey,
decryptingPrivateKey,
- validateSigningCertificateChain);
+ validateSigningCertificateChain,
+ userName,
+ password,
+ accessToken,
+ asyncMdnAllowedHosts);
registerConsumerConfiguration("/", consumerConfig);
listenerService = new RequestListenerService(
@@ -675,6 +724,13 @@ public class AS2ServerConnection {
.orElse(null);
}
+ /**
+ * Registers the configuration used for the requests matching a consumer's
request URI pattern: its signing and
+ * decrypting material and its asynchronous MDN settings. Each consumer
registers its own configuration for its own
+ * pattern, also when the pattern is the root path ({@code /}) for which
the constructor registered a fallback: the
+ * fallback and the configurations of the other consumers sharing this
connection are not its own. Registering a
+ * pattern again replaces the configuration registered for it.
+ */
public void registerConsumerConfiguration(String path,
AS2ConsumerConfiguration config) {
consumerConfigurations.put(path, config);
}
diff --git
a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2ConsumerConfigurationMdnSettingsTest.java
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2ConsumerConfigurationMdnSettingsTest.java
new file mode 100644
index 000000000000..1a297f6a10b3
--- /dev/null
+++
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2ConsumerConfigurationMdnSettingsTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.as2.api;
+
+import
org.apache.camel.component.as2.api.AS2ServerConnection.AS2ConsumerConfiguration;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * The asynchronous MDN settings (the credentials attached to an asynchronous
MDN and the hosts it may be delivered to)
+ * are part of the per-path {@link AS2ConsumerConfiguration}, so the consumers
sharing a server connection each keep
+ * their own. The fallback configuration of the root path carries the values
given to the connection's constructor.
+ */
+class AS2ConsumerConfigurationMdnSettingsTest {
+
+ @Test
+ void configurationCarriesTheMdnSettings() {
+ AS2ConsumerConfiguration config = new AS2ConsumerConfiguration(
+ AS2SignatureAlgorithm.SHA256WITHRSA, null, null, null, null,
+ "user", "password", "token", "receipts.example.com");
+
+ assertEquals(AS2SignatureAlgorithm.SHA256WITHRSA,
config.getSigningAlgorithm());
+ assertEquals("user", config.getMdnUserName());
+ assertEquals("password", config.getMdnPassword());
+ assertEquals("token", config.getMdnAccessToken());
+ assertEquals("receipts.example.com", config.getAsyncMdnAllowedHosts());
+ }
+
+ @Test
+ void configurationWithoutMdnSettingsHasNone() {
+ AS2ConsumerConfiguration config = new AS2ConsumerConfiguration(
+ AS2SignatureAlgorithm.SHA256WITHRSA, null, null, null, null);
+
+ assertNull(config.getMdnUserName());
+ assertNull(config.getMdnPassword());
+ assertNull(config.getMdnAccessToken());
+ assertNull(config.getAsyncMdnAllowedHosts());
+ }
+
+ @Test
+ void rootPathFallbackCarriesTheConnectionSettings() throws Exception {
+ AS2ServerConnection connection = new AS2ServerConnection(
+ "1.1", "AS2ConsumerConfigurationMdnSettingsTest Server",
"server.example.com", 0,
+ AS2SignatureAlgorithm.SHA256WITHRSA, null, null, null, "TBD",
null, null,
+ "user", "password", "token", "receipts.example.com");
+ try {
+ AS2ConsumerConfiguration fallback =
connection.getConfigurationForPath("/").orElseThrow();
+ assertEquals("user", fallback.getMdnUserName());
+ assertEquals("password", fallback.getMdnPassword());
+ assertEquals("token", fallback.getMdnAccessToken());
+ assertEquals("receipts.example.com",
fallback.getAsyncMdnAllowedHosts());
+
+ // a consumer registered for another path keeps its own settings,
not the fallback's
+ connection.registerConsumerConfiguration("/partner", new
AS2ConsumerConfiguration(
+ AS2SignatureAlgorithm.SHA256WITHRSA, null, null, null,
null,
+ null, null, "partner-token", "partner.example.com"));
+ AS2ConsumerConfiguration partner =
connection.getConfigurationForPath("/partner").orElseThrow();
+ assertNull(partner.getMdnUserName());
+ assertNull(partner.getMdnPassword());
+ assertEquals("partner-token", partner.getMdnAccessToken());
+ assertEquals("partner.example.com",
partner.getAsyncMdnAllowedHosts());
+ assertEquals("user",
connection.getConfigurationForPath("/").orElseThrow().getMdnUserName());
+
+ // registering the root path again replaces the fallback
+ connection.registerConsumerConfiguration("/", new
AS2ConsumerConfiguration(
+ AS2SignatureAlgorithm.SHA256WITHRSA, null, null, null,
null));
+ AS2ConsumerConfiguration replaced =
connection.getConfigurationForPath("/").orElseThrow();
+ assertNull(replaced.getMdnUserName());
+ assertNull(replaced.getMdnAccessToken());
+ assertNull(replaced.getAsyncMdnAllowedHosts());
+ } finally {
+ connection.close();
+ }
+ }
+}
diff --git
a/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json
b/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json
index b348e9105ce7..989fb9a25f74 100644
---
a/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json
+++
b/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json
@@ -68,14 +68,14 @@
"expectContinue": { "index": 32, "kind": "parameter", "displayName":
"Expect Continue", "group": "producer", "label": "producer", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Controls whether the Expect: 100-Continue
header is included in outbound AS2 messages. W [...]
"lazyStartProducer": { "index": 33, "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 produ [...]
"accessToken": { "index": 34, "kind": "parameter", "displayName": "Access
Token", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the client
for bearer authentication." },
- "asyncMdnAllowedHosts": { "index": 35, "kind": "parameter", "displayName":
"Async Mdn Allowed Hosts", "group": "security", "label": "security",
"required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Comma separated list of host names an
asynchronous MDN may be delivered to. The delivery [...]
+ "asyncMdnAllowedHosts": { "index": 35, "kind": "parameter", "displayName":
"Async Mdn Allowed Hosts", "group": "security", "label": "security",
"required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Comma separated list of host names an
asynchronous MDN may be delivered to. The delivery [...]
"decryptingPrivateKey": { "index": 36, "kind": "parameter", "displayName":
"Decrypting Private Key", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated":
false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The key used to encrypt the EDI message." },
"encryptingAlgorithm": { "index": 37, "kind": "parameter", "displayName":
"Encrypting Algorithm", "group": "security", "label": "security", "required":
false, "type": "enum", "javaType":
"org.apache.camel.component.as2.api.AS2EncryptionAlgorithm", "enum": [
"AES128_CBC", "AES192_CBC", "AES256_CBC", "AES128_CCM", "AES192_CCM",
"AES256_CCM", "AES128_GCM", "AES192_GCM", "AES256_GCM", "CAMELLIA128_CBC",
"CAMELLIA192_CBC", "CAMELLIA256_CBC", "CAST5_CBC", "DES_CBC", "DES_EDE3_CBC",
"GOST28 [...]
"encryptingCertificateChain": { "index": 38, "kind": "parameter",
"displayName": "Encrypting Certificate Chain", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The chain of certificates used to encrypt EDI
message." },
"hostnameVerifier": { "index": 39, "kind": "parameter", "displayName":
"Hostname Verifier", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "javax.net.ssl.HostnameVerifier",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Set hostname verifier for SSL session." },
- "mdnAccessToken": { "index": 40, "kind": "parameter", "displayName": "Mdn
Access Token", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the server
when it sends an async MDN." },
- "mdnPassword": { "index": 41, "kind": "parameter", "displayName": "Mdn
Password", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the server for
basic authentication when it sends an async [...]
- "mdnUserName": { "index": 42, "kind": "parameter", "displayName": "Mdn
User Name", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the server for
basic authentication when it sends an asyn [...]
+ "mdnAccessToken": { "index": 40, "kind": "parameter", "displayName": "Mdn
Access Token", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the server
when it sends an async MDN. The optio [...]
+ "mdnPassword": { "index": 41, "kind": "parameter", "displayName": "Mdn
Password", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the server for
basic authentication when it sends an async [...]
+ "mdnUserName": { "index": 42, "kind": "parameter", "displayName": "Mdn
User Name", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the server for
basic authentication when it sends an asyn [...]
"password": { "index": 43, "kind": "parameter", "displayName": "Password",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "security": "secret", "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the client for
basic authentication." },
"signatureVerificationRequired": { "index": 44, "kind": "parameter",
"displayName": "Signature Verification Required", "group": "security", "label":
"security", "required": false, "type": "boolean", "javaType": "boolean",
"deprecated": false, "autowired": false, "secret": false, "defaultValue":
false, "configurationClass": "org.apache.camel.component.as2.AS2Configuration",
"configurationField": "configuration", "description": "Whether to reject an
inbound signed AS2 message that cann [...]
"signedReceiptMicAlgorithms": { "index": 45, "kind": "parameter",
"displayName": "Signed Receipt Mic Algorithms", "group": "security", "label":
"security", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.as2.AS2Configuration",
"configurationField": "configuration", "description": "The list of algorithms,
in order of preference, requested to generate a messa [...]
diff --git
a/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc
b/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc
index c5aabf3e6aec..8b3988308f01 100644
--- a/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc
+++ b/components/camel-as2/camel-as2-component/src/main/docs/as2-component.adoc
@@ -99,3 +99,11 @@ An `as2://server/listen` consumer receives AS2 messages on
`serverPortNumber`, f
A `SIGNED`, `ENCRYPTED` or `SIGNED_ENCRYPTED` message structure also needs the
keys and certificates of the signing
and encrypting options.
+
+Several `as2://server/listen` consumers can share a `serverPortNumber`, each
with its own `requestUriPattern`. Each
+consumer applies its own keys and certificates and its own asynchronous MDN
settings (`mdnUserName`, `mdnPassword`,
+`mdnAccessToken` and `asyncMdnAllowedHosts`) to the messages received on its
own pattern.
+
+The settings of the shared server connection still come from the first
endpoint created on the port:
+`sslContextParameters` and `sslContext` (used for the TLS listener and for the
https delivery of asynchronous MDNs),
+`mdnMessageTemplate`, `server`, `serverFqdn` and `as2Version`. They are per
port, not per consumer.
diff --git
a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java
b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java
index cc283631a27c..3bc6aec16704 100644
---
a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java
+++
b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java
@@ -661,7 +661,9 @@ public class AS2Configuration {
/**
* The user-name that is used by the server for basic authentication when
it sends an async MDN. If options for
- * basic authentication and bearer authentication are both set then basic
authentication takes precedence.
+ * basic authentication and bearer authentication are both set then basic
authentication takes precedence. The
+ * option applies to the consumer endpoint it is set on: consumers sharing
a serverPortNumber each send their async
+ * MDNs with their own values.
*/
public void setMdnUserName(String mdnUserName) {
this.mdnUserName = mdnUserName;
@@ -672,7 +674,8 @@ public class AS2Configuration {
}
/**
- * The password that is used by the server for basic authentication when
it sends an async MDN.
+ * The password that is used by the server for basic authentication when
it sends an async MDN. The option applies
+ * to the consumer endpoint it is set on, like mdnUserName.
*/
public void setMdnPassword(String mdnPassword) {
this.mdnPassword = mdnPassword;
@@ -683,7 +686,8 @@ public class AS2Configuration {
}
/**
- * The access token that is used by the server when it sends an async MDN.
+ * The access token that is used by the server when it sends an async MDN.
The option applies to the consumer
+ * endpoint it is set on, like mdnUserName.
*/
public void setMdnAccessToken(String accessToken) {
this.mdnAccessToken = accessToken;
@@ -700,6 +704,9 @@ public class AS2Configuration {
* When set, an asynchronous MDN whose delivery address names a host
outside the list is refused, and the configured
* mdnUserName/mdnPassword/mdnAccessToken are attached only to a host on
the list. When not set, the MDN is still
* delivered to the sender-supplied address but no credentials are
attached.
+ *
+ * The option applies to the consumer endpoint it is set on: consumers
sharing a serverPortNumber each apply their
+ * own list.
*/
public void setAsyncMdnAllowedHosts(String asyncMdnAllowedHosts) {
this.asyncMdnAllowedHosts = asyncMdnAllowedHosts;
diff --git
a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Consumer.java
b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Consumer.java
index 9446fc6135e1..9a2e8854dba4 100644
---
a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Consumer.java
+++
b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Consumer.java
@@ -93,18 +93,22 @@ public class AS2Consumer extends
AbstractApiConsumer<AS2ApiName, AS2Configuratio
String uri = properties.computeIfAbsent("requestUriPattern", param ->
"/").toString();
- // Check if the configuration for this specific URI path has already
been registered
- // (e.g., by the default "/" fallback or another consumer).
- // If not, create and register it now using the endpoint's configured
keys/certs.
- if (as2ServerConnection.getConfigurationForPath(uri).isEmpty()) {
- AS2ServerConnection.AS2ConsumerConfiguration consumerConfig = new
AS2ServerConnection.AS2ConsumerConfiguration(
- getEndpoint().getSigningAlgorithm(),
- getEndpoint().getSigningCertificateChain(),
- getEndpoint().getSigningPrivateKey(),
- getEndpoint().getDecryptingPrivateKey(),
- getEndpoint().getValidateSigningCertificateChain());
- as2ServerConnection.registerConsumerConfiguration(uri,
consumerConfig);
- }
+ // Register this consumer's own configuration for its URI pattern: the
endpoint's keys/certs and its
+ // asynchronous MDN settings. The server connection is shared by every
consumer on the port and was
+ // built from the first endpoint created on it, so the "/" fallback
and the configurations of the
+ // other consumers are not this consumer's; a consumer whose pattern
is "/" replaces the fallback.
+ AS2Configuration configuration = getEndpoint().getConfiguration();
+ AS2ServerConnection.AS2ConsumerConfiguration consumerConfig = new
AS2ServerConnection.AS2ConsumerConfiguration(
+ getEndpoint().getSigningAlgorithm(),
+ getEndpoint().getSigningCertificateChain(),
+ getEndpoint().getSigningPrivateKey(),
+ getEndpoint().getDecryptingPrivateKey(),
+ getEndpoint().getValidateSigningCertificateChain(),
+ configuration.getMdnUserName(),
+ configuration.getMdnPassword(),
+ configuration.getMdnAccessToken(),
+ configuration.getAsyncMdnAllowedHosts());
+ as2ServerConnection.registerConsumerConfiguration(uri, consumerConfig);
as2ServerConnection.listen(uri, this);
}
diff --git
a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerTwoConsumerAsyncMdnSettingsTest.java
b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerTwoConsumerAsyncMdnSettingsTest.java
new file mode 100644
index 000000000000..2420baafa10c
--- /dev/null
+++
b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerTwoConsumerAsyncMdnSettingsTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.as2;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.as2.api.AS2MediaType;
+import org.apache.camel.component.as2.api.AS2MessageStructure;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.AvailablePortFinder;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * Several {@code as2://server/listen} consumers share one {@code
serverPortNumber}, each with its own
+ * {@code requestUriPattern}. Each of them delivers the asynchronous MDNs for
the messages received on its own pattern
+ * with its own {@code mdnUserName}, {@code mdnPassword}, {@code
mdnAccessToken} and {@code asyncMdnAllowedHosts}, not
+ * with the values of the consumer that created the shared server connection.
This also holds for a consumer on the root
+ * pattern, for which the connection registers a fallback configuration from
the endpoint that created it.
+ */
+class AS2ServerTwoConsumerAsyncMdnSettingsTest extends AS2ServerSecTestBase {
+
+ private static final String FIRST_PATTERN = "/receiver-one";
+ private static final String SECOND_PATTERN = "/receiver-two";
+ private static final String THIRD_PATTERN = "/receiver-three";
+ private static final String ROOT_PATTERN = "/";
+ private static final String FIRST_USER_NAME = "first-user";
+ private static final String FIRST_PASSWORD = "first-password";
+ private static final String SECOND_ACCESS_TOKEN = "second-token";
+ private static final String ROOT_ACCESS_TOKEN = "root-token";
+ private static final String RECEIPT_PATH = "/receipts";
+
+ // one receipt endpoint per host name, so that each delivery address
resolves to the endpoint bound to it
+ @RegisterExtension
+ AvailablePortFinder.Port localhostReceiptPort = AvailablePortFinder.find();
+ @RegisterExtension
+ AvailablePortFinder.Port loopbackReceiptPort = AvailablePortFinder.find();
+
+ @Test
+ void asyncMdnForTheSecondConsumerUsesItsOwnSettings() throws Exception {
+ String authorization = authorizationOfTheReceiptFor(SECOND_PATTERN,
loopbackReceiptAddress());
+
+ assertEquals("Bearer " + SECOND_ACCESS_TOKEN, authorization);
+ }
+
+ @Test
+ void asyncMdnForTheSecondConsumerAppliesItsOwnAllowedHosts() throws
Exception {
+ // localhost is on the first consumer's list only, so the second
consumer's MDN is not delivered to it
+ MockEndpoint receipts = getMockEndpoint("mock:receipts");
+ receipts.expectedMessageCount(0);
+
+ HttpCoreContext context = sendTo(SECOND_PATTERN,
localhostReceiptAddress());
+ verifyOkResponse(context);
+
+ receipts.assertIsSatisfied(2000);
+ }
+
+ @Test
+ void asyncMdnForAConsumerWithoutSettingsCarriesNoAuthorization() throws
Exception {
+ assertNull(authorizationOfTheReceiptFor(THIRD_PATTERN,
localhostReceiptAddress()));
+ }
+
+ @Test
+ void asyncMdnForTheConsumerOnTheRootPatternUsesItsOwnSettings() throws
Exception {
+ String authorization = authorizationOfTheReceiptFor(ROOT_PATTERN,
localhostReceiptAddress());
+
+ assertEquals("Bearer " + ROOT_ACCESS_TOKEN, authorization);
+ }
+
+ @Test
+ void asyncMdnForTheFirstConsumerIsUnchanged() throws Exception {
+ String authorization = authorizationOfTheReceiptFor(FIRST_PATTERN,
localhostReceiptAddress());
+
+ String encoded = Base64.getEncoder()
+ .encodeToString((FIRST_USER_NAME + ":" +
FIRST_PASSWORD).getBytes(StandardCharsets.UTF_8));
+ assertEquals("Basic " + encoded, authorization);
+ }
+
+ @Test
+ void asyncMdnAfterTheFirstConsumerStopsUsesTheRemainingConsumerSettings()
throws Exception {
+ context.getRouteController().stopRoute("first");
+
+ String authorization = authorizationOfTheReceiptFor(SECOND_PATTERN,
loopbackReceiptAddress());
+
+ assertEquals("Bearer " + SECOND_ACCESS_TOKEN, authorization);
+ }
+
+ private String authorizationOfTheReceiptFor(String requestUri, String
receiptDeliveryAddress) throws Exception {
+ MockEndpoint receipts = getMockEndpoint("mock:receipts");
+ receipts.expectedMessageCount(1);
+
+ HttpCoreContext context = sendTo(requestUri, receiptDeliveryAddress);
+ verifyOkResponse(context);
+
+ receipts.assertIsSatisfied();
+ return
receipts.getExchanges().get(0).getIn().getHeader("Authorization", String.class);
+ }
+
+ private HttpCoreContext sendTo(String requestUri, String
receiptDeliveryAddress) throws Exception {
+ return clientConnection().send(EDI_MESSAGE, requestUri, SUBJECT, FROM,
AS2_NAME, AS2_NAME,
+ AS2MessageStructure.PLAIN, AS2MediaType.APPLICATION_EDIFACT,
null, null,
+ null, null, null, null,
+ DISPOSITION_NOTIFICATION_TO, null, null, null,
+ null, receiptDeliveryAddress, null, null, null);
+ }
+
+ private String localhostReceiptAddress() {
+ return "http://localhost:" + localhostReceiptPort.getPort() +
RECEIPT_PATH;
+ }
+
+ private String loopbackReceiptAddress() {
+ return "http://127.0.0.1:" + loopbackReceiptPort.getPort() +
RECEIPT_PATH;
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ // the first consumer creates the server connection shared by
every consumer on the port
+ from("as2://server/listen?requestUriPattern=" + FIRST_PATTERN
+ + "&mdnUserName=" + FIRST_USER_NAME + "&mdnPassword=" +
FIRST_PASSWORD
+ + "&asyncMdnAllowedHosts=localhost,127.0.0.1")
+ .routeId("first")
+ .to("mock:first");
+
+ from("as2://server/listen?requestUriPattern=" + SECOND_PATTERN
+ + "&mdnAccessToken=" + SECOND_ACCESS_TOKEN +
"&asyncMdnAllowedHosts=127.0.0.1")
+ .routeId("second")
+ .to("mock:second");
+
+ // no asynchronous MDN settings at all
+ from("as2://server/listen?requestUriPattern=" + THIRD_PATTERN)
+ .routeId("third")
+ .to("mock:third");
+
+ // the root pattern, on the connection another consumer created
+ from("as2://server/listen?requestUriPattern=" + ROOT_PATTERN
+ + "&mdnAccessToken=" + ROOT_ACCESS_TOKEN +
"&asyncMdnAllowedHosts=localhost")
+ .routeId("root")
+ .to("mock:root");
+
+ // where the asynchronous MDNs are delivered to
+ from("jetty:http://localhost:" +
localhostReceiptPort.getPort() + RECEIPT_PATH)
+ .to("mock:receipts");
+ from("jetty:http://127.0.0.1:" + loopbackReceiptPort.getPort()
+ RECEIPT_PATH)
+ .to("mock:receipts");
+ }
+ };
+ }
+}
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 02ac2638c47b..0b73f4d223e5 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
@@ -2523,6 +2523,21 @@ With `strict=false`, unmarshalling no longer returns
`null` or a partial result
recoveries of the TOON specification apply, such as accepting an array whose
length differs from the declared length;
other malformed TOON fails with `IllegalArgumentException`, as in strict mode.
+=== camel-as2 - consumers sharing a port use their own asynchronous MDN
settings
+
+`as2://server/listen` consumers that share a `serverPortNumber` now deliver
asynchronous MDNs with their own
+`mdnUserName`, `mdnPassword`, `mdnAccessToken` and `asyncMdnAllowedHosts`,
selected by the matched `requestUriPattern`
+in the same way as the signing material. Previously the values of the first
consumer configured on the port were used
+for every consumer on it, and the values set on the other consumers were
ignored. A consumer that relied on inheriting
+them now has to set them itself.
+
+This applies to `asyncMdnAllowedHosts` as well. A consumer without its own
list used to inherit the first consumer's
+list, so asynchronous MDNs to other hosts were refused; it now delivers to the
address the sender gives, without
+credentials. A deployment that counted on the inherited list as a restriction
has to set it on each consumer.
+
+A consumer whose `requestUriPattern` is `/` now also uses its own keys,
certificates and asynchronous MDN settings when
+another consumer created the shared server connection; before, it used the
first consumer's.
+
=== camel-ftp, camel-sftp, camel-ftps, camel-mina-sftp - sendNoop also applies
to the consumer
The `sendNoop` option now also applies to the check a consumer makes before
each poll, not only to the pre-write check
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java
index bdcfdec593df..97e5a5dc260e 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java
@@ -630,7 +630,9 @@ public interface AS2EndpointBuilderFactory {
* the list is refused, and the configured
* mdnUserName/mdnPassword/mdnAccessToken are attached only to a host
on
* the list. When not set, the MDN is still delivered to the
- * sender-supplied address but no credentials are attached.
+ * sender-supplied address but no credentials are attached. The option
+ * applies to the consumer endpoint it is set on: consumers sharing a
+ * serverPortNumber each apply their own list.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -762,7 +764,8 @@ public interface AS2EndpointBuilderFactory {
}
/**
* The access token that is used by the server when it sends an async
- * MDN.
+ * MDN. The option applies to the consumer endpoint it is set on, like
+ * mdnUserName.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -777,7 +780,8 @@ public interface AS2EndpointBuilderFactory {
}
/**
* The password that is used by the server for basic authentication
when
- * it sends an async MDN.
+ * it sends an async MDN. The option applies to the consumer endpoint
it
+ * is set on, like mdnUserName.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -794,7 +798,9 @@ public interface AS2EndpointBuilderFactory {
* The user-name that is used by the server for basic authentication
* when it sends an async MDN. If options for basic authentication and
* bearer authentication are both set then basic authentication takes
- * precedence.
+ * precedence. The option applies to the consumer endpoint it is set
on:
+ * consumers sharing a serverPortNumber each send their async MDNs with
+ * their own values.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -1766,7 +1772,9 @@ public interface AS2EndpointBuilderFactory {
* the list is refused, and the configured
* mdnUserName/mdnPassword/mdnAccessToken are attached only to a host
on
* the list. When not set, the MDN is still delivered to the
- * sender-supplied address but no credentials are attached.
+ * sender-supplied address but no credentials are attached. The option
+ * applies to the consumer endpoint it is set on: consumers sharing a
+ * serverPortNumber each apply their own list.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -1898,7 +1906,8 @@ public interface AS2EndpointBuilderFactory {
}
/**
* The access token that is used by the server when it sends an async
- * MDN.
+ * MDN. The option applies to the consumer endpoint it is set on, like
+ * mdnUserName.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -1913,7 +1922,8 @@ public interface AS2EndpointBuilderFactory {
}
/**
* The password that is used by the server for basic authentication
when
- * it sends an async MDN.
+ * it sends an async MDN. The option applies to the consumer endpoint
it
+ * is set on, like mdnUserName.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -1930,7 +1940,9 @@ public interface AS2EndpointBuilderFactory {
* The user-name that is used by the server for basic authentication
* when it sends an async MDN. If options for basic authentication and
* bearer authentication are both set then basic authentication takes
- * precedence.
+ * precedence. The option applies to the consumer endpoint it is set
on:
+ * consumers sharing a serverPortNumber each send their async MDNs with
+ * their own values.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -2843,7 +2855,9 @@ public interface AS2EndpointBuilderFactory {
* the list is refused, and the configured
* mdnUserName/mdnPassword/mdnAccessToken are attached only to a host
on
* the list. When not set, the MDN is still delivered to the
- * sender-supplied address but no credentials are attached.
+ * sender-supplied address but no credentials are attached. The option
+ * applies to the consumer endpoint it is set on: consumers sharing a
+ * serverPortNumber each apply their own list.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -2975,7 +2989,8 @@ public interface AS2EndpointBuilderFactory {
}
/**
* The access token that is used by the server when it sends an async
- * MDN.
+ * MDN. The option applies to the consumer endpoint it is set on, like
+ * mdnUserName.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -2990,7 +3005,8 @@ public interface AS2EndpointBuilderFactory {
}
/**
* The password that is used by the server for basic authentication
when
- * it sends an async MDN.
+ * it sends an async MDN. The option applies to the consumer endpoint
it
+ * is set on, like mdnUserName.
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -3007,7 +3023,9 @@ public interface AS2EndpointBuilderFactory {
* The user-name that is used by the server for basic authentication
* when it sends an async MDN. If options for basic authentication and
* bearer authentication are both set then basic authentication takes
- * precedence.
+ * precedence. The option applies to the consumer endpoint it is set
on:
+ * consumers sharing a serverPortNumber each send their async MDNs with
+ * their own values.
*
* The option is a: <code>java.lang.String</code> type.
*