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)
+ }
+
+}