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.
          * 

Reply via email to