You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/06/01 20:40:35 UTC

(maven) branch maven-4.0.x-test-fixes updated (9d5e53be36 -> 759fa5d831)

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

gnodet pushed a change to branch maven-4.0.x-test-fixes
in repository https://gitbox.apache.org/repos/asf/maven.git


 discard 9d5e53be36 Remove deployBuildPom() — constant not available on maven-4.0.x
 discard 7ff9c06a2b Preserve default-phases bindings for standard lifecycle phases (PR #11889)
 discard ded2a5a7c8 Improve error message for prefix-based remote repository filtering (PR #11888)
 discard 192a3cb450 Re-enable MNG-6772 integration test for nested import scope (PR #11826)
 discard 113dbbee51 Fix @PreDestroy ClassNotFoundException from premature ClassRealm disposal (PR #11825)
 discard ccbacead3e Fail-fast consumer POM validation for non-4.0.0 model versions (PR #11780)
     new 76da9fe46c Fix @PreDestroy ClassNotFoundException from premature ClassRealm disposal (PR #11825)
     new e8131a33a0 Re-enable MNG-6772 integration test for nested import scope (PR #11826)
     new a960ebcbe6 Improve error message for prefix-based remote repository filtering (PR #11888)
     new 759fa5d831 Preserve default-phases bindings for standard lifecycle phases (PR #11889)

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (9d5e53be36)
            \
             N -- N -- N   refs/heads/maven-4.0.x-test-fixes (759fa5d831)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .gitignore                                         |  2 -
 .../main/java/org/apache/maven/api/Constants.java  | 15 ----
 .../org/apache/maven/api/feature/Features.java     |  7 --
 .../impl/DefaultConsumerPomBuilder.java            | 37 ++-------
 .../impl/ConsumerPomBuilderTest.java               | 15 ----
 .../consumer/parent-with-conditions/pom.xml        | 32 -------
 .../maven/it/MavenITgh11772ConsumerPom410Test.java | 97 ----------------------
 .../gh-11772-consumer-pom-410/child/pom.xml        | 38 ---------
 .../resources/gh-11772-consumer-pom-410/pom.xml    | 41 ---------
 9 files changed, 9 insertions(+), 275 deletions(-)
 delete mode 100644 impl/maven-core/src/test/resources/consumer/parent-with-conditions/pom.xml
 delete mode 100644 its/core-it-suite/src/test/java/org/apache/maven/it/MavenITgh11772ConsumerPom410Test.java
 delete mode 100644 its/core-it-suite/src/test/resources/gh-11772-consumer-pom-410/child/pom.xml
 delete mode 100644 its/core-it-suite/src/test/resources/gh-11772-consumer-pom-410/pom.xml


(maven) 04/04: Preserve default-phases bindings for standard lifecycle phases (PR #11889)

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch maven-4.0.x-test-fixes
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 759fa5d83167bf102cc7bb5d538da0034686404d
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Mon Jun 1 21:54:25 2026 +0200

    Preserve default-phases bindings for standard lifecycle phases (PR #11889)
    
    Cherry-pick from ci-issue-11796 branch.
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 .../internal/impl/DefaultLifecycleRegistry.java    | 63 ++++++++++++++++++++--
 .../maven/lifecycle/DefaultLifecyclesTest.java     | 51 ++++++++++++++++++
 ...mng11796DefaultPhasesStandardLifecycleTest.java | 57 ++++++++++++++++++++
 .../consumer-project/pom.xml                       | 22 ++++++++
 .../extension-plugin/pom.xml                       | 39 ++++++++++++++
 .../org/apache/maven/its/mng11796/TouchMojo.java   | 47 ++++++++++++++++
 .../main/resources/META-INF/plexus/components.xml  | 20 +++++++
 7 files changed, 294 insertions(+), 5 deletions(-)

diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java
index 84abf9fbb7..d9a7058df0 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultLifecycleRegistry.java
@@ -227,6 +227,64 @@ public String id() {
 
                 @Override
                 public Collection<Phase> phases() {
+                    List<Phase> phases = new ArrayList<>(buildOwnPhases());
+                    // Also include phases for default-phases entries that reference phases
+                    // not defined in this lifecycle (e.g., standard lifecycle phases like
+                    // process-sources). This preserves plugin bindings from components.xml
+                    // <default-phases> that map goals to standard lifecycle phases.
+                    Map<String, LifecyclePhase> lfPhases = lifecycle.getDefaultLifecyclePhases();
+                    if (lfPhases != null) {
+                        Set<String> ownPhaseNames = new HashSet<>(lifecycle.getPhases());
+                        for (Map.Entry<String, LifecyclePhase> entry : lfPhases.entrySet()) {
+                            if (!ownPhaseNames.contains(entry.getKey())) {
+                                String phaseName = entry.getKey();
+                                LifecyclePhase lfPhase = entry.getValue();
+                                phases.add(new Phase() {
+                                    @Override
+                                    public String name() {
+                                        return phaseName;
+                                    }
+
+                                    @Override
+                                    public List<Phase> phases() {
+                                        return List.of();
+                                    }
+
+                                    @Override
+                                    public Stream<Phase> allPhases() {
+                                        return Stream.of(this);
+                                    }
+
+                                    @Override
+                                    public List<Plugin> plugins() {
+                                        Map<String, Plugin> plugins = new LinkedHashMap<>();
+                                        DefaultPackagingRegistry.parseLifecyclePhaseDefinitions(
+                                                plugins, phaseName, lfPhase);
+                                        return plugins.values().stream().toList();
+                                    }
+
+                                    @Override
+                                    public Collection<Link> links() {
+                                        return List.of();
+                                    }
+                                });
+                            }
+                        }
+                    }
+                    return phases;
+                }
+
+                @Override
+                public Collection<Phase> v3phases() {
+                    return buildOwnPhases();
+                }
+
+                @Override
+                public Collection<Alias> aliases() {
+                    return Collections.emptyList();
+                }
+
+                private List<Phase> buildOwnPhases() {
                     List<String> names = lifecycle.getPhases();
                     List<Phase> phases = new ArrayList<>();
                     for (int i = 0; i < names.size(); i++) {
@@ -293,11 +351,6 @@ public Type type() {
                     }
                     return phases;
                 }
-
-                @Override
-                public Collection<Alias> aliases() {
-                    return Collections.emptyList();
-                }
             };
         }
     }
diff --git a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java
index 0856e5ce6e..c848970ef7 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/lifecycle/DefaultLifecyclesTest.java
@@ -23,12 +23,14 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
 import org.apache.maven.internal.impl.DefaultLifecycleRegistry;
 import org.apache.maven.internal.impl.DefaultLookup;
+import org.apache.maven.lifecycle.mapping.LifecyclePhase;
 import org.codehaus.plexus.PlexusContainer;
 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 import org.codehaus.plexus.testing.PlexusTest;
@@ -38,6 +40,9 @@
 import static org.hamcrest.Matchers.arrayWithSize;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -99,6 +104,52 @@ void testCustomLifecycle() throws ComponentLookupException {
         assertThat(dl.getLifeCycles().get(3).getId(), is("etl"));
     }
 
+    /**
+     * Test for <a href="https://github.com/apache/maven/issues/11796">MNG-11796</a>:
+     * custom lifecycle with {@code <default-phases>} binding goals to standard lifecycle phases.
+     */
+    @Test
+    void testCustomLifecycleDefaultPhasesForStandardPhases() throws ComponentLookupException {
+        // Create a custom lifecycle with default-phases mapping to a standard lifecycle phase
+        Map<String, LifecyclePhase> defaultPhases = new HashMap<>();
+        defaultPhases.put("process-sources", new LifecyclePhase("com.example:my-plugin:touch"));
+
+        Lifecycle customLifecycle = new Lifecycle("my-extension", Arrays.asList("my-dummy-phase"), defaultPhases);
+
+        List<Lifecycle> allLifecycles = new ArrayList<>();
+        allLifecycles.add(customLifecycle);
+        allLifecycles.addAll(defaultLifeCycles.getLifeCycles());
+
+        Map<String, Lifecycle> lifeCycles = allLifecycles.stream().collect(Collectors.toMap(Lifecycle::getId, l -> l));
+        PlexusContainer mockedPlexusContainer = mock(PlexusContainer.class);
+        when(mockedPlexusContainer.lookupMap(Lifecycle.class)).thenReturn(lifeCycles);
+
+        DefaultLifecycles dl = new DefaultLifecycles(
+                new DefaultLifecycleRegistry(
+                        List.of(new DefaultLifecycleRegistry.LifecycleWrapperProvider(mockedPlexusContainer))),
+                new DefaultLookup(mockedPlexusContainer));
+
+        // Find the custom lifecycle
+        Lifecycle result = dl.getLifeCycles().stream()
+                .filter(l -> "my-extension".equals(l.getId()))
+                .findFirst()
+                .orElseThrow(() -> new AssertionError("Custom lifecycle not found"));
+
+        // Verify that getPhases() only contains the custom phase (not standard phases)
+        assertTrue(result.getPhases().contains("my-dummy-phase"), "Custom lifecycle should contain its own phase");
+
+        // Verify that defaultLifecyclePhases includes the standard phase binding
+        Map<String, LifecyclePhase> resultDefaultPhases = result.getDefaultLifecyclePhases();
+        assertNotNull(resultDefaultPhases, "Default lifecycle phases should not be null");
+        assertTrue(
+                resultDefaultPhases.containsKey("process-sources"),
+                "Default lifecycle phases should contain 'process-sources' binding");
+        String goalSpec = resultDefaultPhases.get("process-sources").toString();
+        assertTrue(
+                goalSpec.contains("com.example") && goalSpec.contains("my-plugin") && goalSpec.contains("touch"),
+                "The process-sources binding should map to the correct goal, got: " + goalSpec);
+    }
+
     private Lifecycle getLifeCycleById(String id) {
         return defaultLifeCycles.getLifeCycles().stream()
                 .filter(l -> id.equals(l.getId()))
diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng11796DefaultPhasesStandardLifecycleTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng11796DefaultPhasesStandardLifecycleTest.java
new file mode 100644
index 0000000000..f1b96b05cc
--- /dev/null
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng11796DefaultPhasesStandardLifecycleTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.maven.it;
+
+import java.io.File;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * This is a test for
+ * <a href="https://github.com/apache/maven/issues/11796">MNG-11796</a>.
+ * <p>
+ * Verifies that {@code <default-phases>} in a custom lifecycle's {@code components.xml}
+ * correctly binds plugin goals to standard lifecycle phases (e.g., {@code process-sources}).
+ */
+class MavenITmng11796DefaultPhasesStandardLifecycleTest extends AbstractMavenIntegrationTestCase {
+
+    /**
+     * Verify that a plugin extension with {@code <default-phases>} mapping a goal
+     * to the standard {@code process-sources} phase causes that goal to execute
+     * during {@code mvn compile}.
+     */
+    @Test
+    void testDefaultPhasesBindToStandardLifecyclePhases() throws Exception {
+        File testDir = extractResources("/mng-11796-default-phases-standard-lifecycle");
+
+        // Install the extension plugin
+        Verifier verifier = newVerifier(new File(testDir, "extension-plugin").getAbsolutePath());
+        verifier.addCliArgument("install");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+
+        // Run compile on the consumer project - the touch goal should execute at process-sources
+        verifier = newVerifier(new File(testDir, "consumer-project").getAbsolutePath());
+        verifier.addCliArgument("compile");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+        verifier.verifyTextInLog("MNG-11796 touch goal executed");
+        verifier.verifyFilePresent("target/touch.txt");
+    }
+}
diff --git a/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/consumer-project/pom.xml b/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/consumer-project/pom.xml
new file mode 100644
index 0000000000..75b1e9ef8d
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/consumer-project/pom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.its.mng11796</groupId>
+  <artifactId>consumer-project</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <name>MNG-11796 Consumer Project</name>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.its.mng11796</groupId>
+        <artifactId>extension-plugin</artifactId>
+        <version>1.0-SNAPSHOT</version>
+        <extensions>true</extensions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/extension-plugin/pom.xml b/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/extension-plugin/pom.xml
new file mode 100644
index 0000000000..68be77394e
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/extension-plugin/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.its.mng11796</groupId>
+  <artifactId>extension-plugin</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>maven-plugin</packaging>
+  <name>MNG-11796 Extension Plugin</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>3.2.5</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+        <version>3.6.4</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>descriptor</goal>
+            </goals>
+            <configuration>
+              <goalPrefix>mng11796</goalPrefix>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/extension-plugin/src/main/java/org/apache/maven/its/mng11796/TouchMojo.java b/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/extension-plugin/src/main/java/org/apache/maven/its/mng11796/TouchMojo.java
new file mode 100644
index 0000000000..abed805eaf
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/extension-plugin/src/main/java/org/apache/maven/its/mng11796/TouchMojo.java
@@ -0,0 +1,47 @@
+/*
+ * 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.maven.its.mng11796;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+
+/**
+ * Creates a marker file to prove the goal was executed.
+ * @goal touch
+ */
+public class TouchMojo extends AbstractMojo {
+    /**
+     * @parameter default-value="${project.build.directory}"
+     */
+    private File outputDirectory;
+
+    public void execute() throws MojoExecutionException {
+        getLog().info("MNG-11796 touch goal executed");
+        File touchFile = new File(outputDirectory, "touch.txt");
+        touchFile.getParentFile().mkdirs();
+        try {
+            touchFile.createNewFile();
+        } catch (IOException e) {
+            throw new MojoExecutionException("Failed to create touch file", e);
+        }
+    }
+}
diff --git a/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/extension-plugin/src/main/resources/META-INF/plexus/components.xml b/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/extension-plugin/src/main/resources/META-INF/plexus/components.xml
new file mode 100644
index 0000000000..dffe42cf06
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-11796-default-phases-standard-lifecycle/extension-plugin/src/main/resources/META-INF/plexus/components.xml
@@ -0,0 +1,20 @@
+<component-set>
+  <components>
+    <component>
+      <role>org.apache.maven.lifecycle.Lifecycle</role>
+      <role-hint>mng11796</role-hint>
+      <implementation>org.apache.maven.lifecycle.Lifecycle</implementation>
+      <configuration>
+        <id>mng11796</id>
+        <phases>
+          <phase>mng11796-dummy-phase</phase>
+        </phases>
+        <default-phases>
+          <process-sources>
+            org.apache.maven.its.mng11796:extension-plugin:touch
+          </process-sources>
+        </default-phases>
+      </configuration>
+    </component>
+  </components>
+</component-set>


(maven) 03/04: Improve error message for prefix-based remote repository filtering (PR #11888)

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch maven-4.0.x-test-fixes
in repository https://gitbox.apache.org/repos/asf/maven.git

commit a960ebcbe6ced33d70b94bd7bc2501b10f1606e1
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Mon Jun 1 21:53:52 2026 +0200

    Improve error message for prefix-based remote repository filtering (PR #11888)
    
    Cherry-pick from ci-issue-11856 branch.
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 .../maven/exception/DefaultExceptionHandler.java   | 34 +++++++++++++++++-
 .../exception/DefaultExceptionHandlerTest.java     | 40 ++++++++++++++++++++++
 2 files changed, 73 insertions(+), 1 deletion(-)

diff --git a/impl/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java b/impl/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java
index 98db980812..fdf2441bec 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/exception/DefaultExceptionHandler.java
@@ -40,6 +40,7 @@
 import org.apache.maven.plugin.PluginExecutionException;
 import org.apache.maven.project.ProjectBuildingException;
 import org.apache.maven.project.ProjectBuildingResult;
+import org.eclipse.aether.transfer.ArtifactFilteredOutException;
 
 /*
 
@@ -177,6 +178,9 @@ private String getReference(Set<Throwable> dejaVu, Throwable exception) {
                         reference = ConnectException.class.getSimpleName();
                     }
                 }
+                if (findCause(exception, ArtifactFilteredOutException.class) != null) {
+                    reference = "https://maven.apache.org/resolver/remote-repository-filtering.html";
+                }
             } else if (exception instanceof LinkageError) {
                 reference = LinkageError.class.getSimpleName();
             } else if (exception instanceof PluginExecutionException) {
@@ -207,7 +211,9 @@ private String getReference(Set<Throwable> dejaVu, Throwable exception) {
             }
         }
 
-        if ((reference != null && !reference.isEmpty()) && !reference.startsWith("http:")) {
+        if ((reference != null && !reference.isEmpty())
+                && !reference.startsWith("http:")
+                && !reference.startsWith("https:")) {
             reference = "http://cwiki.apache.org/confluence/display/MAVEN/" + reference;
         }
 
@@ -229,6 +235,8 @@ private boolean isNoteworthyException(Throwable exception) {
     private String getMessage(String message, Throwable exception) {
         String fullMessage = (message != null) ? message : "";
 
+        boolean hasArtifactFilteredOut = false;
+
         // To break out of possible endless loop when getCause returns "this", or dejaVu for n-level recursion (n>1)
         Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
         for (Throwable t = exception; t != null && t != t.getCause(); t = t.getCause()) {
@@ -260,15 +268,39 @@ private String getMessage(String message, Throwable exception) {
                 fullMessage = join(fullMessage, exceptionMessage);
             }
 
+            if (t instanceof ArtifactFilteredOutException) {
+                hasArtifactFilteredOut = true;
+            }
+
             if (!dejaVu.add(t)) {
                 fullMessage = join(fullMessage, "[CIRCULAR REFERENCE]");
                 break;
             }
         }
 
+        if (hasArtifactFilteredOut) {
+            fullMessage += """
+
+                This error indicates that a remote repository filter has rejected this artifact. This commonly happens with repository managers using virtual/group repositories that do not properly aggregate prefix files.
+                To disable remote repository filtering, add one or both of these to your command line or to .mvn/maven.config:
+                  -Daether.remoteRepositoryFilter.prefixes=false
+                  -Daether.remoteRepositoryFilter.groupId=false
+                See https://maven.apache.org/resolver/remote-repository-filtering.html""";
+        }
+
         return fullMessage.trim();
     }
 
+    private static <T extends Throwable> T findCause(Throwable exception, Class<T> type) {
+        Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
+        for (Throwable t = exception; t != null && dejaVu.add(t); t = t.getCause()) {
+            if (type.isInstance(t)) {
+                return type.cast(t);
+            }
+        }
+        return null;
+    }
+
     private String join(String message1, String message2) {
         String message = "";
 
diff --git a/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java b/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java
index c9a84014d2..88359e2e8f 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/exception/DefaultExceptionHandlerTest.java
@@ -29,9 +29,15 @@
 import org.apache.maven.plugin.PluginExecutionException;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.transfer.ArtifactFilteredOutException;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  */
@@ -124,6 +130,40 @@ public synchronized Throwable getCause() {
         assertEquals(expectedReference, summary.getReference());
     }
 
+    @Test
+    void testArtifactFilteredOutException() {
+        RemoteRepository repo =
+                new RemoteRepository.Builder("my-repo", "default", "https://repo.example.com/maven").build();
+        ArtifactFilteredOutException filterEx = new ArtifactFilteredOutException(
+                new DefaultArtifact("com.example:my-lib:jar:1.0"),
+                repo,
+                "Prefix com/example/my-lib/1.0/my-lib-1.0.jar NOT allowed from my-repo"
+                        + " (https://repo.example.com/maven, default, releases)");
+        ArtifactResult artifactResult = new ArtifactResult(new org.eclipse.aether.resolution.ArtifactRequest(
+                new DefaultArtifact("com.example:my-lib:jar:1.0"), java.util.List.of(repo), null));
+        artifactResult.addException(filterEx);
+        ArtifactResolutionException resolutionEx =
+                new ArtifactResolutionException(java.util.List.of(artifactResult), "Could not resolve artifact");
+        MojoExecutionException mojoEx = new MojoExecutionException("Resolution failed", resolutionEx);
+
+        DefaultExceptionHandler handler = new DefaultExceptionHandler();
+        ExceptionSummary summary = handler.handleException(mojoEx);
+
+        assertTrue(
+                summary.getMessage().contains("-Daether.remoteRepositoryFilter.prefixes=false"),
+                "Message should contain the prefixes workaround property");
+        assertTrue(
+                summary.getMessage().contains("-Daether.remoteRepositoryFilter.groupId=false"),
+                "Message should contain the groupId workaround property");
+        assertTrue(
+                summary.getMessage().contains("remote repository filter"),
+                "Message should explain the filtering cause");
+        assertEquals(
+                "https://maven.apache.org/resolver/remote-repository-filtering.html",
+                summary.getReference(),
+                "Reference should point to the RRF documentation");
+    }
+
     @Test
     void testHandleExceptionSelfReferencing() {
         RuntimeException boom3 = new RuntimeException("BOOM3");


(maven) 01/04: Fix @PreDestroy ClassNotFoundException from premature ClassRealm disposal (PR #11825)

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch maven-4.0.x-test-fixes
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 76da9fe46c9fbdb4c59224648d96a3609eb8ee82
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Mon Jun 1 21:53:38 2026 +0200

    Fix @PreDestroy ClassNotFoundException from premature ClassRealm disposal (PR #11825)
    
    Cherry-pick from fix/realm-cache-dispose-ordering branch.
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 .../maven/plugin/DefaultExtensionRealmCache.java   |  2 +-
 .../maven/plugin/DefaultPluginRealmCache.java      |  2 +-
 .../maven/project/DefaultProjectRealmCache.java    |  2 +-
 .../maven/plugin/DefaultRealmCacheDisposeTest.java | 85 ++++++++++++++++++++++
 4 files changed, 88 insertions(+), 3 deletions(-)

diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java
index b202115258..ce3cb5135f 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java
@@ -149,6 +149,6 @@ public void register(MavenProject project, Key key, CacheRecord record) {
 
     @Override
     public void dispose() {
-        flush();
+        cache.clear();
     }
 }
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java
index 681955f21d..1e822a2ccb 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java
@@ -215,6 +215,6 @@ public void register(MavenProject project, Key key, CacheRecord record) {
 
     @Override
     public void dispose() {
-        flush();
+        cache.clear();
     }
 }
diff --git a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java
index 82a1a814c1..9111177c34 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java
@@ -125,6 +125,6 @@ public void register(MavenProject project, Key key, CacheRecord record) {
 
     @Override
     public void dispose() {
-        flush();
+        cache.clear();
     }
 }
diff --git a/impl/maven-core/src/test/java/org/apache/maven/plugin/DefaultRealmCacheDisposeTest.java b/impl/maven-core/src/test/java/org/apache/maven/plugin/DefaultRealmCacheDisposeTest.java
new file mode 100644
index 0000000000..4edf41a61a
--- /dev/null
+++ b/impl/maven-core/src/test/java/org/apache/maven/plugin/DefaultRealmCacheDisposeTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.maven.plugin;
+
+import java.util.List;
+
+import org.apache.maven.artifact.Artifact;
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Verifies that dispose() does not dispose ClassRealms prematurely.
+ * <p>
+ * Plexus Disposable.dispose() runs before Sisu's @PreDestroy callbacks.
+ * If dispose() disposes ClassRealms, beans loaded from those realms will
+ * get ClassNotFoundException when their @PreDestroy methods execute.
+ * dispose() should only clear the cache map; flush() should dispose realms.
+ *
+ * @see <a href="https://github.com/apache/maven/issues/10571">MNG-8572</a>
+ */
+class DefaultRealmCacheDisposeTest {
+
+    @Test
+    void disposeDoesNotDisposeClassRealms() throws Exception {
+        ClassWorld world = new ClassWorld();
+        ClassRealm realm = world.newRealm("test-plugin-realm");
+
+        DefaultPluginRealmCache cache = new DefaultPluginRealmCache();
+        PluginRealmCache.CacheRecord record = new PluginRealmCache.CacheRecord(realm, List.<Artifact>of());
+        cache.cache.put(new TestKey(), record);
+
+        cache.dispose();
+
+        assertTrue(cache.cache.isEmpty(), "dispose() should clear the cache");
+        assertNotNull(world.getClassRealm("test-plugin-realm"), "dispose() should NOT dispose the ClassRealm");
+    }
+
+    @Test
+    void flushDisposesClassRealms() throws Exception {
+        ClassWorld world = new ClassWorld();
+        ClassRealm realm = world.newRealm("test-plugin-realm-flush");
+
+        DefaultPluginRealmCache cache = new DefaultPluginRealmCache();
+        PluginRealmCache.CacheRecord record = new PluginRealmCache.CacheRecord(realm, List.<Artifact>of());
+        cache.cache.put(new TestKey(), record);
+
+        cache.flush();
+
+        assertTrue(cache.cache.isEmpty(), "flush() should clear the cache");
+        assertNull(world.getClassRealm("test-plugin-realm-flush"), "flush() SHOULD dispose the ClassRealm");
+    }
+
+    private static class TestKey implements PluginRealmCache.Key {
+        @Override
+        public int hashCode() {
+            return 1;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj instanceof TestKey;
+        }
+    }
+}


(maven) 02/04: Re-enable MNG-6772 integration test for nested import scope (PR #11826)

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch maven-4.0.x-test-fixes
in repository https://gitbox.apache.org/repos/asf/maven.git

commit e8131a33a0d9954514cb7aba2145ba2f2bd48424
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Mon Jun 1 21:53:43 2026 +0200

    Re-enable MNG-6772 integration test for nested import scope (PR #11826)
    
    Cherry-pick from fix/enable-mng6772-it branch.
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 .../maven/it/MavenITmng6772NestedImportScopeRepositoryOverride.java     | 2 --
 1 file changed, 2 deletions(-)

diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6772NestedImportScopeRepositoryOverride.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6772NestedImportScopeRepositoryOverride.java
index ae8cbe9c19..dec8f72ea1 100644
--- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6772NestedImportScopeRepositoryOverride.java
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng6772NestedImportScopeRepositoryOverride.java
@@ -20,7 +20,6 @@
 
 import java.io.File;
 
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -33,7 +32,6 @@
  * The test confirms that the dominant repository definition (child) wins while resolving the import POMs.
  *
  */
-@Disabled // This IT has been disabled until it is decided how the solution shall look like
 public class MavenITmng6772NestedImportScopeRepositoryOverride extends AbstractMavenIntegrationTestCase {
 
     public MavenITmng6772NestedImportScopeRepositoryOverride() {