This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch maven-4.0.x in repository https://gitbox.apache.org/repos/asf/maven.git
commit 686f1b8e76c1aaccabe4b588164abd6b0ac64904 Author: Guillaume Nodet <[email protected]> AuthorDate: Tue May 26 16:13:09 2026 +0200 Extract shared session infrastructure into AbstractUpgradeStrategy Move Maven session management (getSession, createMaven4Session, TransporterFactoryConfig, temp directory helpers, buildEffectiveModel) from PluginUpgradeStrategy into AbstractUpgradeStrategy to enable reuse across strategies. Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../mvnup/goals/AbstractUpgradeStrategy.java | 136 ++++++++++++++ .../invoker/mvnup/goals/PluginUpgradeStrategy.java | 201 +-------------------- 2 files changed, 139 insertions(+), 198 deletions(-) diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/AbstractUpgradeStrategy.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/AbstractUpgradeStrategy.java index 1b2cc1de72..3828e3f03a 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/AbstractUpgradeStrategy.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/AbstractUpgradeStrategy.java @@ -18,9 +18,13 @@ */ package org.apache.maven.cling.invoker.mvnup.goals; +import java.io.File; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -28,8 +32,27 @@ import eu.maveniverse.domtrip.Element; import eu.maveniverse.domtrip.maven.Coordinates; import eu.maveniverse.domtrip.maven.MavenPomElements; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; import org.apache.maven.api.cli.mvnup.UpgradeOptions; +import org.apache.maven.api.di.Named; +import org.apache.maven.api.di.Provides; +import org.apache.maven.api.model.Repository; +import org.apache.maven.api.model.RepositoryPolicy; +import org.apache.maven.api.services.ModelBuilder; +import org.apache.maven.api.services.ModelBuilderRequest; +import org.apache.maven.api.services.ModelBuilderResult; +import org.apache.maven.api.services.RepositoryFactory; +import org.apache.maven.api.services.Sources; import org.apache.maven.cling.invoker.mvnup.UpgradeContext; +import org.apache.maven.impl.standalone.ApiRunner; +import org.codehaus.plexus.components.secdispatcher.Dispatcher; +import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.LegacyDispatcher; +import org.eclipse.aether.spi.connector.transport.TransporterFactory; +import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor; +import org.eclipse.aether.spi.io.PathProcessor; +import org.eclipse.aether.transport.file.FileTransporterFactory; +import org.eclipse.aether.transport.jdk.JdkTransporterFactory; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PARENT; @@ -47,6 +70,8 @@ */ public abstract class AbstractUpgradeStrategy implements UpgradeStrategy { + private Session session; + /** * Template method that handles common logging and error handling. * Subclasses implement the actual upgrade logic in doApply(). @@ -185,4 +210,115 @@ public static Set<Coordinates> computeAllArtifactCoordinates(UpgradeContext cont context.info("Computed " + coordinatesByGAV.size() + " unique artifact(s) for inference"); return new HashSet<>(coordinatesByGAV.values()); } + + protected Session getSession() { + if (session == null) { + session = createMaven4Session(); + } + return session; + } + + private Session createMaven4Session() { + Session session = ApiRunner.createSession(injector -> { + injector.bindInstance(Dispatcher.class, new LegacyDispatcher()); + injector.bindImplicit(TransporterFactoryConfig.class); + }); + + // TODO: we should read settings + RemoteRepository central = + session.createRemoteRepository(RemoteRepository.CENTRAL_ID, "https://repo.maven.apache.org/maven2"); + RemoteRepository snapshots = session.getService(RepositoryFactory.class) + .createRemote(Repository.newBuilder() + .id("apache-snapshots") + .url("https://repository.apache.org/content/repositories/snapshots/") + .releases(RepositoryPolicy.newBuilder().enabled("false").build()) + .snapshots(RepositoryPolicy.newBuilder().enabled("true").build()) + .build()); + + return session.withRemoteRepositories(List.of(central, snapshots)); + } + + protected Path createTempProjectStructure(UpgradeContext context, Map<Path, Document> pomMap) throws Exception { + Path tempDir = Files.createTempDirectory("mvnup-project-"); + context.debug("Created temp project directory: " + tempDir); + + Path commonRoot = findCommonRoot(pomMap.keySet()); + context.debug("Common root: " + commonRoot); + + for (Map.Entry<Path, Document> entry : pomMap.entrySet()) { + Path originalPath = entry.getKey(); + Document document = entry.getValue(); + + Path relativePath = commonRoot.relativize(originalPath); + Path tempPomPath = tempDir.resolve(relativePath); + + Files.createDirectories(tempPomPath.getParent()); + Files.writeString(tempPomPath, document.toXml()); + context.debug("Wrote POM to temp location: " + tempPomPath); + } + + return tempDir; + } + + protected Path findCommonRoot(Set<Path> pomPaths) { + Path commonRoot = null; + for (Path pomPath : pomPaths) { + Path parent = pomPath.getParent(); + if (parent == null) { + parent = Path.of("."); + } + if (commonRoot == null) { + commonRoot = parent; + } else { + while (!parent.startsWith(commonRoot)) { + commonRoot = commonRoot.getParent(); + if (commonRoot == null) { + break; + } + } + } + } + return commonRoot; + } + + protected void cleanupTempDirectory(Path tempDir) { + try { + Files.walk(tempDir) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } catch (Exception e) { + // Best effort cleanup + } + } + + protected org.apache.maven.api.model.Model buildEffectiveModel(Path pomPath) { + Session session = getSession(); + ModelBuilder modelBuilder = session.getService(ModelBuilder.class); + + ModelBuilderRequest request = ModelBuilderRequest.builder() + .session(session) + .source(Sources.buildSource(pomPath)) + .requestType(ModelBuilderRequest.RequestType.BUILD_EFFECTIVE) + .recursive(false) + .build(); + + ModelBuilderResult result = modelBuilder.newSession().build(request); + return result.getEffectiveModel(); + } + + static class TransporterFactoryConfig { + @Provides + @Named(JdkTransporterFactory.NAME) + static TransporterFactory jdkTransporterFactory( + ChecksumExtractor checksumExtractor, PathProcessor pathProcessor) { + return new JdkTransporterFactory(checksumExtractor, pathProcessor); + } + + @Provides + @Named(FileTransporterFactory.NAME) + static TransporterFactory fileTransporterFactory() { + return new FileTransporterFactory(); + } + } } diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategy.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategy.java index ac0de5eefb..552ab1c2cf 100644 --- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategy.java +++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnup/goals/PluginUpgradeStrategy.java @@ -18,10 +18,7 @@ */ package org.apache.maven.cling.invoker.mvnup.goals; -import java.io.File; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -32,35 +29,17 @@ import eu.maveniverse.domtrip.Document; import eu.maveniverse.domtrip.Editor; import eu.maveniverse.domtrip.Element; -import org.apache.maven.api.RemoteRepository; -import org.apache.maven.api.Session; import org.apache.maven.api.cli.mvnup.UpgradeOptions; import org.apache.maven.api.di.Inject; import org.apache.maven.api.di.Named; import org.apache.maven.api.di.Priority; -import org.apache.maven.api.di.Provides; import org.apache.maven.api.di.Singleton; import org.apache.maven.api.model.Build; import org.apache.maven.api.model.Model; import org.apache.maven.api.model.Parent; import org.apache.maven.api.model.Plugin; import org.apache.maven.api.model.PluginManagement; -import org.apache.maven.api.model.Repository; -import org.apache.maven.api.model.RepositoryPolicy; -import org.apache.maven.api.services.ModelBuilder; -import org.apache.maven.api.services.ModelBuilderRequest; -import org.apache.maven.api.services.ModelBuilderResult; -import org.apache.maven.api.services.RepositoryFactory; -import org.apache.maven.api.services.Sources; import org.apache.maven.cling.invoker.mvnup.UpgradeContext; -import org.apache.maven.impl.standalone.ApiRunner; -import org.codehaus.plexus.components.secdispatcher.Dispatcher; -import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.LegacyDispatcher; -import org.eclipse.aether.spi.connector.transport.TransporterFactory; -import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor; -import org.eclipse.aether.spi.io.PathProcessor; -import org.eclipse.aether.transport.file.FileTransporterFactory; -import org.eclipse.aether.transport.jdk.JdkTransporterFactory; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.ARTIFACT_ID; import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.BUILD; @@ -130,8 +109,6 @@ public class PluginUpgradeStrategy extends AbstractUpgradeStrategy { "1.4", "Versions before 1.4 use a removed DependencyGraphBuilder API incompatible with Maven 4")); - private Session session; - @Inject public PluginUpgradeStrategy() {} @@ -486,104 +463,6 @@ public static List<PluginUpgrade> getPluginUpgrades() { return PLUGIN_UPGRADES; } - /** - * Gets or creates the cached Maven 4 session. - */ - private Session getSession() { - if (session == null) { - session = createMaven4Session(); - } - return session; - } - - /** - * Creates a new Maven 4 session for effective POM computation. - */ - private Session createMaven4Session() { - Session session = ApiRunner.createSession(injector -> { - injector.bindInstance(Dispatcher.class, new LegacyDispatcher()); - injector.bindImplicit(TransporterFactoryConfig.class); - }); - - // Configure repositories - // TODO: we should read settings - RemoteRepository central = - session.createRemoteRepository(RemoteRepository.CENTRAL_ID, "https://repo.maven.apache.org/maven2"); - RemoteRepository snapshots = session.getService(RepositoryFactory.class) - .createRemote(Repository.newBuilder() - .id("apache-snapshots") - .url("https://repository.apache.org/content/repositories/snapshots/") - .releases(RepositoryPolicy.newBuilder().enabled("false").build()) - .snapshots(RepositoryPolicy.newBuilder().enabled("true").build()) - .build()); - - return session.withRemoteRepositories(List.of(central, snapshots)); - } - - /** - * Creates a temporary project structure with all POMs written to preserve relative paths. - * This allows Maven 4 API to properly resolve the project hierarchy. - */ - private Path createTempProjectStructure(UpgradeContext context, Map<Path, Document> pomMap) throws Exception { - Path tempDir = Files.createTempDirectory("mvnup-project-"); - context.debug("Created temp project directory: " + tempDir); - - // Find the common root of all POM paths to preserve relative structure - Path commonRoot = findCommonRoot(pomMap.keySet()); - context.debug("Common root: " + commonRoot); - - // Write each POM to the temp directory, preserving relative structure - for (Map.Entry<Path, Document> entry : pomMap.entrySet()) { - Path originalPath = entry.getKey(); - Document document = entry.getValue(); - - // Calculate the relative path from common root - Path relativePath = commonRoot.relativize(originalPath); - Path tempPomPath = tempDir.resolve(relativePath); - - // Ensure parent directories exist - Files.createDirectories(tempPomPath.getParent()); - - // Write POM to temp location - writePomToFile(document, tempPomPath); - context.debug("Wrote POM to temp location: " + tempPomPath); - } - - return tempDir; - } - - /** - * Finds the common root directory of all POM paths. - */ - private Path findCommonRoot(Set<Path> pomPaths) { - Path commonRoot = null; - for (Path pomPath : pomPaths) { - Path parent = pomPath.getParent(); - if (parent == null) { - parent = Path.of("."); - } - if (commonRoot == null) { - commonRoot = parent; - } else { - // Find common ancestor - while (!parent.startsWith(commonRoot)) { - commonRoot = commonRoot.getParent(); - if (commonRoot == null) { - break; - } - } - } - } - return commonRoot; - } - - /** - * Writes a Document to a file using the same format as the existing codebase. - */ - private void writePomToFile(Document document, Path filePath) throws Exception { - Files.writeString(filePath, document.toXml()); - } - /** * Analyzes plugins using effective models built from the temp directory. * Returns a map of POM path to the set of plugin keys that need management. @@ -637,28 +516,9 @@ private Map<String, PluginUpgrade> getPluginUpgradesAsMap() { upgrade -> upgrade.groupId() + ":" + upgrade.artifactId(), upgrade -> upgrade)); } - /** - * Analyzes the effective model for a single POM to find plugins that need upgrades. - */ private Set<String> analyzeEffectiveModelForPlugins( UpgradeContext context, Path tempPomPath, Map<String, PluginUpgrade> pluginUpgrades) { - - // Use the cached Maven 4 session - Session session = getSession(); - ModelBuilder modelBuilder = session.getService(ModelBuilder.class); - - // Build effective model - ModelBuilderRequest request = ModelBuilderRequest.builder() - .session(session) - .source(Sources.buildSource(tempPomPath)) - .requestType(ModelBuilderRequest.RequestType.BUILD_EFFECTIVE) - .recursive(false) // We only want this POM, not its modules - .build(); - - ModelBuilderResult result = modelBuilder.newSession().build(request); - Model effectiveModel = result.getEffectiveModel(); - - // Analyze plugins from effective model + Model effectiveModel = buildEffectiveModel(tempPomPath); return analyzePluginsFromEffectiveModel(context, effectiveModel, pluginUpgrades); } @@ -729,19 +589,7 @@ private String getPluginKey(Plugin plugin) { private Path findLastLocalParentForPluginManagement( UpgradeContext context, Path tempPomPath, Map<Path, Document> pomMap, Path tempDir, Path commonRoot) { - // Build effective model to get parent information - Session session = getSession(); - ModelBuilder modelBuilder = session.getService(ModelBuilder.class); - - ModelBuilderRequest request = ModelBuilderRequest.builder() - .session(session) - .source(Sources.buildSource(tempPomPath)) - .requestType(ModelBuilderRequest.RequestType.BUILD_EFFECTIVE) - .recursive(false) - .build(); - - ModelBuilderResult result = modelBuilder.newSession().build(request); - Model effectiveModel = result.getEffectiveModel(); + Model effectiveModel = buildEffectiveModel(tempPomPath); // Convert the temp path back to the original path Path relativePath = tempDir.relativize(tempPomPath); @@ -761,17 +609,8 @@ private Path findLastLocalParentForPluginManagement( // Parent is local, so it becomes our new candidate lastLocalParent = parentPath; - // Load the parent model to continue walking up Path parentTempPath = tempDir.resolve(commonRoot.relativize(parentPath)); - ModelBuilderRequest parentRequest = ModelBuilderRequest.builder() - .session(session) - .source(Sources.buildSource(parentTempPath)) - .requestType(ModelBuilderRequest.RequestType.BUILD_EFFECTIVE) - .recursive(false) - .build(); - - ModelBuilderResult parentResult = modelBuilder.newSession().build(parentRequest); - currentModel = parentResult.getEffectiveModel(); + currentModel = buildEffectiveModel(parentTempPath); } else { // Parent is external, stop here break; @@ -896,20 +735,6 @@ private void addPluginManagementEntryFromUpgrade( + upgrade.minVersion() + " (found through effective model analysis)"); } - /** - * Cleans up the temporary directory. - */ - private void cleanupTempDirectory(Path tempDir) { - try { - Files.walk(tempDir) - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - } catch (Exception e) { - // Best effort cleanup - don't fail the whole operation - } - } - /** * Holds plugin upgrade information for Maven 4 compatibility. * This class contains the minimum version requirements for plugins @@ -938,24 +763,4 @@ public static class PluginUpgradeInfo { this.minVersion = minVersion; } } - - /** - * DI configuration that registers transporter factories for the standalone Maven session. - * Uses {@code @Provides} methods so the factories are properly registered as named beans - * and feed into the {@code Map<String, TransporterFactory>} used by the TransporterProvider. - */ - static class TransporterFactoryConfig { - @Provides - @Named(JdkTransporterFactory.NAME) - static TransporterFactory jdkTransporterFactory( - ChecksumExtractor checksumExtractor, PathProcessor pathProcessor) { - return new JdkTransporterFactory(checksumExtractor, pathProcessor); - } - - @Provides - @Named(FileTransporterFactory.NAME) - static TransporterFactory fileTransporterFactory() { - return new FileTransporterFactory(); - } - } }
