Load project class within maven mojo

635 views Asked by At

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?

1

There are 1 answers

0
Martin Ackermann On

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.