Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
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;
Expand All @@ -52,10 +53,9 @@
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.internal.impl.DefaultPathProcessor;
import org.eclipse.aether.internal.impl.DefaultTransporterProvider;
import org.eclipse.aether.internal.impl.transport.http.DefaultChecksumExtractor;
import org.eclipse.aether.spi.connector.transport.TransporterProvider;
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 org.jdom2.Document;
Expand Down Expand Up @@ -441,15 +441,7 @@ private Session getSession() {
private Session createMaven4Session() {
Session session = ApiRunner.createSession(injector -> {
injector.bindInstance(Dispatcher.class, new LegacyDispatcher());

injector.bindInstance(
TransporterProvider.class,
new DefaultTransporterProvider(Map.of(
"https",
new JdkTransporterFactory(
new DefaultChecksumExtractor(Map.of()), new DefaultPathProcessor()),
"file",
new FileTransporterFactory())));
injector.bindImplicit(TransporterFactoryConfig.class);
});

// Configure repositories
Expand Down Expand Up @@ -571,7 +563,7 @@ private Map<Path, Set<String>> analyzePluginsUsingEffectiveModels(
}

} catch (Exception e) {
context.debug("Failed to analyze effective model for " + originalPomPath + ": " + e.getMessage());
context.warning("Failed to analyze effective model for " + originalPomPath + ": " + e.getMessage());
}
}

Expand Down Expand Up @@ -897,4 +889,24 @@ 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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
Expand Down Expand Up @@ -522,10 +525,91 @@ void shouldHaveValidPluginUpgradeDefinitions() throws Exception {
}
}

@Nested
@DisplayName("Inherited Plugin Detection")
class InheritedPluginDetectionTests {

@Test
@DisplayName("should detect inherited plugins from remote parent POM and add pluginManagement")
void shouldDetectInheritedPluginsFromRemoteParent() throws Exception {
// org.apache:apache:23 defines maven-enforcer-plugin:1.4.1 in pluginManagement.
// A child POM that inherits from this parent should get pluginManagement overrides
// added by mvnup for plugins that need Maven 4 compatibility upgrades.
// Uses an absolute path because the effective model analysis path resolution
// requires it to match between phases.
String pomXml = """
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache</groupId>
<artifactId>apache</artifactId>
<version>23</version>
</parent>
<groupId>org.example</groupId>
<artifactId>test-child</artifactId>
<version>1.0.0-SNAPSHOT</version>
</project>
""";

Document document = saxBuilder.build(new StringReader(pomXml));
Path pomPath = Paths.get("/project/pom.xml").toAbsolutePath();
Map<Path, Document> pomMap = Map.of(pomPath, document);

UpgradeContext context = createMockContext();
UpgradeResult result = strategy.apply(context, pomMap);

assertTrue(result.success(), "Strategy should succeed");
assertTrue(result.modifiedCount() > 0, "Should have added plugin management for inherited plugins");

XMLOutputter out = new XMLOutputter(Format.getRawFormat());
StringWriter writer = new StringWriter();
out.output(document.getRootElement(), writer);
String xml = writer.toString();
assertTrue(
xml.contains("<artifactId>maven-enforcer-plugin</artifactId>"),
"Should add pluginManagement for maven-enforcer-plugin inherited from parent");
}
}

@Nested
@DisplayName("Error Handling")
class ErrorHandlingTests {

@Test
@DisplayName("should warn when effective model analysis fails for POM with unresolvable remote parent")
void shouldWarnWhenEffectiveModelAnalysisFailsForUnresolvableRemoteParent() throws Exception {
// POM inherits from a remote parent that does not exist.
// The effective model analysis should warn (not silently swallow) the failure.
String pomXml = """
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.nonexistent.test</groupId>
<artifactId>nonexistent-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>test-child</artifactId>
</project>
""";

Document document = saxBuilder.build(new StringReader(pomXml));
Map<Path, Document> pomMap = Map.of(Paths.get("pom.xml"), document);

UpgradeContext context = createMockContext();
UpgradeResult result = strategy.apply(context, pomMap);

// Strategy should complete successfully even when effective model analysis fails
assertNotNull(result, "Result should not be null");
assertTrue(result.success(), "Strategy should succeed even when effective model analysis fails");
assertTrue(result.processedPoms().contains(Paths.get("pom.xml")), "POM should be marked as processed");

// The warning should have been logged (not silently swallowed at debug level)
verify(context.logger, atLeastOnce())
.warn(argThat(msg -> msg.contains("Failed to analyze effective model")));
}

@Test
@DisplayName("should handle malformed POM gracefully")
void shouldHandleMalformedPOMGracefully() throws Exception {
Expand Down
Loading