Skip to content

Commit

Permalink
Incomplete runtime classpath fix
Browse files Browse the repository at this point in the history
Similar fix to m-assembly-p: do not rely on Maven Core to provide
"runtime" resolution scope as it have issues, see:
https://issues.apache.org/jira/browse/MNG-8041

Instead, use Resolver APIs directly to get what is needed.
Contains several minor code improvements across Provisio
maven-plugin module.

Contains IT from jvanzyl#70
Fixes jvanzyl#71
  • Loading branch information
cstamas committed Feb 6, 2024
1 parent b531aca commit 99f552b
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 64 deletions.
5 changes: 5 additions & 0 deletions provisio-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@
<artifactId>maven-resolver-impl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-util</artifactId>
<!-- NOT provided: to work with 3.8 and 3.9 -->
</dependency>
<!-- Test -->
<dependency>
<groupId>io.takari.maven.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,24 @@
import ca.vanzyl.provisio.model.Runtime;
import io.takari.incrementalbuild.Incremental;
import io.takari.incrementalbuild.Incremental.Configuration;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.ArtifactProperties;
import org.eclipse.aether.artifact.ArtifactType;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.artifact.DefaultArtifactType;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.DefaultDependencyNode;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.util.filter.ScopeDependencyFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -83,20 +80,22 @@ public abstract class BaseMojo
@Parameter(required = true, defaultValue = "${basedir}/src/main/provisio")
protected File descriptorDirectory;

@Parameter(defaultValue = "${session}")
protected MavenSession session;

protected ProvisioArtifact projectArtifact() {
ProvisioArtifact jarArtifact = null;
//
// We also need to definitively know what others types of runtime artifacts have been created. Right now there
// is no real protocol for knowing what something like, say, the JAR plugin did to drop off a file somewhere. We
// need to improve this but for now we'll look.
// ===
// While this above is still true, this check below is better, in a sense it allows JAR plugin to drop off file
// anywhere.
//
File jar = new File(project.getBuild().getDirectory(), project.getArtifactId() + "-" + project.getVersion() + ".jar");
if (jar.exists()) {
Artifact projectArtifact = RepositoryUtils.toArtifact(project.getArtifact());
if (projectArtifact.getFile() != null
&& projectArtifact.getFile().getName().endsWith(".jar")
&& projectArtifact.getFile().exists()) {
jarArtifact = new ProvisioArtifact(project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion());
jarArtifact.setFile(jar);
jarArtifact.setFile(projectArtifact.getFile());
}
return jarArtifact;
}
Expand All @@ -113,39 +112,44 @@ protected ArtifactSet getRuntimeClasspathAsArtifactSet() {
// for this Mojo. I think this will be sufficient for anything related to creating a runtime.
//
ArtifactSet artifactSet = new ArtifactSet();
for (org.apache.maven.artifact.Artifact mavenArtifact : project.getArtifacts()) {
if (!mavenArtifact.getScope().equals("system") && !mavenArtifact.getScope().equals("provided")) {
artifactSet.addArtifact(new ProvisioArtifact(toArtifact(mavenArtifact)));
}
for (Artifact mavenArtifact : resolveRuntimeScopeTransitively()) {
artifactSet.addArtifact(new ProvisioArtifact(mavenArtifact));
}
return artifactSet;
}

private static Artifact toArtifact(org.apache.maven.artifact.Artifact artifact) {
if (artifact == null) {
return null;
}

String version = artifact.getVersion();
if (version == null && artifact.getVersionRange() != null) {
version = artifact.getVersionRange().toString();
/**
* This method is in use instead of project offering mojo asked resolution scope due presence of:
* <a href="https://issues.apache.org/jira/browse/MNG-8041">MNG-8041</a>
*/
private List<Artifact> resolveRuntimeScopeTransitively() {
DependencyFilter runtimeFilter = new ScopeDependencyFilter("system", "provided", "test");
List<org.eclipse.aether.graph.Dependency> dependencies = project.getDependencies().stream()
.map(d -> RepositoryUtils.toDependency(d, repositorySystemSession.getArtifactTypeRegistry()))
.filter(d -> runtimeFilter.accept(new DefaultDependencyNode(d), Collections.emptyList()))
.collect(Collectors.toList());
List<org.eclipse.aether.graph.Dependency> managedDependencies = Collections.emptyList();
if (project.getDependencyManagement() != null) {
managedDependencies = project.getDependencyManagement().getDependencies().stream()
.map(d -> RepositoryUtils.toDependency(d, repositorySystemSession.getArtifactTypeRegistry()))
.collect(Collectors.toList());
}

Map<String, String> props = null;
if (org.apache.maven.artifact.Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) {
String localPath = (artifact.getFile() != null) ? artifact.getFile().getPath() : "";
props = Collections.singletonMap(ArtifactProperties.LOCAL_PATH, localPath);
CollectRequest collectRequest = new CollectRequest();
collectRequest.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact()));
collectRequest.setRepositories(project.getRemoteProjectRepositories());
collectRequest.setDependencies(dependencies);
collectRequest.setManagedDependencies(managedDependencies);
DependencyRequest request = new DependencyRequest(collectRequest, runtimeFilter);
try {
return repositorySystem.resolveDependencies(repositorySystemSession, request).getArtifactResults().stream()
.map(ArtifactResult::getArtifact)
.collect(Collectors.toList());
} catch (DependencyResolutionException e) {
logger.error("Failed to resolve runtime dependencies", e);
throw new RuntimeException(e);
}

Artifact result = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), artifact.getArtifactHandler().getExtension(), version, props,
newArtifactType(artifact.getType(), artifact.getArtifactHandler()));
result = result.setFile(artifact.getFile());

return result;
}

private static ArtifactType newArtifactType(String id, ArtifactHandler handler) {
return new DefaultArtifactType(id, handler.getExtension(), handler.getClassifier(), handler.getLanguage(), handler.isAddedToClasspath(), handler.isIncludesDependencies());
}

protected ProvisioningRequest getRequest(Runtime runtime)
Expand Down Expand Up @@ -173,7 +177,7 @@ protected void checkDuplicates(List<ProvisioArtifact> artifacts)
.filter(strings -> strings.size() > 1)
.map(strings -> String.join(", ", strings))
.collect(Collectors.toList());
if (duplicates.size() != 0) {
if (!duplicates.isEmpty()) {
throw new MojoFailureException("Found different versions of the same dependency: " + String.join(", ", duplicates));
}
}
Expand All @@ -186,10 +190,10 @@ protected List<Dependency> getDependencies(List<ProvisioArtifact> artifacts)
dependency.setGroupId(artifact.getGroupId());
dependency.setArtifactId(artifact.getArtifactId());
dependency.setVersion(artifact.getVersion());
if (artifact.getClassifier() != null && artifact.getClassifier().length() != 0) {
if (artifact.getClassifier() != null && !artifact.getClassifier().isEmpty()) {
dependency.setClassifier(artifact.getClassifier());
}
if (artifact.getExtension() != null && artifact.getExtension().length() != 0 && !artifact.getExtension().equals("jar")) {
if (artifact.getExtension() != null && !artifact.getExtension().isEmpty() && !artifact.getExtension().equals("jar")) {
dependency.setType(artifact.getExtension());
}
dependencies.add(dependency);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.WriterFactory;

import java.io.File;
Expand All @@ -48,6 +47,7 @@ public class GeneratorMojo
@Parameter(property = "dependencyExtendedPomLocation", defaultValue = "${project.build.directory}/generated/provisio/dependency-extended-pom.xml")
private File dependencyExtendedPomLocation;

@Override
public void execute()
throws MojoExecutionException, MojoFailureException
{
Expand All @@ -65,7 +65,7 @@ public void execute()
throw new MojoExecutionException("Error resolving artifacts.", e);
}
}
if (artifacts.size() == 0) {
if (artifacts.isEmpty()) {
return;
}
checkDuplicates(artifacts);
Expand All @@ -85,17 +85,11 @@ private void writeModel(Model model)
} catch (IOException e) {
throw new MojoExecutionException("Error creating parent directories for the POM file: " + e.getMessage(), e);
}
Writer writer = null;
try {
writer = WriterFactory.newXmlWriter(dependencyExtendedPomLocation);
try (Writer writer = WriterFactory.newXmlWriter(dependencyExtendedPomLocation)) {
new MavenXpp3Writer().write(writer, model);
writer.close();
}
catch (IOException e) {
throw new MojoExecutionException("Error writing POM file: " + e.getMessage(), e);
}
finally {
IOUtil.close(writer);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,26 +147,24 @@ public List<String> getManagedDependencies(MavenProject project) {
}

public String toCoordinate(Dependency d) {
StringBuffer sb = new StringBuffer().append(d.getGroupId()).append(":").append(d.getArtifactId()).append(":").append(d.getType());
if (d.getClassifier() != null && d.getClassifier().isEmpty() == false) {
StringBuilder sb = new StringBuilder().append(d.getGroupId()).append(":").append(d.getArtifactId()).append(":").append(d.getType());
if (d.getClassifier() != null && !d.getClassifier().isEmpty()) {
sb.append(":").append(d.getClassifier());
}
sb.append(":").append(d.getVersion());
return sb.toString();
}

public String toVersionlessCoordinate(Dependency d) {
StringBuffer sb = new StringBuffer().append(d.getGroupId()).append(":").append(d.getArtifactId()).append(":").append(d.getType());
if (d.getClassifier() != null && d.getClassifier().isEmpty() == false) {
StringBuilder sb = new StringBuilder().append(d.getGroupId()).append(":").append(d.getArtifactId()).append(":").append(d.getType());
if (d.getClassifier() != null && !d.getClassifier().isEmpty()) {
sb.append(":").append(d.getClassifier());
}
return sb.toString();
}

public String toVersionlessCoordinate(MavenProject project) {
String extension = artifactHandlerManager.getArtifactHandler(project.getPackaging()).getExtension();
StringBuffer sb = new StringBuffer().append(project.getGroupId()).append(":").append(project.getArtifactId()).append(":").append(extension);
return sb.toString();
return project.getGroupId() + ":" + project.getArtifactId() + ":" + extension;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import ca.vanzyl.provisio.model.ProvisioningResult;
import ca.vanzyl.provisio.model.Runtime;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
Expand All @@ -41,6 +40,7 @@ public class ProvisioningMojo extends BaseMojo {
@Parameter(defaultValue = "${project.build.directory}/${project.artifactId}-${project.version}")
private File outputDirectory;

@Override
public void execute() throws MojoExecutionException {
if (skipProvision) {
getLog().info("Skipping provision");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class ValidatorMojo
@Parameter(required = true, property = "pomFile", defaultValue = "${basedir}/pom.xml")
private File pomFile;

@Override
public void execute()
throws MojoExecutionException, MojoFailureException
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
import static org.junit.Assert.assertArrayEquals;

@RunWith(MavenJUnitTestRunner.class)
@MavenVersions({"3.6.3", "3.8.4"})
@MavenVersions({"3.6.3", "3.8.8", "3.9.6"})
@SuppressWarnings({"JUnitTestNG", "PublicField"})
public class GeneratorIntegrationTest
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* Copyright (C) 2015-2020 Jason van Zyl
* <p>
* Licensed 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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 ca.vanzyl.maven.plugins.provisio;

import io.takari.maven.testing.TestResources;
import io.takari.maven.testing.executor.MavenRuntime;
import io.takari.maven.testing.executor.MavenRuntime.MavenRuntimeBuilder;
import io.takari.maven.testing.executor.MavenVersions;
import io.takari.maven.testing.executor.junit.MavenJUnitTestRunner;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;

import static org.junit.Assert.assertTrue;

@RunWith(MavenJUnitTestRunner.class)
@MavenVersions({"3.6.3", "3.8.8", "3.9.6"})
@SuppressWarnings({"JUnitTestNG", "PublicField"})
public class ProvisioningIntegrationTest
{
@Rule
public final TestResources resources = new TestResources();

public final MavenRuntime maven;

public ProvisioningIntegrationTest(MavenRuntimeBuilder mavenBuilder)
throws Exception
{
this.maven = mavenBuilder.withCliOptions("-B", "-U").build();
}

@Test
public void testTransitiveWithLocalTestScope()
throws Exception
{
File basedir = resources.getBasedir("transitive-test");
maven.forProject(basedir)
.execute("provisio:provision")
.assertErrorFreeLog();

File libdir = new File(basedir, "target/test-1.0/lib");
assertTrue("guice exists", new File(libdir, "guice-7.0.0.jar").isFile());
assertTrue("guava exists", new File(libdir, "guava-31.0.1-jre.jar").isFile());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import java.io.File;

@RunWith(MavenJUnitTestRunner.class)
@MavenVersions({"3.6.3", "3.8.4"})
@MavenVersions({"3.6.3", "3.8.8", "3.9.6"})
@SuppressWarnings({"JUnitTestNG", "PublicField"})
public class ValidatorIntegrationTest
{
Expand Down
37 changes: 37 additions & 0 deletions provisio-maven-plugin/src/test/projects/transitive-test/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?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>ca.vanzyl.provisio.maven.plugins.its</groupId>
<artifactId>test</artifactId>
<version>1.0</version>
<packaging>provisio</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>7.0.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>ca.vanzyl.provisio.maven.plugins</groupId>
<artifactId>provisio-maven-plugin</artifactId>
<version>${it-plugin.version}</version>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<runtime>
<artifactSet to="/lib" ref="runtime.classpath" />
</runtime>

0 comments on commit 99f552b

Please sign in to comment.