Search code examples
javamavenpluginsclassloadermaven-plugin

How to add maven build output to plugin classloader?


I'm running into a problem with an AntTask run within the maven-antrun-plugin. Unfortunately, the AntTask uses the plugin classloader to locate a file from the project, but when run from within a plugin, the build output is not included in the plugin's classpath.

From the Guide to Maven Classloading:

Please note that the plugin classloader does neither contain the dependencies of the current project nor its build output.

...

Plugins are free to create further classloaders on their discretion. For example, a plugin might want to create a classloader that combines the plugin class path and the project class path.

Can anyone point me in right direction how to create my own version of the maven-antrun-plugin in which I can create my own classloader that combines the plugin class path and the project class path? I need to update the classloader such that when a class executed by my custom antrun-plugin calls:

getClass().getClassLoader().getResource()

the classloader will search the build output folder as well.


Solution

  • After several hours trying to work my way around this issue with configuration, I bit the bullet and simply wrote my own plugin that extends the AntRun plugin. This was done using Maven 3.2.5:

    @Mojo( name = "run", threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST )
    public class CustomAntMojo
        extends AntRunMojo
    {
    
        @Component
        private PluginDescriptor pluginDescriptor;
    
        public void execute()
            throws MojoExecutionException
        {
            File buildDirectory = new File( getMavenProject().getBuild().getOutputDirectory() );
    
            // add the build directory to the classpath for the classloader
            try {
                ClassRealm realm = pluginDescriptor.getClassRealm();
                realm.addURL(buildDirectory.toURI().toURL());
            } catch (MalformedURLException e1) {
                e1.printStackTrace();
            }
    
            // configure the log4j logger to output the ant logs to the maven log
            BasicConfigurator.configure( new MavenLoggerLog4jBridge(getLog()));
            super.execute();
    
        }
    }
    

    With the MavenLoggerLog4jBridge class being used to convert from my Ant task's Log4j output to maven logger (https://stackoverflow.com/a/6948208/827480):

    import org.apache.log4j.AppenderSkeleton;
    import org.apache.log4j.Level;
    import org.apache.log4j.spi.LoggingEvent;
    import org.apache.maven.plugin.logging.Log;
    public class MavenLoggerLog4jBridge extends AppenderSkeleton {
        private Log logger;
    
        public MavenLoggerLog4jBridge(Log logger) {
            this.logger = logger;
        }
    
        protected void append(LoggingEvent event) {
            int level = event.getLevel().toInt();
            String msg = event.getMessage().toString();
            if (level <= Level.DEBUG_INT ) {
                this.logger.debug(msg);
            } else if (level == Level.INFO_INT) {
                this.logger.info(msg);
            } else if (level == Level.WARN_INT) {
                this.logger.warn(msg);
            } else if (level == Level.ERROR_INT || level == Level.FATAL_INT) {
                this.logger.error(msg);
            }
        }
    
        public void close() {
        }
    
        public boolean requiresLayout() {
            return false;
        }
    }
    

    Hopefully it might be of some use or assistance to someone in the future.