This is an automated email from the ASF dual-hosted git repository.
cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git
The following commit(s) were added to refs/heads/master by this push:
new beb63f6f6 GH-1668: Add catch-all for group filter (#1694)
beb63f6f6 is described below
commit beb63f6f62a0a739ea225d4206caafc2bbca3368
Author: Tamas Cservenak <[email protected]>
AuthorDate: Fri Nov 28 15:44:47 2025 +0100
GH-1668: Add catch-all for group filter (#1694)
Introduced `*` "root" entry, that may define the "default acceptance".
Fixes #1668
---
.../internal/impl/filter/ruletree/GroupTree.java | 15 ++-
.../aether/internal/impl/filter/ruletree/Node.java | 6 +-
.../impl/filter/ruletree/GroupTreeTest.java | 118 +++++++++++++++++++++
src/site/markdown/remote-repository-filtering.md | 10 ++
4 files changed, 145 insertions(+), 4 deletions(-)
diff --git
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTree.java
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTree.java
index e2d0295d3..b4e29dfbd 100644
---
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTree.java
+++
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTree.java
@@ -36,7 +36,10 @@ import static java.util.stream.Collectors.toList;
* <li>a valid Maven groupID ie "org.apache.maven".</li>
* </ul>
* By default, a G entry ie {@code org.apache.maven} means "allow {@code
org.apache.maven} G and all Gs below
- * (so {@code org.apache.maven.plugins} etc. are all allowed).
+ * (so {@code org.apache.maven.plugins} etc. are all allowed). There is one
special entry {@code "*"} (asterisk)
+ * that means "root" and defines the default acceptance: {@code "*"} means "by
default accept" and {@code "!*"}
+ * means "by default deny" (same effect as when this character is not present
in file). Use of limiter modifier
+ * on "root" like {@code "=*"} has no effect, is simply ignored.
*
* Examples:
* <pre>
@@ -61,6 +64,7 @@ import static java.util.stream.Collectors.toList;
public class GroupTree extends Node {
public static final GroupTree SENTINEL = new GroupTree("sentinel");
+ private static final String ROOT = "*";
private static final String MOD_EXCLUSION = "!";
private static final String MOD_STOP = "=";
@@ -69,7 +73,7 @@ public class GroupTree extends Node {
}
public GroupTree(String name) {
- super(name, false, null);
+ super(name, false, false);
}
public int loadNodes(Stream<String> linesStream) {
@@ -95,6 +99,10 @@ public class GroupTree extends Node {
stop = true;
line = line.substring(MOD_STOP.length());
}
+ if (ROOT.equals(line)) {
+ this.setAllow(allow);
+ return true;
+ }
List<String> groupElements = elementsOfGroup(line);
for (String groupElement : groupElements.subList(0,
groupElements.size() - 1)) {
currentNode = currentNode.addSibling(groupElement, false,
null);
@@ -122,6 +130,7 @@ public class GroupTree extends Node {
accepted = currentNode.isAllow();
}
}
- return accepted != null && accepted;
+ // use `accepted`, if defined; otherwise fallback to root (it always
has `allow` set)
+ return accepted != null ? accepted : this.isAllow();
}
}
diff --git
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/Node.java
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/Node.java
index 453a08704..beccf29c2 100644
---
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/Node.java
+++
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/filter/ruletree/Node.java
@@ -26,7 +26,7 @@ import java.util.HashMap;
class Node {
private final String name;
private final boolean stop;
- private final Boolean allow;
+ private Boolean allow;
private final HashMap<String, Node> siblings;
protected Node(String name, boolean stop, Boolean allow) {
@@ -52,6 +52,10 @@ class Node {
return allow;
}
+ public void setAllow(Boolean allow) {
+ this.allow = allow;
+ }
+
protected Node addSibling(String name, boolean stop, Boolean allow) {
return siblings.computeIfAbsent(name, n -> new Node(n, stop, allow));
}
diff --git
a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTreeTest.java
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTreeTest.java
index ffe8cc170..1cdecd136 100644
---
a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTreeTest.java
+++
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/filter/ruletree/GroupTreeTest.java
@@ -18,10 +18,13 @@
*/
package org.eclipse.aether.internal.impl.filter.ruletree;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -50,5 +53,120 @@ public class GroupTreeTest {
assertTrue(groupTree.acceptedGroupId("org.apache.baz"));
assertFalse(groupTree.acceptedGroupId("org.apache.baz.aaa"));
+
+ assertFalse(groupTree.acceptedGroupId("not.in.list.but.uses.default"));
+ }
+
+ @Test
+ void smokeWithPositiveDefault() {
+ GroupTree groupTree = new GroupTree("root");
+ groupTree.loadNodes(Stream.of(
+ "# comment",
+ "",
+ "org.apache.maven",
+ "!=org.apache.maven.foo",
+ "!org.apache.maven.bar",
+ "=org.apache.baz",
+ "*"));
+ assertTrue(groupTree.acceptedGroupId("org.apache.maven"));
+ assertTrue(groupTree.acceptedGroupId("org.apache.maven.aaa"));
+
+ assertFalse(groupTree.acceptedGroupId("org.apache.maven.foo"));
+ assertTrue(groupTree.acceptedGroupId("org.apache.maven.foo.aaa"));
+
+ assertFalse(groupTree.acceptedGroupId("org.apache.maven.bar"));
+ assertFalse(groupTree.acceptedGroupId("org.apache.maven.bar.aaa"));
+
+ assertTrue(groupTree.acceptedGroupId("org.apache.baz"));
+ assertTrue(groupTree.acceptedGroupId("org.apache.baz.aaa"));
+
+ assertTrue(groupTree.acceptedGroupId("not.in.list.but.uses.default"));
+ }
+
+ @Test
+ void smokeWithPositiveDefaultExclusionsOnly() {
+ GroupTree groupTree = new GroupTree("root");
+ groupTree.loadNodes(Stream.of("# comment", "*",
"!org.apache.maven.foo", "!=org.apache.maven.bar"));
+ // no applicable rule; root=ALLOW
+ assertTrue(groupTree.acceptedGroupId("org.apache.maven"));
+ assertTrue(groupTree.acceptedGroupId("org.apache.maven.aaa"));
+
+ // exclusion rule present (this and below)
+ assertFalse(groupTree.acceptedGroupId("org.apache.maven.foo"));
+ assertFalse(groupTree.acceptedGroupId("org.apache.maven.foo.aaa"));
+
+ // exclusion+stop rule present (only this)
+ assertFalse(groupTree.acceptedGroupId("org.apache.maven.bar"));
+ assertTrue(groupTree.acceptedGroupId("org.apache.maven.bar.aaa"));
+
+ // no applicable rule; root=ALLOW
+ assertTrue(groupTree.acceptedGroupId("not.in.list.but.uses.default"));
+ }
+
+ @Test
+ void smokeWithNegativeDefault() {
+ GroupTree groupTree = new GroupTree("root");
+ groupTree.loadNodes(Stream.of(
+ "# comment",
+ "",
+ "org.apache.maven",
+ "!=org.apache.maven.foo",
+ "!org.apache.maven.bar",
+ "=org.apache.baz",
+ "!*"));
+ assertTrue(groupTree.acceptedGroupId("org.apache.maven"));
+ assertTrue(groupTree.acceptedGroupId("org.apache.maven.aaa"));
+
+ assertFalse(groupTree.acceptedGroupId("org.apache.maven.foo"));
+ assertTrue(groupTree.acceptedGroupId("org.apache.maven.foo.aaa"));
+
+ assertFalse(groupTree.acceptedGroupId("org.apache.maven.bar"));
+ assertFalse(groupTree.acceptedGroupId("org.apache.maven.bar.aaa"));
+
+ assertTrue(groupTree.acceptedGroupId("org.apache.baz"));
+ assertFalse(groupTree.acceptedGroupId("org.apache.baz.aaa"));
+
+ assertFalse(groupTree.acceptedGroupId("not.in.list.but.uses.default"));
+ }
+
+ @Test
+ void implicitAndExplicitDefaultIsSame() {
+ // w/o asterisk: uses coded defaults
+ GroupTree implicitTree = new GroupTree("root");
+ implicitTree.loadNodes(Stream.of(
+ "# comment",
+ "",
+ "org.apache.maven",
+ "!=org.apache.maven.foo",
+ "!org.apache.maven.bar",
+ "=org.apache.baz"));
+ HashMap<String, Boolean> implicitResults = new HashMap<>();
+ // w/ asterisk: set to same value as default (should cause no change)
+ GroupTree explicitTree = new GroupTree("root");
+ explicitTree.loadNodes(Stream.of(
+ "# comment",
+ "",
+ "org.apache.maven",
+ "!=org.apache.maven.foo",
+ "!org.apache.maven.bar",
+ "=org.apache.baz",
+ "!*"));
+ HashMap<String, Boolean> explicitResults = new HashMap<>();
+
+ for (String key : Arrays.asList(
+ "org.apache.maven",
+ "org.apache.maven.aaa",
+ "org.apache.maven.foo",
+ "org.apache.maven.foo.aaa",
+ "org.apache.maven.bar",
+ "org.apache.maven.bar.aaa",
+ "org.apache.baz",
+ "org.apache.baz.aaa",
+ "not.in.list.but.uses.default")) {
+ implicitResults.put(key, implicitTree.acceptedGroupId(key));
+ explicitResults.put(key, explicitTree.acceptedGroupId(key));
+ }
+
+ assertEquals(implicitResults, explicitResults);
}
}
diff --git a/src/site/markdown/remote-repository-filtering.md
b/src/site/markdown/remote-repository-filtering.md
index c8420d514..8012895fd 100644
--- a/src/site/markdown/remote-repository-filtering.md
+++ b/src/site/markdown/remote-repository-filtering.md
@@ -181,6 +181,16 @@ Lines 1 and 2 are ignored. Line 3 means "allow
`org.apache.maven` G and below".
only" (so `org.apache.maven.foo.bar` is allowed due first line). Line 5 means
"disallow `org.apache.maven.indexer` and below"
and finally line 6 means "allow `org.apache.bar` ONLY" (so
`org.apache.bar.foo` is NOT enabled).
+One can use one special entry "root" `*` (asterisk) to define the "default
acceptance" (that without it
+defaults to REJECTED). Similarly, adding `!*` to file defines "default
acceptance" of FALSE/REJECTED as well,
+and adding it to file changes nothing, as this is the default acceptance (but
may serve some documentation purposes).
+Be aware: In case a line with single asterisk `*` is present, the whole logic
of Group filter is getting inverted,
+hence there is no need to add "allowed entries" (they are allowed by default),
but one can add "disallowed entries" by
+adding `!com.foo` and alike.
+
+Conflicting rules: rule parser is intentionally trivial, so in case of
conflicting rules the "first wins" strategy is
+applied. Ideally, user should keep files sorted or handle them in a way one
can detect conflicts in it.
+
## Operation
To make RRF filters operate, as they are by default enabled, you have to make
sure that: