Initialize full dependency tree in Maven aggregator plugin - java

I have an aggregator plugin in Maven with an injected DependencyGraphBuilder. My goal is to resolve the DependencyNode of the current project with all child nodes initialized as well.
#Mojo(name = "mojo", defaultPhase = LifecyclePhase.SITE,
requiresDependencyResolution = ResolutionScope.COMPILE, aggregator = true)
public class MyMojo extends AbstractMojo {
#Component(hint = "default")
private DependencyGraphBuilder dependencyGraphBuilder;
#Parameter(defaultValue = "${reactorProjects}", readonly = true, required = true)
private List<MavenProject> reactorProjects;
}
Currently, if i try to resolve to root node, i am only able to get the dependencies of the current project. I assume that Maven did not resolve that dependencies (only for the current project).
dependencyGraphBuilder.buildDependencyGraph(project, null, reactorProjects);
Summing up:
How can i resolve the child nodes to build to full dependency tree
using DependencyGraphBuilder in a Maven reactor project or is this not possible for aggregator projects?
Additional Info:
I have looked at similar questions, but all of those did not assume an aggregator project.

Since you are trying to implement mvn dependency:tree why not to copy the solution from the Maven Dependency Plugin. Take a look at TreeMojo which also uses DependencyGraphBuilder with DependencyNodeVisitor.

Related

Load project class within maven mojo

I am trying to load a projects class during the execution of a maven mojo.
Unfortunately this operation fails, since the class loader is missing a referenced class.
Looking around I found already the approaches Maven mojo plugin to load class from hosting project and Maven plugin can't load class
I combined the two approaches, ending up with the following code:
private ClassLoader getClassLoader(final MavenProject project) {
try {
final List<URL> classPathUrls = new ArrayList<>();
// adding the projects dependency jars
final Set<Artifact> artifacts = getProject().getDependencyArtifacts();
for (final Artifact artifact : artifacts) {
classPathUrls.add(artifact.getFile().toURI().toURL());
}
// adding the projects classes itself
final List<String> classpathElements = project.getCompileClasspathElements();
classpathElements.add(project.getBuild().getOutputDirectory());
classpathElements.add(project.getBuild().getTestOutputDirectory());
for (final String classpathElement : classpathElements) {
classPathUrls.add(new File(classpathElement).toURI().toURL());
}
// creating a class loader
final URL urls[] = new URL[classPathUrls.size()];
for (int i = 0; i < classPathUrls.size(); ++i) {
urls[i] = classPathUrls.get(i);
}
return new URLClassLoader(urls, this.getClass().getClassLoader());
} catch (final Exception e) {
getLog().debug("Couldn't get the classloader.");
return this.getClass().getClassLoader();
}
}
The class which fails to load is an implementation of the interface "org.bson.codecs.Codec", which is contained in "org.mongodb:bson", which is implicit imported via the dependency:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.1.1</version>
<scope>provided</scope>
</dependency>
This dependency has a dependency to another jar (scope: provided), containing the mentioned interface, which can be seen in the maven dependency tree:
$> mvn dependency:tree
[INFO] net.my.project:my-project:jar:1.0-SNAPSHOT
[INFO] +- org.mongodb:mongodb-driver-sync:jar:4.1.1:provided
[INFO] | +- org.mongodb:bson:jar:4.1.1:provided
[INFO] | \- org.mongodb:mongodb-driver-core:jar:4.1.1:provided
When I look to the class path elements of the created class loader, I see that mongodb-driver-sync.jar is included, but since it declares the "org.mongodb:bson" dependency with scope provided it the interface is still not in class path.
So, the final question is: How can I load a class which references a class from an "indirect" dependency?
I've noticed, that
project.getArtifacts()
was empty, even though the javadoc says it is supposed to contain all dependencies (lazily populated).
So, with additional research I found Custom Maven Plugin development - getArtifacts is empty though dependencies are included which suggests to adjust the #Mojo annotation:
#Mojo(name = "mojoName", requiresDependencyResolution = ResolutionScope.COMPILE)
after adjusting the annotation, it is even enough to use the "project.getCompileClasspathElements();", it is not necessary anymore to iterate through the artifacts at all.

Get list of maven dependencies of org.apache.maven.project.MavenProject

I'm trying to get a list of dependencies of some maven artifacts using org.apache.maven.project.MavenProject.
My code is like this.
public List<Dependencies> loadProject() {
Model mavenModel = new Model();
mavenModel.setModelVersion("4.0.0");
mavenModel.setGroupId("org");
mavenModel.setArtifactId("wso2");
mavenModel.setVersion("1.0.0");
addDependency(mavenModel, "com.google.inject", "guice", "4.2.2");
addDependency(mavenModel, "ch.qos.logback", "logback-classic", "1.2.3");
MavenProject mavenProject = new MavenProject(mavenModel);
//******* Need to resolve dependencies of `mavenProject` and *******
//******* get the list of dependencies of this project. *******
return dependencies;
}
private static void addDependency(Model mavenModel, String groupId, String artifactId, String version) {
Dependency dependency = new Dependency();
dependency.setGroupId(groupId);
dependency.setArtifactId(artifactId);
dependency.setVersion(version);
mavenModel.addDependency(dependency);
}
Basically I'm trying to get the dependency tree results which returns by mvn dependency:tree command as a list by programmatically.
Example
For the artifacts:
com.google.inject:guide:4.2.2
ch.qos.logback:logback-classic:1.2.3
Dependency list:
List = [
com.google.inject:guice:jar:4.2.2:compile,
javax.inject:javax.inject:jar:1:compile,
aopalliance:aopalliance:jar:1.0:compile,
com.google.guava:guava:jar:25.1-android:compile,
com.google.code.findbugs:jsr305:jar:3.0.2:compile,
org.checkerframework:checker-compat-qual:jar:2.0.0:compile,
com.google.errorprone:error_prone_annotations:jar:2.1.3:compile,
com.google.j2objc:j2objc-annotations:jar:1.1:compile,
org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:compile,
ch.qos.logback:logback-classic:jar:1.2.3:compile,
ch.qos.logback:logback-core:jar:1.2.3:compile,
org.slf4j:slf4j-api:jar:1.7.25:compile
]
You can use the method public Set<Artifact> getArtifacts() of your MavenProject class which returns a set of artifacts representing all dependencies that the project has, including transitive ones.
NB: Contents are lazily populated, so depending on what phases have run dependencies in some scopes won't be included. eg. if only compile phase has run, dependencies with scope test won't be included.
All the information are coming from the documentation that you can find here.

Programatically getting an effective POM using Maven Resolver Provider

What do I want to do?
Given a POM file on the local filesystem.
I want to programmatically obtain the effective POM of that POM file. Specifically I want to do the following:
Resolve the POMs dependencies
Ensure that all parent POMs are processed
Obtain the list of dependencies of the fully resolved POM
And so on...
I don't need to obtain transitive dependencies.
What works?
I'm using Maven Resolver Provider which sort of works. However
I have to use a package private class org.apache.maven.repository.internal.DefaultModelResolver
Here a GitHub link to a sample Maven project that you can run: https://github.com/sahilm/maven-resolver-test
The example program does the following:
Downloads the latest spring boot POM from Maven Central.
Prints out it's direct dependencies (with parent deps included)
You can run the the program with:
mvn exec:java -Dexec.mainClass="com.sahilm.maven_resolver_test.Test"
What I need help with?
I need help with understanding why I have to use a package private class to get stuff to work.
Is there another way to get the information I need?
You can create (in your project) a public class under the package: org.apache.maven.repository.internal that extends the package-accessibility class. Just use a class name that is not possible to be used in the furutre by the vendor.
package org.apache.maven.repository.internal;
public class VisibleDefaultModelResolver extends DefaultModelResolver{
public VisibleDefaultModelResolver(RepositorySystemSession session, RequestTrace trace, String context, ArtifactResolver resolver, VersionRangeResolver versionRangeResolver, RemoteRepositoryManager remoteRepositoryManager, List<RemoteRepository> repositories) {
super(session, trace, context, resolver, versionRangeResolver, remoteRepositoryManager, repositories);
}
}
Then your code becomes:
ModelResolver modelResolver = new VisibleDefaultModelResolver(session, requestTrace, "context", artifactResolver, versionRangeResolver, remoteRepositoryManager, repos);
Maybe you can use ProjectModelResolver. Here's a code snippet,
DefaultRepositorySystem repositorySystem =
new DefaultRepositorySystem();
repositorySystem.initService(locator);
ModelResolver modelResolver =
new ProjectModelResolver(session, requestTrace,
repositorySystem, remoteRepositoryManager, repos,
ProjectBuildingRequest.RepositoryMerging.POM_DOMINANT,
null);
I've included a working code here.

Load classpath of project gradle plugin is applied to

I am writing a custom gradle plugin, which will generate some code for me, based on the code it finds in the project it is applied to.
For this I need to find all classes that extend a specific class.
The problem is that the class, that is extended, is not loaded in the classpath, since it is a dependency of the other project.
Currently I got this for my custom Task
public class GenerateCodeTask extends DefaultTask {
#TaskAction
public void generateCode() throws MalformedURLException, ClassNotFoundException {
File buildDir = new File(getProject().getBuildDir(), "classes/main");
File root = new File(getProject().getProjectDir(), "src/main/generated");
URLClassLoader classLoader = new URLClassLoader(new URL[]{buildDir.toURL()});
Class itemClass = classLoader.loadClass("net.minecraft.item.Item");
Reflections reflections = new Reflections(classLoader);
Set<Class<?>> items = reflections.getSubTypesOf(itemClass);
}
}
And this for the plugin
public class EasymodsPlugin implements Plugin<Project> {
#Override
public void apply(Project p) {
Task t = p.getTasks().create("generateCode", GenerateCodeTask.class);
t.dependsOn(p.getTasks().getByPath("compileJava"));
}
}
This is the error I am getting
java.lang.ClassNotFoundException: net.minecraft.item.Item
I know that the problem is that the library containing the class is not loaded, and that causes the exception.
What I want is to be able to load all dependencies of my project into the classloader, so I can use reflections to find all "items" in the project (which I need to generate code)
Greetings Failender
I think you almost got it.
You need the compileClasspath property. I pass it as an input parameter to my task, and build the Class Loader from it:
In plugin:
Set<File> ccp = project.getConfigurations().getByName("compileClasspath").getFiles();
task.classpath = ccp;
In task:
#InputFiles
Iterable<File> classpath;
What this property is adding is:
the path to the classes on projects it depends on that are generated under project_id/build dir. ( So no need to build the path manually )
All library deps for your project. That i think you were missing.
So, in the case your class is of type 1:
It is better if you have different subprojects: the one that has the original classes and the other with the generated ones.
So the second depends on the first:
dependencies {
compile project(':my_project_with_classes_to_extend')
}
In the case is the second one, you can just add the library as a dep to your project and it will find the class.
And rewire the tasks so you are sure the fist project is built before calling your task (just on root level of your 2nd project build.gradle):
I think that´s the part that wasn't really working for you, apparently, compileJava and build are not exactly the same. Or at least, compileJava wasnt working for me either.
myGeneratorTask.dependsOn( ":my_project_with_classes_to_extend:build" )
compileJava.dependsOn( "myGeneratorTask" )

Maven MOJO: Get execution configuration in runtime

I am developing a maven plugin.
When maven instantiates the mojo class, fields annotated as #Parameter will be "dependency injected" by maven, as childenodes are defined inside the project/build/plugins/plugin/executions/execution tag.
Like this:
#Parameter(defaultValue = "${basedir}/src", alias = "src")
private String sourcePath;
will be filled by
<configuration>
<src>${basedir}/whatever</src>
</configuration>
.
Is there a way to get the configuration via some java calls?
I know that I can use
public Xpp3Dom org.apache.maven.plugin.MojoExecution.getConfiguration()
to retrieve that configuration, the problem is that properties are not resolved in this case, so I get "${basedir}/whatever" for sourcePath, ${} of properties are not resolved. I need them resolved, whatever property they are.
Is there a way to get the resolved values runtime?
Thanks
Annotate your mojo with:
#Mojo(name = "mymojoid", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class MyMojo extends AbstractMojo{}
Then the values will automatically be resolved

Categories