Is it possible to access Maven project information from a custom plugin? - maven

I'm writing a custom plugin in Maven and would like to access information about the project. As a simple example, within my Java code, I'd like to get the project's build directory. I know that I can get it using a parameter annotation like so:
#Mojo( name="myplugin" )
public class MyPluginMojo extends AbstractMojo {
// DOES work. The project.build.directory prop is resolved.
#Parameter( property="myplugin.buildDir", defaultValue="${project.build.directory}", required=true )
private File buildDir;
public void execute () throws MojoExecutionException
{
System.out.println(buildDir.getPath());
// DOES NOT work, prints the literal string.
System.out.println("${project.build.directory}");
}
}
That feels like a hack. To start with, I have no need to expose this parameter to the pom.xml. I'm only doing it this way because within the annotation, the property gets resolved.
I'd also like access to other properties, namely the project's dependencies.
I've been googling for hours without luck. The closest thing I've found is the MavenProject plugin but I can't get it to work either and it hasn't been updated since 2009 from the looks of it.
Gradle provides the "project" variable for this when writing plugins. Does Maven simply not allow this?
--- Update ---
Thanks to Robert's link to the documentation, I got this working. One thing that was a surprise to me was that the project.build.directory is not available through the injected project. According to the docs, you inject that separately. Here's what I added to my class to get both the project object and the build directory:
#Parameter( defaultValue="${project}", readonly=true, required=true )
MavenProject project;
#Parameter( defaultValue = "${project.build.directory}", readonly=true, required=true )
private File target;
And a dependency to my pom:
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>3.0-alpha-2</version>
</dependency>

You're on the right path, it not a hack. But if you don't want to expose it as a parameter, then you should also add readonly=true. Maven also has the project variable, see http://maven.apache.org/plugin-tools/maven-plugin-tools-annotations/ for all common objects you can use within your project.

The only way to get current Maven project inside a Maven plugin is to inject it. See this question for more info.
See also the Mojo Cookbook that describe how to inject the current Maven project.

Related

Can I create a Gradle plugin that adds a dependency or another plugin based on a Gradle extension value?

Can I create a Gradle plugin that adds a dependency based on an extension value?
I have a convention plugin that I use for libraries various projects, which brings in various dependencies, takes care of boilerplate configuration, configures other plugins etc etc. I want to add an extension to the plugin that can tell the plugin whether or not to add a certain dependency, in this case it happens to be Spock, as not every library module needs the Spock dependency.
So far, my plugin looks like this
interface BasePluginExtension {
Property<Boolean> getUseSpock()
}
class BasePlugin implements Plugin<Project> {
#Override
void apply(Project project) {
BasePluginExtension basePluginExtension = project.extensions.create('basePluginConfig', BasePluginExtension)
// If a value was supplied, use it, otherwise assume we want Spock
if (basePluginExtension?.useSpock?.get() ?: true) {
// Printing for debugging purposes
println "I'm using spock! ${basePluginExtension.useSpock.get()}"
// Currently apply a plugin that applies Spock but could also just add a dependency
project.plugins.apply("test-config")
}
}
}
Then in the build.gradle file that I want to pull my plugin into, I have
plugins {
id 'base-plugin'
}
basePluginConfig {
useSpock = true
}
I'm following the docs on configuring an extension but I am getting the following error:
Cannot query the value of extension 'basePluginConfig' property 'useSpock' because it has no value available.
I've also tried the method of making an abstract class for the extension but I want the ability to have multiple configurable parameters in the future.
Is adding a dependency after plugin extension values have been configured not allowed/out of order for how Gradle works? Or am I possibly missing something obvious?

maven mojo for reading app classes and generating java

I want to write a maven plugin which will explore the classpath of my application at build time, search for classes with a certain annotation, and generate some java code adding utilities for these classes, which should get compiled in the JAR of the application.
So I wrote a mojo, inheriting from AbstractMojo, and getting the project through:
#Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
I have most of the code, and my mojo does get execute, but I'm having trouble inserting my mojo at right build phase.
If I plug it like that:
#Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES,
requiresDependencyResolution = ResolutionScope.COMPILE)
then the java code which I generate is compiled in the JAR file.
Note that I use project.addCompileSourceRoot to register the output folder.
But that isn't enough for me because it's too early in the build: I cannot read the classpath and find the classes from my project. I think they're not compiled yet.
I search for classes like so:
final List<URL> urls = List.ofAll(project.getCompileClasspathElements())
.map(element -> Try.of(() -> new File(element).toURI().toURL()).get());
final URLClassLoader classLoader = new URLClassLoader(urls.toJavaList().toArray(new URL[0]), Thread.currentThread().getContextClassLoader());
final Set<Class<?>> entities = HashSet.ofAll(new Reflections(classLoader).getTypesAnnotatedWith(MyAnnotation.class));
(I'm using vavr but you get the gist in any case)
So, by plugging my code at the GENERATE_SOURCES phase, this code doesn't work and I don't find any classes.
However, if I plug my mojo at the PROCESS_CLASSES phase:
#Mojo(name = "generate", defaultPhase = LifecyclePhase.PROCESS_CLASSES,
requiresDependencyResolution = ResolutionScope.COMPILE)
Then my classes are found, I can access the rest of the code from the application, but the code that I generate is not taken into account in the build, despite using addCompileSourceRoot.
How do I get both features working at the same time: ability to explore code from the rest of the application and ability to generate code which will be compiled with the rest of the JAR?
I guess a possible answer would be "you can't", but as far as I can tell, querydsl and immmutables are doing it (I tried reading their source but couldn't find the relevant code).
So #khmarbaise was right, what I wanted was not a maven mojo, but rather a maven annotation processor.
I found that this walkthrough was very helpful in creating one, and also this stackoverflow answer came in handy.

Spring Tool Suite 3.7.0 not reading #ConfigurationProperties for content assist in YML

Hi I have move on to spring tool suite 3.7.0 with the highly anticipated feature of YAML editor as described here https://spring.io/blog/2015/06/30/spring-tool-suite-3-7-0-released specially the content assist that it provides .
The issue I am having is that my properties class as below
#ConfigurationProperties(prefix="datasource.ucp")
#Data
public Class DumbProperties{
private String url;
private String user;
...
}
does work but when I open my application.yml I still have to provide these manually the content assist doesnt work .ALso STS givem me a warning that the property doesnt exists .Screen shot below
ALso the maven entry for the same to find #ConfigurationProperties are added as below
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
anything I am missing here!!
Two things have to be in place for the configuration properties in your own source-code to work.
The "spring-boot-configuration-processor" must be on the classpath
The project must be confgure properly so that Eclipse JDT Annotation Processing is enabled to run the spring-boot-configuration-processor as part of an eclipse workspace build.
It sounds like you have 1. so probably its number 2. that's missing.
Normally, 2. should be configured automatically by STS, but it does this as part of m2e project configuration. If you just added the configuration-processor by pasting the xml into your pom, then its likely the project-configurator has not yet been executed. So try forcing it by selecting "Update Project" from the "Maven" context menu (accessed by right click on your project).
If that doesn't help, we'll have to troubleshoot a bit more as I don't know what's missing from your project's setup.

How can I programmatically create a Plexus container for Maven?

I've written a part of a Maven plugin, but it can no longer run as a plugin. It have access to SCM, or any pom.xml, just an artifact repository and some Maven coordinates. It is meant to take Maven coordinates and print out their dependencies (performing some reporting on them). To do this it needs programmatic access to (for example):
MavenProjectBuilder
DependencyGraphBuilder
The usual stuff you'd injected into a mojo, but it's not a mojo and cannot be run as such (as I don't have a pom.xml).
The code I have hasn't helped me, e.g. AbstractMojoTestCase requires a pom.xml, something I don't have.
I'd like some code like..
DefaultPlexusContainer container = new DefaultPlexusContainer();
Context context = container.getContext();
context.put("plexus", new DefaultPlexusContainer());
container.initialize();
container.start();
MavenProjectBuilder mavenProjectBuilder = (MavenProjectBuilder) container.lookup(MavenProjectBuilder.ROLE);
DependencyGraphBuilder dependencyGraphBuilder = (DependencyGraphBuilder) container.lookup(DependencyGraphBuilder.class.getName());
MavenProject project = mavenProjectBuilder.buildFromRepository(artifact, remoteArtifactRepositories, localRepository);
// do stuff with dependencies
But there's something missing. Help!
Have a look at how it's done https://github.com/jenkinsci/lib-jenkins-maven-embedder (we use that in jenkins for similar purpose)

MavenProject: Get the available classes for use on my plugin

I'm loading a Maven project as described here. I'm trying to figure out how I can retrieve the source roots so I can figure out the Java classes I have so my Mojo can use them.
I tried a couple of the methods in there, like getResources or getScriptSources without luck. Any idea?
Thanks in advance!
Edit:
I was asked to elaborate a little bit in what I'm attempting to do, so here it is:
The plugin I'm developing will take the sources in the project and create test cases from those. Unless configured, I want to generate tests for all the classes, and for that, I need to somehow figure out where are my sources so I can configure properly.
Hope that helps.
Here's the repository. I planned on publishing it later but I provided source as requested.
Have you read the plugin developers documentation?
That page will link to Plugins Cookbook which links to Mojo Developer Cookbook which has The maven project, or the effective pom. and gives you access to org.apache.maven.project.MavenProject object via
/** #parameter default-value="${project}" */
private org.apache.maven.project.MavenProject mavenProject;
Alternatively via Java 5 annotations
#Component
MavenProject project;
You can call getCompileSourceRoots() to get a list of the directories that will be used for compilation.
You will also need to do more reading about how to setup inclusion/exclusions. You can use other plugins as examples of how to do this, e.g. maven-compiler-plugin
If you want to use annotations, it is very important to make sure your pom is configured as per using annotations and that you use annotations at the class level as well. Mixing javadoc annotations might not work.
I think the simplest solution would be to define a mojo parameter:
/**
* #parameter default-value="${project.build.sourceDirectory}"
* #required
*/
private File sourceDirectory;
or with new annotation based definition:
#Parameter(required = true, defaultValue="${project.build.sourceDirectory}"}
private File sourceDirectory;
which should give your wished result.

Resources