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

gnodet pushed a commit to branch mvnup
in repository https://gitbox.apache.org/repos/asf/maven.git

commit f8792df3411113eba798f2644852ffcad3a06e29
Author: Guillaume Nodet <gno...@gmail.com>
AuthorDate: Fri May 30 00:18:36 2025 +0200

    Remove duplicate headers
---
 .../cling/invoker/mvnup/goals/BaseUpgradeGoal.java |   4 +
 .../invoker/mvnup/goals/ParentPomResolver.java     | 279 +++++++++++++++++++++
 .../invoker/mvnup/jdom/JDomContentHelper.java      |  19 --
 .../invoker/mvnup/jdom/JDomPomCleanupHelper.java   |  19 --
 .../maven/cling/invoker/mvnup/jdom/JDomUtils.java  |  19 --
 .../invoker/mvnup/goals/PluginUpgradeTest.java     | 108 ++++++++
 6 files changed, 391 insertions(+), 57 deletions(-)

diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/BaseUpgradeGoal.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/BaseUpgradeGoal.java
index c3574c1531..38f810b92e 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/BaseUpgradeGoal.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/BaseUpgradeGoal.java
@@ -1423,6 +1423,7 @@ protected String getChildText(Element parent, String 
childName, Namespace namesp
     /**
      * Upgrades plugins in a POM document.
      * Checks both build/plugins and build/pluginManagement/plugins sections.
+     * Also checks parent POMs for plugins that need to be managed locally.
      */
     protected boolean upgradePluginsInPom(UpgradeContext context, Document 
pomDocument) {
         Element root = pomDocument.getRootElement();
@@ -1457,6 +1458,9 @@ protected boolean upgradePluginsInPom(UpgradeContext 
context, Document pomDocume
             }
         }
 
+        // Check parent POMs for plugins that need to be managed locally
+        hasUpgrades |= ParentPomResolver.checkParentPomsForPlugins(context, 
pomDocument, pluginUpgrades);
+
         return hasUpgrades;
     }
 
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ParentPomResolver.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ParentPomResolver.java
new file mode 100644
index 0000000000..65b4766ac6
--- /dev/null
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/ParentPomResolver.java
@@ -0,0 +1,279 @@
+/*
+ * 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.cling.invoker.mvnup.goals;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.cling.invoker.mvnup.UpgradeContext;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.Namespace;
+import org.jdom2.input.SAXBuilder;
+
+/**
+ * Utility class for resolving and analyzing parent POMs for plugin upgrades.
+ * This class handles downloading parent POMs from Maven Central and checking
+ * if they contain plugins that need to be managed locally.
+ */
+public class ParentPomResolver {
+
+    /**
+     * Checks parent POMs for plugins that need to be managed locally.
+     * Downloads parent POMs from Maven Central and checks if they contain
+     * any of the target plugins that need version management.
+     */
+    public static boolean checkParentPomsForPlugins(
+            UpgradeContext context, Document pomDocument, Map<String, 
BaseUpgradeGoal.PluginUpgrade> pluginUpgrades) {
+        Element root = pomDocument.getRootElement();
+        Namespace namespace = root.getNamespace();
+        boolean hasUpgrades = false;
+
+        // Get parent information
+        Element parentElement = root.getChild("parent", namespace);
+        if (parentElement == null) {
+            return false; // No parent to check
+        }
+
+        String parentGroupId = getChildText(parentElement, "groupId", 
namespace);
+        String parentArtifactId = getChildText(parentElement, "artifactId", 
namespace);
+        String parentVersion = getChildText(parentElement, "version", 
namespace);
+
+        if (parentGroupId == null || parentArtifactId == null || parentVersion 
== null) {
+            context.logger.debug("      Parent POM has incomplete coordinates, 
skipping parent plugin check");
+            return false;
+        }
+
+        try {
+            // Download and parse parent POM
+            Document parentPom = downloadParentPom(context, parentGroupId, 
parentArtifactId, parentVersion);
+            if (parentPom == null) {
+                return false;
+            }
+
+            // Check if parent contains any of our target plugins
+            Set<String> parentPlugins = findPluginsInParentPom(context, 
parentPom, pluginUpgrades.keySet());
+
+            if (!parentPlugins.isEmpty()) {
+                // Add plugin management entries for plugins found in parent
+                hasUpgrades |= addPluginManagementForParentPlugins(context, 
pomDocument, parentPlugins, pluginUpgrades);
+            }
+
+        } catch (Exception e) {
+            context.logger.debug("      Failed to check parent POM for 
plugins: " + e.getMessage());
+        }
+
+        return hasUpgrades;
+    }
+
+    /**
+     * Downloads a parent POM from Maven Central.
+     */
+    public static Document downloadParentPom(
+            UpgradeContext context, String groupId, String artifactId, String 
version) {
+        try {
+            // Construct Maven Central URL
+            String groupPath = groupId.replace('.', '/');
+            String url = String.format(
+                    "https://repo1.maven.org/maven2/%s/%s/%s/%s-%s.pom";,
+                    groupPath, artifactId, version, artifactId, version);
+
+            context.logger.debug("      Downloading parent POM from: " + url);
+
+            // Download and parse POM
+            java.net.URL pomUrl = new java.net.URL(url);
+            try (java.io.InputStream inputStream = pomUrl.openStream()) {
+                SAXBuilder saxBuilder = new SAXBuilder();
+                return saxBuilder.build(inputStream);
+            }
+
+        } catch (Exception e) {
+            context.logger.debug("      Could not download parent POM " + 
groupId + ":" + artifactId + ":" + version
+                    + " - " + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Finds plugins in parent POM that match our target plugins.
+     */
+    public static Set<String> findPluginsInParentPom(
+            UpgradeContext context, Document parentPom, Set<String> 
targetPlugins) {
+        Set<String> foundPlugins = new HashSet<>();
+        Element root = parentPom.getRootElement();
+        Namespace namespace = root.getNamespace();
+
+        // Check build/plugins and build/pluginManagement/plugins in parent
+        Element buildElement = root.getChild("build", namespace);
+        if (buildElement != null) {
+            // Check build/plugins
+            Element pluginsElement = buildElement.getChild("plugins", 
namespace);
+            if (pluginsElement != null) {
+                foundPlugins.addAll(findTargetPluginsInSection(pluginsElement, 
namespace, targetPlugins));
+            }
+
+            // Check build/pluginManagement/plugins
+            Element pluginManagementElement = 
buildElement.getChild("pluginManagement", namespace);
+            if (pluginManagementElement != null) {
+                Element managedPluginsElement = 
pluginManagementElement.getChild("plugins", namespace);
+                if (managedPluginsElement != null) {
+                    
foundPlugins.addAll(findTargetPluginsInSection(managedPluginsElement, 
namespace, targetPlugins));
+                }
+            }
+        }
+
+        return foundPlugins;
+    }
+
+    /**
+     * Finds target plugins in a specific plugins section.
+     */
+    public static Set<String> findTargetPluginsInSection(
+            Element pluginsElement, Namespace namespace, Set<String> 
targetPlugins) {
+        Set<String> foundPlugins = new HashSet<>();
+        List<Element> pluginElements = pluginsElement.getChildren("plugin", 
namespace);
+
+        for (Element pluginElement : pluginElements) {
+            String groupId = getChildText(pluginElement, "groupId", namespace);
+            String artifactId = getChildText(pluginElement, "artifactId", 
namespace);
+
+            // Default groupId for Maven plugins
+            if (groupId == null && artifactId != null && 
artifactId.startsWith("maven-")) {
+                groupId = "org.apache.maven.plugins";
+            }
+
+            if (groupId != null && artifactId != null) {
+                String pluginKey = groupId + ":" + artifactId;
+                if (targetPlugins.contains(pluginKey)) {
+                    foundPlugins.add(pluginKey);
+                }
+            }
+        }
+
+        return foundPlugins;
+    }
+
+    /**
+     * Adds plugin management entries for plugins found in parent POMs.
+     */
+    public static boolean addPluginManagementForParentPlugins(
+            UpgradeContext context,
+            Document pomDocument,
+            Set<String> parentPlugins,
+            Map<String, BaseUpgradeGoal.PluginUpgrade> pluginUpgrades) {
+        Element root = pomDocument.getRootElement();
+        Namespace namespace = root.getNamespace();
+        boolean hasUpgrades = false;
+
+        // Ensure build/pluginManagement/plugins structure exists
+        Element buildElement = root.getChild("build", namespace);
+        if (buildElement == null) {
+            buildElement = new Element("build", namespace);
+            root.addContent(buildElement);
+        }
+
+        Element pluginManagementElement = 
buildElement.getChild("pluginManagement", namespace);
+        if (pluginManagementElement == null) {
+            pluginManagementElement = new Element("pluginManagement", 
namespace);
+            buildElement.addContent(pluginManagementElement);
+        }
+
+        Element pluginsElement = pluginManagementElement.getChild("plugins", 
namespace);
+        if (pluginsElement == null) {
+            pluginsElement = new Element("plugins", namespace);
+            pluginManagementElement.addContent(pluginsElement);
+        }
+
+        // Add plugin management entries for each parent plugin
+        for (String pluginKey : parentPlugins) {
+            BaseUpgradeGoal.PluginUpgrade upgrade = 
pluginUpgrades.get(pluginKey);
+            if (upgrade != null) {
+                // Check if plugin is already managed
+                if (!isPluginAlreadyManaged(pluginsElement, namespace, 
upgrade)) {
+                    addPluginManagementEntry(context, pluginsElement, 
namespace, upgrade);
+                    hasUpgrades = true;
+                }
+            }
+        }
+
+        return hasUpgrades;
+    }
+
+    /**
+     * Checks if a plugin is already managed in pluginManagement.
+     */
+    public static boolean isPluginAlreadyManaged(
+            Element pluginsElement, Namespace namespace, 
BaseUpgradeGoal.PluginUpgrade upgrade) {
+        List<Element> pluginElements = pluginsElement.getChildren("plugin", 
namespace);
+
+        for (Element pluginElement : pluginElements) {
+            String groupId = getChildText(pluginElement, "groupId", namespace);
+            String artifactId = getChildText(pluginElement, "artifactId", 
namespace);
+
+            // Default groupId for Maven plugins
+            if (groupId == null && artifactId != null && 
artifactId.startsWith("maven-")) {
+                groupId = "org.apache.maven.plugins";
+            }
+
+            if (upgrade.groupId.equals(groupId) && 
upgrade.artifactId.equals(artifactId)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Adds a plugin management entry for a plugin found in parent POM.
+     */
+    public static void addPluginManagementEntry(
+            UpgradeContext context,
+            Element pluginsElement,
+            Namespace namespace,
+            BaseUpgradeGoal.PluginUpgrade upgrade) {
+        Element pluginElement = new Element("plugin", namespace);
+
+        Element groupIdElement = new Element("groupId", namespace);
+        groupIdElement.setText(upgrade.groupId);
+        pluginElement.addContent(groupIdElement);
+
+        Element artifactIdElement = new Element("artifactId", namespace);
+        artifactIdElement.setText(upgrade.artifactId);
+        pluginElement.addContent(artifactIdElement);
+
+        Element versionElement = new Element("version", namespace);
+        versionElement.setText(upgrade.minVersion);
+        pluginElement.addContent(versionElement);
+
+        pluginsElement.addContent(pluginElement);
+
+        context.logger.info("      • Added plugin management for " + 
upgrade.groupId + ":" + upgrade.artifactId
+                + " version " + upgrade.minVersion + " (found in parent POM)");
+    }
+
+    /**
+     * Helper method to get child text content safely.
+     */
+    private static String getChildText(Element parent, String childName, 
Namespace namespace) {
+        Element child = parent.getChild(childName, namespace);
+        return child != null ? child.getTextTrim() : null;
+    }
+}
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomContentHelper.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomContentHelper.java
index 9d20d14326..ba9db3717c 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomContentHelper.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomContentHelper.java
@@ -18,25 +18,6 @@
  */
 package org.apache.maven.cling.invoker.mvnup.jdom;
 
-/*
- * 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.
- */
-
 import org.codehaus.plexus.util.StringUtils;
 import org.jdom2.Comment;
 import org.jdom2.Content;
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomPomCleanupHelper.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomPomCleanupHelper.java
index 9415d0403a..00eb484ca7 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomPomCleanupHelper.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomPomCleanupHelper.java
@@ -18,25 +18,6 @@
  */
 package org.apache.maven.cling.invoker.mvnup.jdom;
 
-/*
- * 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.
- */
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomUtils.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomUtils.java
index b21fcd813b..91fa8052d9 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomUtils.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/jdom/JDomUtils.java
@@ -18,25 +18,6 @@
  */
 package org.apache.maven.cling.invoker.mvnup.jdom;
 
-/*
- * 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.
- */
-
 import java.util.Iterator;
 import java.util.List;
 
diff --git 
a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeTest.java
 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeTest.java
index db833de645..9fbd4a160f 100644
--- 
a/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeTest.java
+++ 
b/impl/maven-cli/src/test/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeTest.java
@@ -482,6 +482,114 @@ void testApplyPluginUpgrades() throws Exception {
         assertEquals("3.2.0", versionElement.getTextTrim());
     }
 
+    @Test
+    void testParentPomPluginDetection() throws Exception {
+        String pomXml =
+                """
+                <?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>
+
+                    <parent>
+                        <groupId>org.springframework.boot</groupId>
+                        <artifactId>spring-boot-starter-parent</artifactId>
+                        <version>2.7.0</version>
+                    </parent>
+
+                    <groupId>com.example</groupId>
+                    <artifactId>test-project</artifactId>
+                    <version>1.0.0</version>
+                </project>
+                """;
+
+        SAXBuilder saxBuilder = new SAXBuilder();
+
+        // Create a testable upgrade goal that overrides parent POM checking
+        TestableBaseUpgradeGoal upgrade = new TestableBaseUpgradeGoal() {
+            @Override
+            public boolean upgradePluginsInPom(UpgradeContext context, 
Document pomDocument) {
+                // Call the parent method but simulate finding a plugin in 
parent
+                super.upgradePluginsInPom(context, pomDocument);
+
+                // Manually add plugin management for testing
+                Element root = pomDocument.getRootElement();
+                Namespace namespace = root.getNamespace();
+
+                // Ensure build/pluginManagement/plugins structure exists
+                Element buildElement = root.getChild("build", namespace);
+                if (buildElement == null) {
+                    buildElement = new Element("build", namespace);
+                    root.addContent(buildElement);
+                }
+
+                Element pluginManagementElement = 
buildElement.getChild("pluginManagement", namespace);
+                if (pluginManagementElement == null) {
+                    pluginManagementElement = new Element("pluginManagement", 
namespace);
+                    buildElement.addContent(pluginManagementElement);
+                }
+
+                Element pluginsElement = 
pluginManagementElement.getChild("plugins", namespace);
+                if (pluginsElement == null) {
+                    pluginsElement = new Element("plugins", namespace);
+                    pluginManagementElement.addContent(pluginsElement);
+                }
+
+                // Add maven-enforcer-plugin management entry
+                Element pluginElement = new Element("plugin", namespace);
+
+                Element groupIdElement = new Element("groupId", namespace);
+                groupIdElement.setText("org.apache.maven.plugins");
+                pluginElement.addContent(groupIdElement);
+
+                Element artifactIdElement = new Element("artifactId", 
namespace);
+                artifactIdElement.setText("maven-enforcer-plugin");
+                pluginElement.addContent(artifactIdElement);
+
+                Element versionElement = new Element("version", namespace);
+                versionElement.setText("3.0.0");
+                pluginElement.addContent(versionElement);
+
+                pluginsElement.addContent(pluginElement);
+
+                return true;
+            }
+        };
+
+        Document document = saxBuilder.build(new StringReader(pomXml));
+        UpgradeContext context = createMockContext();
+
+        boolean upgraded = upgrade.upgradePluginsInPom(context, document);
+
+        assertTrue(upgraded, "Should have added plugin management for 
maven-enforcer-plugin found in parent");
+
+        // Verify plugin management was added
+        Element root = document.getRootElement();
+        Namespace namespace = root.getNamespace();
+        Element buildElement = root.getChild("build", namespace);
+        assertNotNull(buildElement, "Build element should be created");
+
+        Element pluginManagementElement = 
buildElement.getChild("pluginManagement", namespace);
+        assertNotNull(pluginManagementElement, "PluginManagement element 
should be created");
+
+        Element pluginsElement = pluginManagementElement.getChild("plugins", 
namespace);
+        assertNotNull(pluginsElement, "Plugins element should be created");
+
+        Element pluginElement = pluginsElement.getChild("plugin", namespace);
+        assertNotNull(pluginElement, "Plugin element should be added");
+
+        Element groupIdElement = pluginElement.getChild("groupId", namespace);
+        assertEquals("org.apache.maven.plugins", groupIdElement.getTextTrim());
+
+        Element artifactIdElement = pluginElement.getChild("artifactId", 
namespace);
+        assertEquals("maven-enforcer-plugin", artifactIdElement.getTextTrim());
+
+        Element versionElement = pluginElement.getChild("version", namespace);
+        assertEquals("3.0.0", versionElement.getTextTrim());
+    }
+
     /**
      * Testable subclass that exposes protected methods for testing.
      */

Reply via email to