Question

I'm trying to make a maven plugin that needs to use reflection. I want a project to run the plugin, and give it the full name of a class in the project, and the plugin will load it by reflection to get info from it.

There's something strange with the classloader though, because it can't find the class when I use

Class.forName("package.MyClass");

Looking here, I can't quite figure out if my plugin's classloader, when being run in a different project, has access to that project's classes.

Was it helpful?

Solution

I'm sure there's a better way, but here's how I got it to work:

Add the following to the javadoc at the top of your mojo: @requiresDependencyResolution runtime

Add a MavenProject parameter:

/**
 * @parameter expression="${project}"
 * @required
 * @readonly
 */
private MavenProject project;

Then you can get the dependencies at runtime, and make your own classloader:

List runtimeClasspathElements = project.getRuntimeClasspathElements();
URL[] runtimeUrls = new URL[runtimeClasspathElements.size()];
for (int i = 0; i < runtimeClasspathElements.size(); i++) {
  String element = (String) runtimeClasspathElements.get(i);
  runtimeUrls[i] = new File(element).toURI().toURL();
}
URLClassLoader newLoader = new URLClassLoader(runtimeUrls,
  Thread.currentThread().getContextClassLoader());

Then you can load your class using this new classloader:

Class bundle = newLoader.loadClass("package.MyClass");

OTHER TIPS

You should consider using this to add the runtime class path elements to the current class realm. (You can use the PluginDescriptor to retrieve the class realm.

List<String> runtimeClasspathElements = project.getRuntimeClasspathElements();
ClassRealm realm = descriptor.getClassRealm();

for (String element : runtimeClasspathElements)
{
    File elementFile = new File(element);
    realm.addURL(elementFile.toURI().toURL());
}

This worked perfectly for me!

As Dave asked, here is the way to get the PluginDescriptor:

/**
 * The plugin descriptor
 * 
 * @parameter default-value="${descriptor}"
 */
private PluginDescriptor descriptor;

I ran across this exact issue, today. The above suggestions didn't work for me, thought I would submit my solution to the list. I used the HibernateExporter mojo source which can be viewed at: http://grepcode.com/file/repo1.maven.org/maven2/org.codehaus.mojo/hibernate3-maven-plugin/2.2/org/codehaus/mojo/hibernate3/HibernateExporterMojo.java?av=f

/**
 * @parameter expression="${project}"
 * @required
 * @readonly
 */
private MavenProject project;

private ClassLoader getClassLoader() throws MojoExecutionException
{
  try
  {
    List<String> classpathElements = project.getCompileClasspathElements();
    classpathElements.add(project.getBuild().getOutputDirectory() );
    classpathElements.add(project.getBuild().getTestOutputDirectory() );
    URL urls[] = new URL[classpathElements.size()];

    for ( int i = 0; i < classpathElements.size(); ++i )
    {
      urls[i] = new File( (String) classpathElements.get( i ) ).toURI().toURL();
    }
    return new URLClassLoader(urls, getClass().getClassLoader() );
  }
  catch (Exception e)//gotta catch em all
  {
    throw new MojoExecutionException("Couldn't create a classloader.", e);
  }
}

public void execute() throws MojoExecutionException
{
  ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
  Thread.currentThread().setContextClassLoader(getClassLoader());

   //... your code here ...
}

Also make sure you are using the right MavenProject class. add this to your pom

<dependency>
  <groupId>org.apache.maven</groupId>
  <artifactId>maven-core</artifactId>
  <version>3.0.3</version>
</dependency>

<dependency>
  <groupId>org.apache.maven</groupId>
  <artifactId>maven-plugin-api</artifactId>
  <version>3.0.3</version>
</dependency>

This worked for me and maven3 to get dependencies into the plugin classpath.

The trick is to use @Component to inject the PluginDescriptor. Otherwise it will not be set up correctly.

@Component
private MavenProject project;
@Component
private PluginDescriptor descriptor;

private void addDependenciesToClasspath(String artifactId) {
    for (Artifact artifact : project.getDependencyArtifacts()) {
        if (artifact.getArtifactId().equals(artifactId)) {
            try {
                final URL url = artifact.getFile().toURI().toURL();
                final ClassRealm realm = descriptor.getClassRealm();
                realm.addURL(url);
            }
            catch (MalformedURLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top