Search code examples
javaspring-bootmavenclassloadermaven-plugin

Maven plugin: get access to the "same classloader" as when running tests?


I'm trying to develop a Maven plugin that looks for certain classes and generates specific documentation for them. I've built a POC as a unit test. I'm trying to convert that now to a Maven plugin.

The test code has access to the classes of the project + the classes of any dependency. I'd like to have that "same classloader" (or a classloader with the same access) available in my Maven plugin.

Is this possible?

Getting access to the project classes

Creating a classloader that contains the classes of the project is doable:

@Mojo(name = "tao-generator", defaultPhase = LifecyclePhase.INSTALL)
public class TaoGeneratorMojo extends AbstractMojo {

    @Parameter(defaultValue = "${project}", required = true, readonly = true)
    MavenProject project;

    ...

    @SneakyThrows
    private ClassLoader getClassLoader(MavenProject project) {
        List<URL> runtimeUrls = new ArrayList<>();
        List<String> runtimeClasspathElements = project.getRuntimeClasspathElements();
        for (int i = 0; i < runtimeClasspathElements.size(); i++) {
            String element = runtimeClasspathElements.get(i);
            runtimeUrls.add(new File(element).toURI().toURL());
        }

        URL[] array = new URL[runtimeUrls.size()];
        array = runtimeUrls.toArray(array);

        return new URLClassLoader(array,
                Thread.currentThread().getContextClassLoader());
    }

Getting access to the dependencies' classes

I haven't figured out how to create a classloader that has access to the dependencies classes. Can I use a similar technique, starting from MavenProject project?

Another angle: loading classes from a fat Spring Boot jar

I've also tried to load the classes directly from the generated fat Spring Boot jar. That doesn't work with a simple URLClassLoader, since that classloader isn't able to load classes from nested jars. Maybe this could be the starting point of another angle to make this work?


Solution

  • You can access the dependencies by calling project.getArtifacts(). From its javadoc:

    All dependencies that this project has, including transitive ones. 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.

    You can use attributes on @Mojo to control the dependencies that are available. Specifically, you need to set both requiresDependencyResolution and requiresDependencyCollection to ResolutionScope.TEST.