This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch fix/CAMEL-22849 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 12ece6f0b808f5a363775a69a09e4f374dbd1911 Author: Guillaume Nodet <[email protected]> AuthorDate: Tue Jan 27 15:06:01 2026 +0100 CAMEL-22849: Add comprehensive tests for regex special characters in AS2 wildcard patterns --- .../component/as2/api/AS2ServerConnection.java | 13 ++-- .../AS2ServerConnectionPatternMatchingTest.java | 87 ++++++++++++++++++++++ .../component/as2/AS2ServerWildcardPatternIT.java | 12 +-- 3 files changed, 100 insertions(+), 12 deletions(-) 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 e2694b24b678..7f87027a7eca 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 @@ -92,11 +92,12 @@ public class AS2ServerConnection { private final String accessToken; /** - * Stores the configuration for each consumer endpoint path (e.g., "/consumerA"). - * Uses LinkedHashMap to preserve insertion order, ensuring that when multiple patterns match a request, - * the first registered pattern (in route definition order) takes precedence. + * Stores the configuration for each consumer endpoint path (e.g., "/consumerA"). Uses LinkedHashMap to preserve + * insertion order, ensuring that when multiple patterns match a request, the first registered pattern (in route + * definition order) takes precedence. */ - private final Map<String, AS2ConsumerConfiguration> consumerConfigurations = Collections.synchronizedMap(new LinkedHashMap<>()); + private final Map<String, AS2ConsumerConfiguration> consumerConfigurations + = Collections.synchronizedMap(new LinkedHashMap<>()); /** * Cache of compiled regex patterns for wildcard matching. Key is the pattern string, value is the compiled Pattern @@ -183,8 +184,8 @@ public class AS2ServerConnection { * </ul> * * <p> - * When multiple patterns match, the first registered pattern (in route definition order) takes precedence. - * Exact matches always take precedence over wildcard matches. + * When multiple patterns match, the first registered pattern (in route definition order) takes precedence. Exact + * matches always take precedence over wildcard matches. * * @param path The canonical request URI path (e.g., "/consumerA"). * @return An Optional containing the configuration if a match is found, otherwise empty. diff --git a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2ServerConnectionPatternMatchingTest.java b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2ServerConnectionPatternMatchingTest.java new file mode 100644 index 000000000000..eb4468a52c75 --- /dev/null +++ b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2ServerConnectionPatternMatchingTest.java @@ -0,0 +1,87 @@ +/* + * 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 java.util.regex.Pattern; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit test for AS2ServerConnection pattern matching logic + */ +public class AS2ServerConnectionPatternMatchingTest { + + /** + * Simulates the getCompiledPattern logic from AS2ServerConnection + */ + private Pattern getCompiledPattern(String pattern) { + String[] segments = pattern.split("\\*", -1); + StringBuilder regex = new StringBuilder(); + for (int i = 0; i < segments.length; i++) { + regex.append(Pattern.quote(segments[i])); + if (i < segments.length - 1) { + regex.append(".*"); + } + } + return Pattern.compile(regex.toString()); + } + + @Test + public void testWildcardPatternMatching() { + Pattern pattern = getCompiledPattern("/consumer/*"); + + assertTrue(pattern.matcher("/consumer/orders").matches()); + assertTrue(pattern.matcher("/consumer/invoices").matches()); + assertFalse(pattern.matcher("/admin/orders").matches()); + } + + @Test + public void testRegexSpecialCharactersWithWildcard() { + Pattern pattern = getCompiledPattern("/api/v2(3)/*"); + + // Should match - parentheses are literal + assertTrue(pattern.matcher("/api/v2(3)/orders").matches()); + assertTrue(pattern.matcher("/api/v2(3)/invoices").matches()); + + // Should NOT match - parentheses are literal, not regex grouping + assertFalse(pattern.matcher("/api/v23/orders").matches()); + assertFalse(pattern.matcher("/api/v2/orders").matches()); + } + + @Test + public void testRegexSpecialCharactersExactMatch() { + Pattern pattern = getCompiledPattern("/api/v1.2/endpoint"); + + // Should match - dot is literal + assertTrue(pattern.matcher("/api/v1.2/endpoint").matches()); + + // Should NOT match - dot is literal, not regex "any character" + assertFalse(pattern.matcher("/api/v1X2/endpoint").matches()); + } + + @Test + public void testMultipleWildcards() { + Pattern pattern = getCompiledPattern("/api/*/v2(3)/*"); + + assertTrue(pattern.matcher("/api/v1/v2(3)/orders").matches()); + assertTrue(pattern.matcher("/api/test/v2(3)/invoices").matches()); + assertFalse(pattern.matcher("/api/v1/v23/orders").matches()); + } +} diff --git a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerWildcardPatternIT.java b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerWildcardPatternIT.java index 8743ca36838c..811007d5ab82 100644 --- a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerWildcardPatternIT.java +++ b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerWildcardPatternIT.java @@ -62,8 +62,8 @@ public class AS2ServerWildcardPatternIT extends AS2ServerSecTestBase { from("as2://server/listen?requestUriPattern=/api/v1.2/endpoint") .to("mock:regexSpecialCharsConsumer"); - // Consumer with wildcard and special characters - from("as2://server/listen?requestUriPattern=/api/v2+3/*") + // Consumer with wildcard and special characters (using parentheses which are regex special chars) + from("as2://server/listen?requestUriPattern=/api/v2(3)/*") .to("mock:regexSpecialCharsWildcardConsumer"); } }; @@ -216,8 +216,8 @@ public class AS2ServerWildcardPatternIT extends AS2ServerSecTestBase { MockEndpoint regexWildcardEndpoint = getMockEndpoint("mock:regexSpecialCharsWildcardConsumer"); regexWildcardEndpoint.expectedMessageCount(1); - // Send to /api/v2+3/orders - the plus should be treated as a literal plus, not regex "one or more" - HttpCoreContext context = sendToPath("/api/v2+3/orders", AS2MessageStructure.PLAIN); + // Send to /api/v2(3)/orders - the parentheses should be treated as literal chars, not regex grouping + HttpCoreContext context = sendToPath("/api/v2(3)/orders", AS2MessageStructure.PLAIN); verifyOkResponse(context); regexWildcardEndpoint.assertIsSatisfied(); @@ -233,8 +233,8 @@ public class AS2ServerWildcardPatternIT extends AS2ServerSecTestBase { MockEndpoint regexWildcardEndpoint = getMockEndpoint("mock:regexSpecialCharsWildcardConsumer"); regexWildcardEndpoint.expectedMessageCount(0); - // Send to /api/v23/orders - should NOT match because plus is literal, not regex "one or more" - // If Pattern.quote() wasn't working, this would incorrectly match + // Send to /api/v23/orders - should NOT match because parentheses are literal, not regex grouping + // If Pattern.quote() wasn't working, /api/v2(3)/* would match /api/v23/orders as a regex HttpCoreContext context = sendToPath("/api/v23/orders", AS2MessageStructure.PLAIN); // This should fail to find a matching consumer, but we're just verifying it doesn't match the wrong one
