This is an automated email from the ASF dual-hosted git repository.

oxsean pushed a commit to branch 3.3
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.3 by this push:
     new e8496783c8 fix diff method can not be added to RadixTree (#15445)
e8496783c8 is described below

commit e8496783c815109b7a7c6b331b78f0bb423e012a
Author: stellar <[email protected]>
AuthorDate: Wed Jun 11 18:37:52 2025 +0800

    fix diff method can not be added to RadixTree (#15445)
---
 .../mapping/DefaultRequestMappingRegistry.java     |  2 +-
 .../rpc/protocol/tri/rest/mapping/RadixTree.java   | 20 +++++-
 .../protocol/tri/rest/mapping/Registration.java    | 21 ++++++
 .../protocol/tri/rest/mapping/RequestMapping.java  |  4 ++
 .../protocol/tri/rest/mapping/RadixTreeTest.groovy | 38 +++++++++++
 .../tri/rest/mapping/RegistrationSpec.groovy       | 78 ++++++++++++++++++++++
 6 files changed, 160 insertions(+), 3 deletions(-)

diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
index ee38269b50..90f8fcc298 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
@@ -169,7 +169,7 @@ public final class DefaultRequestMappingRegistry implements 
RequestMappingRegist
         try {
             Registration registration = new Registration(mapping, handler);
             for (PathExpression path : 
mapping.getPathCondition().getExpressions()) {
-                Registration exists = tree.addPath(path, registration);
+                Registration exists = tree.addPath(path, registration, 
Registration::isMappingOverlap);
                 if (exists == null) {
                     counter.incrementAndGet();
                     if (LOGGER.isDebugEnabled()) {
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java
index 786f29ded9..b95f22ff4c 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTree.java
@@ -31,7 +31,9 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 import java.util.function.Predicate;
 
 /**
@@ -63,13 +65,25 @@ public final class RadixTree<T> {
         this(true, '/');
     }
 
+    /**
+     * This is a default implementation that does not check for equality.
+     */
     public T addPath(PathExpression path, T value) {
+        return addPath(path, value, (t, t2) -> Boolean.TRUE);
+    }
+
+    /**
+     * When the path is the same, the predicate is used to determine whether 
the values are considered equal.
+     * If the predicate returns true, the existing value is returned directly.
+     */
+    public T addPath(PathExpression path, T value, BiFunction<T, T, Boolean> 
predicate) {
+        Objects.requireNonNull(predicate);
         if (path.isDirect()) {
             KeyString key = new KeyString(path.getPath(), caseSensitive);
             List<Match<T>> matches = directPathMap.computeIfAbsent(key, k -> 
new ArrayList<>());
             for (int i = 0, size = matches.size(); i < size; i++) {
                 Match<T> match = matches.get(i);
-                if (match.getValue().equals(value)) {
+                if (predicate.apply(match.getValue(), value)) {
                     return match.getValue();
                 }
             }
@@ -85,7 +99,9 @@ public final class RadixTree<T> {
                 List<Pair<PathExpression, T>> values = child.values;
                 for (int j = 0, size = values.size(); j < size; j++) {
                     if (values.get(j).getLeft().equals(path)) {
-                        return values.get(j).getRight();
+                        if (predicate.apply(values.get(j).getRight(), value)) {
+                            return values.get(j).getRight();
+                        }
                     }
                 }
                 values.add(Pair.of(path, value));
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/Registration.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/Registration.java
index f5c993a454..ab2a381ddd 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/Registration.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/Registration.java
@@ -18,6 +18,9 @@ package org.apache.dubbo.rpc.protocol.tri.rest.mapping;
 
 import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta;
 
+import java.util.Collections;
+import java.util.Objects;
+
 public final class Registration {
 
     private final RequestMapping mapping;
@@ -47,6 +50,24 @@ public final class Registration {
         return mapping.equals(((Registration) obj).mapping);
     }
 
+    public boolean isMappingOverlap(Registration other) {
+        RequestMapping otherMapping = other.getMapping();
+        if (mapping == otherMapping) {
+            return true;
+        }
+        return (mapping.getMethodsCondition() == null
+                        || otherMapping.getMethodsCondition() == null
+                        || !Collections.disjoint(
+                                mapping.getMethodsCondition().getMethods(),
+                                
otherMapping.getMethodsCondition().getMethods()))
+                && Objects.equals(mapping.getParamsCondition(), 
otherMapping.getParamsCondition())
+                && Objects.equals(mapping.getHeadersCondition(), 
otherMapping.getHeadersCondition())
+                && Objects.equals(mapping.getConsumesCondition(), 
otherMapping.getConsumesCondition())
+                && Objects.equals(mapping.getProducesCondition(), 
otherMapping.getProducesCondition())
+                && Objects.equals(mapping.getCustomCondition(), 
otherMapping.getCustomCondition())
+                && Objects.equals(mapping.getSig(), otherMapping.getSig());
+    }
+
     @Override
     public int hashCode() {
         return mapping.hashCode();
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java
 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java
index feef0738e8..c7c5577dcf 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java
@@ -236,6 +236,10 @@ public final class RequestMapping implements 
Condition<RequestMapping, HttpReque
         return producesCondition;
     }
 
+    public ConditionWrapper getCustomCondition() {
+        return customCondition;
+    }
+
     public CorsMeta getCors() {
         return cors;
     }
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.groovy
 
b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.groovy
index 37cbb35c9c..f8ac509db6 100644
--- 
a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.groovy
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RadixTreeTest.groovy
@@ -17,6 +17,8 @@
 
 package org.apache.dubbo.rpc.protocol.tri.rest.mapping
 
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathExpression
+
 import spock.lang.Specification
 
 class RadixTreeTest extends Specification {
@@ -59,6 +61,42 @@ class RadixTreeTest extends Specification {
             '/a/b/c/d' | 0
     }
 
+    def "test repeat add,no predicate function"() {
+        given:
+            def tree = new RadixTree<Boolean>()
+            Boolean val1 = tree.addPath('/a/*/*', false)
+            Boolean val2 = tree.addPath('/a/*/*', true)
+        expect:
+            val1 == null;
+            val2 == false;
+    }
+
+    def "test repeat add,use predicate function"() {
+        given:
+            def tree = new RadixTree<Boolean>()
+            Boolean val1 = tree.addPath(PathExpression.parse('/a/*/*'), false, 
{ a, b -> a == b })
+            Boolean val2 = tree.addPath(PathExpression.parse('/a/*/*'), true, 
{ a, b -> a == b })
+            Boolean val3 = tree.addPath(PathExpression.parse('/a/*/*'), true, 
{ a, b -> a == b })
+
+        expect:
+            val1 == null;
+            val2 == null;
+        val3 == true;
+    }
+
+    def "test repeat add,use predicate function and Registration"() {
+        given:
+            def tree = new RadixTree<Boolean>()
+            Boolean val1 = tree.addPath(PathExpression.parse('/a/*/*'), false, 
{ a, b -> a == b })
+            Boolean val2 = tree.addPath(PathExpression.parse('/a/*/*'), true, 
{ a, b -> a == b })
+            Boolean val3 = tree.addPath(PathExpression.parse('/a/*/*'), true, 
{ a, b -> a == b })
+
+        expect:
+            val1 == null;
+            val2 == null;
+            val3 == true;
+    }
+
     def "test sub path match"() {
         given:
             def tree = new RadixTree<String>();
diff --git 
a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RegistrationSpec.groovy
 
b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RegistrationSpec.groovy
new file mode 100644
index 0000000000..de5a1b388c
--- /dev/null
+++ 
b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RegistrationSpec.groovy
@@ -0,0 +1,78 @@
+/*
+ * 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.dubbo.rpc.protocol.tri.rest.mapping
+
+import org.apache.dubbo.remoting.http12.HttpMethods
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta
+
+import spock.lang.Specification
+
+class RegistrationSpec extends Specification {
+
+    def "isMethodOverlap should return true when methods overlap"() {
+        given:
+            def mapping1 = new 
RequestMapping.Builder().method(HttpMethods.GET.name(), 
HttpMethods.PUT.name()).build()
+            def mapping2 = new 
RequestMapping.Builder().method(HttpMethods.PUT.name(), 
HttpMethods.POST.name()).build()
+            def meta = GroovyMock(HandlerMeta)
+            def reg1 = new Registration(mapping1, meta)
+            def reg2 = new Registration(mapping2, meta)
+
+        expect:
+            reg1.isMappingOverlap(reg2)
+    }
+
+    def "isMethodOverlap should return false when methods do not overlap"() {
+        given:
+            def mapping1 = new 
RequestMapping.Builder().method(HttpMethods.GET.name()).build()
+            def mapping2 = new 
RequestMapping.Builder().method(HttpMethods.PUT.name()).build()
+
+            def meta = GroovyMock(HandlerMeta)
+            def reg1 = new Registration(mapping1, meta)
+            def reg2 = new Registration(mapping2, meta)
+
+        expect:
+            !reg1.isMappingOverlap(reg2)
+    }
+
+    def "isMethodOverlap should return true if both MethodsCondition is 
null"() {
+        given:
+            def mapping1 = new RequestMapping.Builder().build()
+            def mapping2 = new RequestMapping.Builder().build()
+
+            def meta = GroovyMock(HandlerMeta)
+            def reg1 = new Registration(mapping1, meta)
+            def reg2 = new Registration(mapping2, meta)
+
+        expect:
+            reg1.isMappingOverlap(reg2)
+    }
+
+    def "isMethodOverlap should return false if only one MethodsCondition is 
null"() {
+        given:
+            def mapping1 = new 
RequestMapping.Builder().method(HttpMethods.GET.name()).build()
+            def mapping2 = new RequestMapping.Builder().method().build()
+
+            def meta = GroovyMock(HandlerMeta)
+            def reg1 = new Registration(mapping1, meta)
+            def reg2 = new Registration(mapping2, meta)
+
+        expect:
+            reg1.isMappingOverlap(reg2)
+    }
+
+}

Reply via email to