diff --git a/core/camel-support/src/main/java/org/apache/camel/support/RestConsumerContextPathMatcher.java b/core/camel-support/src/main/java/org/apache/camel/support/RestConsumerContextPathMatcher.java index bda24e6bc0d99..6b45e375f6799 100644 --- a/core/camel-support/src/main/java/org/apache/camel/support/RestConsumerContextPathMatcher.java +++ b/core/camel-support/src/main/java/org/apache/camel/support/RestConsumerContextPathMatcher.java @@ -16,21 +16,16 @@ */ package org.apache.camel.support; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.OptionalInt; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; /** * A context path matcher when using rest-dsl that allows components to reuse the same matching logic. *

- * The component should use the {@link #matchBestPath(String, String, java.util.List)} with the request details and the - * matcher returns the best matched, or null if none could be determined. + * The component should use the {@link #matchBestPath(String, String, List)} with the request details and the matcher + * returns the best matched, or null if none could be determined. *

* The {@link ConsumerPath} is used for the components to provide the details to the matcher. */ @@ -156,9 +151,9 @@ T> ConsumerPath matchBestPath(String requestMethod, String requestPath, List< } } - // if there are no wildcards, then select the matching with the longest path - boolean noWildcards = candidates.stream().allMatch(p -> countWildcards(p.getConsumerPath()) == 0); - if (noWildcards) { + // if there are no uri template, then select the matching with the longest path + boolean noCurlyBraces = candidates.stream().allMatch(p -> countCurlyBraces(p.getConsumerPath()) == 0); + if (noCurlyBraces) { // grab first which is the longest that matched the request path answer = candidates.stream() .filter(c -> matchPath(requestPath, c.getConsumerPath(), c.isMatchOnUriPrefix())) @@ -172,6 +167,17 @@ T> ConsumerPath matchBestPath(String requestMethod, String requestPath, List< // then match by wildcard path it = candidates.iterator(); + while (it.hasNext()) { + ConsumerPath consumer = it.next(); + // filter non matching paths + if (matchWildCard(requestPath, consumer.getConsumerPath())) { + answer = consumer; + break; + } + } + + // then match by uri template path + it = candidates.iterator(); while (it.hasNext()) { ConsumerPath consumer = it.next(); // filter non matching paths @@ -180,16 +186,16 @@ T> ConsumerPath matchBestPath(String requestMethod, String requestPath, List< } } - // if there is multiple candidates with wildcards then pick anyone with the least number of wildcards + // if there is multiple candidates with uri template then pick anyone with the least number of uri template ConsumerPath best = null; Map>> pathMap = new HashMap<>(); if (candidates.size() > 1) { it = candidates.iterator(); while (it.hasNext()) { ConsumerPath entry = it.next(); - int wildcards = countWildcards(entry.getConsumerPath()); - if (wildcards > 0) { - List> consumerPathsLst = pathMap.computeIfAbsent(wildcards, key -> new ArrayList<>()); + int curlyBraces = countCurlyBraces(entry.getConsumerPath()); + if (curlyBraces > 0) { + List> consumerPathsLst = pathMap.computeIfAbsent(curlyBraces, key -> new ArrayList<>()); consumerPathsLst.add(entry); } } @@ -206,12 +212,12 @@ T> ConsumerPath matchBestPath(String requestMethod, String requestPath, List< } if (best != null) { - // pick the best among the wildcards + // pick the best among uri template answer = best; } } - // if there is one left then its our answer + // if there is one left then it's our answer if (answer == null && candidates.size() == 1) { answer = candidates.get(0); } @@ -257,10 +263,10 @@ private static boolean isOptionsMethod(String method) { * Matches the given request path with the configured consumer path * * @param requestPath the request path - * @param consumerPath the consumer path which may use { } tokens + * @param isUriTemplate the consumer path which may use { } tokens * @return true if matched, false otherwise */ - private static boolean matchRestPath(String requestPath, String consumerPath, boolean wildcard) { + private static boolean matchRestPath(String requestPath, String consumerPath, boolean isUriTemplate) { // deal with null parameters if (requestPath == null && consumerPath == null) { return true; @@ -297,7 +303,7 @@ private static boolean matchRestPath(String requestPath, String consumerPath, bo String p1 = requestPaths[i]; String p2 = consumerPaths[i]; - if (wildcard && p2.startsWith("{") && p2.endsWith("}")) { + if (isUriTemplate && p2.startsWith("{") && p2.endsWith("}")) { // always matches continue; } @@ -312,13 +318,13 @@ private static boolean matchRestPath(String requestPath, String consumerPath, bo } /** - * Counts the number of wildcards in the path + * Counts the number of uri template's curlyBraces in the path * * @param consumerPath the consumer path which may use { } tokens - * @return number of wildcards, or 0 if no wildcards + * @return number of curlyBraces, or 0 if no curlyBraces */ - private static int countWildcards(String consumerPath) { - int wildcards = 0; + private static int countCurlyBraces(String consumerPath) { + int curlyBraces = 0; // remove starting/ending slashes if (consumerPath.startsWith("/")) { @@ -331,11 +337,31 @@ private static int countWildcards(String consumerPath) { String[] consumerPaths = consumerPath.split("/"); for (String p2 : consumerPaths) { if (p2.startsWith("{") && p2.endsWith("}")) { - wildcards++; + curlyBraces++; } } - return wildcards; + return curlyBraces; + } + + private static boolean matchWildCard(String requestPath, String consumerPath) { + if (!requestPath.endsWith("/")) { + requestPath = requestPath + "/"; + } + // Convert URI template to a regex pattern + String regex = consumerPath + .replace("/", "\\/") + .replace("{", "(?<") + .replace("}", ">[^\\/]+)"); + + // Add support for wildcard * as path suffix + regex = regex.replace("*", ".*"); + + // Match the provided path against the regex pattern + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(requestPath); + + return matcher.matches(); } }