How does/should Maven plugins behave with regards to the order in which they process configuration options? I would expect that properties passed via CLI overwrite those defined in a <configuration> block in pom.xml.
Here's an example.
pom.xml
<plugin>
<groupId>group</groupId>
<artifactId>artifact</artifactId>
<version>1.2.3</version>
<executions>
...
</executions>
<configuration>
<url>foo.com</url>
</configuration>
</plugin>
CLI
mvn group:artifact:1.2.3:doit -Dmymojo.url=bar.com
I am currently debugging a plugin (not mine) that gives precedence to the url value defined in the POM rather than the one passed on the CLI. Is that how mojos are supposed to behave i.e. a Maven feature rather than a plugin bug? I didn't find anything mentioned in the ref guide.
As per https://issues.apache.org/jira/browse/MNG-4979 this works as designed. I find it counter-intuitive and don't find the reasons given in MNG-4979 convincing.
If your setup allows to modify the pom.xml you can work around this behavior as suggested by JF Meier (and the issue above).
<properties>
<mymojo.url>foo.bar</mymojo.url>
</properties>
<plugin>
<groupId>group</groupId>
<artifactId>artifact</artifactId>
<version>1.2.3</version>
<executions>
...
</executions>
<configuration>
<url>${mymojo.url}</url>
</configuration>
</plugin>
Through the command line, you set a property url. This would override an entry <url>foo.com</url> in the <properties> section of the POM.
Many plugins allow to set configuration entries through properties, but these properties do not automatically have the same name. In the documentation, this is usually called user property. To see examples, look e.g. at
https://maven.apache.org/plugins/maven-dependency-plugin/get-mojo.html
Related
I have a Java (11.0.7) Maven (3.0.6) multi-module project that contains the following module declarations:
<modules>
<module>jdrum-commons</module>
<module>jdrum-datastore-base</module>
<module>jdrum-datastore-simple</module>
<module>jdrum</module>
</modules>
Each of these Maven modules contains a module-info that defines the necessary requirements and exports to restrict access and visibility.
As such, jdrum-datastore-simple has some test utility classes that I reuse in jdrum's tests. By configuring the surefire plugin in jdrum's config via the code snippet below I am able to package the whole project without any issues.
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
<!-- Allow the unnamed module access to the tests at test-time -->
--add-opens jdrum/at.rovo.drum.impl=ALL-UNNAMED
--illegal-access=deny
</argLine>
</configuration>
</plugin>
</plugins>
</build>
Within the parents POM I've also configured the generation of a report via the site argument, which also generates the Javadoc of the respective projects. The configuration for the JAR containing the javadoc as well as the configuration for the Javadoc generation as part of the report are both the same and look like this:
<!-- Generate Javadoc while reporting -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<inherited>true</inherited>
<configuration>
<verbose>true</verbose>
<source>${maven.compiler.source}</source>
<show>protected</show>
<failOnWarnings>false</failOnWarnings>
<release>${maven.compiler.release}</release>
<stylesheet>java</stylesheet>
</configuration>
<reportSets>
<reportSet>
<id>html</id>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
The Javadoc generation as part of the package step, which generates the project-version-javadoc.jar as output, succeeds as both, the jdrum-datastore-simple dependencies as well as its tests, are only included at test time:
<!-- Test data store to use for testing -->
<dependency>
<groupId>at.rovo</groupId>
<artifactId>jdrum-datastore-simple</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>at.rovo</groupId>
<artifactId>jdrum-datastore-simple</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
<type>test-jar</type>
</dependency>
If I'd change the scope from test to compile or provided the Javadoc generation would also fail with an error such as
Exit code: 1 - javadoc: error - The code being documented uses packages in the unnamed module, but the packages defined in https://github.com/RovoMe/JDrum/jdrum-datastore-simple/apidocs/ are in named modules.
The issue here, as far as I understood the problem, is, that the jdrum-datastore-simple module is not added to the module path of Javadoc. The next logical step was therefore to add that module to the configuration as such:
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<additionalOptions>
<option>--add-modules</option>
<option>jdrum.datastore.simple</option>
</additionalOptions>
</configuration>
</plugin>
</plugins>
</reporting>
This adds the jdrum-datastore-simple module to the Javadoc configuration string, which can be seen in the jdrum/target/site/apidocs/options file that now contains an
...
--add-modules
jdrum.datastore.simple
...
entry. On further analyzing the generated options file it is apparent that the module path is missing out a reference to the actual JAR file and hence the Javadoc generation and thus the Maven process fails due to Javadoc not being able to locate the defined module. If I update that options file and add the path to the missing JAR file and then only perform a mvn package site the whole process succeeds and all is fine (as the pure invocation of the javadoc.bat located in the target/site/apidocs folder would as well).
Now, in order to make the whole process more dynamic I wanted to add or update the module path. However, the maven-javadoc-plugin does not directly allow this. Therefore I came up with adding a further maven-javadoc-plugin option of --module-path and a further option entry that contains the whole path. By the whole path I mean the path to every single dependency, so not only the path to jdrum-datastore-simple. This also works but due to hardcoding the path to the respective JAR files, the project is now not usable by other users unless they have the same system and path structure I used. To fix this I quickly replaced the respective path structure with ${settings.localRepository} and ${project.parent.basedir} properties on the respective modules in the module path. Unfortunately Javadoc is rather nitpicking on the path structure it accepts and it turns out that on my Windows machine Maven does return a path structure starting with C:\Users\... which Javadoc can't handle. If the path structure looks like C:/Users/... however Javadoc is fine with the values.
On further research I stumbled upon this thread which suggests to use Maven's build-helper-maven-plugin to define new properties for i.e. the M2 repository and use the built-in reg-ex capability to replace \ characters with /. However, adding a configuration such as
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>replace-local-repo-characters</id>
<goals>
<goal>regex-property</goal>
</goals>
<configuration>
<name>tag.m2repo</name>
<value>${settings.localRepository}</value>
<regex>\\</regex>
<replacement>/</replacement>
<failIfNoMatch>false</failIfNoMatch>
</configuration>
</execution>
<execution>
<id>replace-local-path-characters</id>
<goals>
<goal>regex-property</goal>
</goals>
<configuration>
<name>tag.basedir</name>
<value>${project.parent.basedir}</value>
<regex>\\</regex>
<replacement>/</replacement>
<failIfNoMatch>false</failIfNoMatch>
</configuration>
</execution>
</executions>
</plugin>
and using the introduced tags instead does not work at all as Maven is complaining about an invalid value provided. If I use $\{settings.localRepository} Maven is fine about the provided value, however in the final options file not the value of the actual settings.localRepository is updated but the provided string itself and I end up with something like $/{settings.localRepository}/org/slf4j/... which Javadoc can't resolve and therefore still misses out on the correct location to the jdrum-datastore-simple dependency.
So, how can I add the path to the missing dependency to maven-javadoc-plugin's module path defined in the generated options file so that the Maven is actually able to generate the whole report?
It seems that with java11 Update 9 (maybe also with update 8; not tested) maven-javadoc-plugin is able to correctly generate the Javadoc for multi-module projects without the need to alter the module-path.
For those interested how the actual Maven POM looks like:
Parent POM
POM for a shared module
POM for a sharing and consuming module
POM for the consuming module
I'm new to annotation processing and I'm trying to automating it with Maven. I've put this in my pom.xml:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<annotationProcessors>
<annotationProcessor>
co.aurasphere.revolver.annotation.processor.InjectAnnotationProcessor</annotationProcessor>
<annotationProcessor>
co.aurasphere.revolver.annotation.processor.RevolverContextAnnotationProcessor</annotationProcessor>
</annotationProcessors>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
The problem is that when I try to build the project I get a CompilationFailureException because Maven can't find the processors.
I've found other questions like this, solved by putting the dependency outside the plugin. I tried that, but nothing changed for me.
Am I missing something?
Thank you.
EDIT
Here is my dependency on another project which contains both the processor and the annotations:
<dependencies>
<dependency>
<groupId>co.aurasphere</groupId>
<artifactId>revolver-annotation-processor</artifactId>
<version>0.0.3-SNAPSHOT</version>
</dependency>
</dependencies>
EDIT 2:
After further investigation, I decided to decompile the processor JAR (built with Maven) and it happens that... my classes are not there. For some reasons, Maven is not compiling my classes into the JAR and that's why the classes are not found. I've tried figuring out what's wrong on that build (this never happened to me before and I've used Maven for a while...).
First of all, the packaging on that project is jar.
The classes are all under src/main/java.
I've checked in my pom.xml that the classpath and source path is the same.
Here's the processor pom:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>co.aurasphere</groupId>
<artifactId>revolver-annotation-processor</artifactId>
<version>0.0.3-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
</dependencies>
EDIT 3
Here's the output of a maven clean install on the processor project. Unfortunately the output is too long and I had to post an external link even if I know it's not good.
EDIT 4
Here are some screenshots of my dependency hierarchy: and .
Since the project was originally created as an Eclipse simple Java project and then converted to a Maven one, I tried to create a new Maven project and move everything to the new one in the hope that the problem was the Eclipse plugin that messed something up, but the error was still there.
This is an extended version of the accepted answer above provided by #Aurasphere. Hopefully this will give some explanation to how the proposed solution works.
First, some background to what is happening here. Say, we want a custom annotation processor. We implement it and put it into a JAR as Maven artefact, so that it could be consumed by other projects. When such projects are being compiled, we want our annotation processor to be recognised by Java compiler and used appropriately. To make this happen, one needs to tell the compiler about a new custom processor. Compiler looks in the resources and checks FQN of classes listed in META-INF/services/javax.annotation.processing.Processor file. It tries to find these classes in classpath and load them to run the processing of annotations used upon classes that are currently being compiled.
So, we want our custom class to be mentioned in this file. We can ask a user of our library to put this file manually, but this is not intuitive and users could be frustrated why the promised processing of annotation doesn't work. That's why we might want to prepare this file in advance and deliver it together with the processor inside JAR of our Maven artefact.
The problem is that if we simply put this file with FQN of the custom processor in it, it will trigger compiler during compilation of our artefact, and since the processor itself is not yet compiled, the compiler will show the error about it. So we need to skip annotation processing to avoid this. This can be done using -proc:none, or with Maven:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<proc>none</proc>
</configuration>
</plugin>
We might have unit tests that will need our annotation processor. In Maven, test compilation is carried out after main sources are built, and all classes are already available including our processor. We just need to add special step during processing of test sources which would use our annotation processor. This can be done using:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>process-test-annotations</id>
<phase>generate-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<proc>only</proc>
<annotationProcessors>
<annotationProcessor>fully.qualified.Name</annotationProcessor>
</annotationProcessors>
</configuration>
</execution>
</executions>
</plugin>
I've found the answer myself. I've figured out that the problem was the file javax.annotation.processing.Processor in META-INF/services/ with the configuration of the annotation processor's class. In order to fix the problem I had to add the following to the pom.xml configuration of my processor project:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<compilerArgument>
-proc:none
</compilerArgument>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
This let Maven build the classes into the actual jar and fixed the problem. I don't know if this is a bug or not but it surely looks strange to me. Thank you everybody for the help!
The easiest way is to register the annotation processor in the META-INF/services directory of the revolver-annotation-processor artifact. No Maven compiler configuration is needed.
Check if it's already registered, if not, register it yourself if you control the source code.
https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html
If you control the source code I also recommend to package the processor in the same artifact as the annotations. Like this, whenever you're using one of the annotations, the annotation processor is also picked-up by the compiler.
The accepted answer here works by disabling all annotation processing, which may not be suitable if other annotation processors need to run during the compilation. Instead, the SPI configuration file listing the newly compiled annotation processor can be added in a post-processing step. I added a directory src/main/post-resources to my project and this plugin configuration:
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>annotation-processor-spi</id>
<phase>process-classes</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
<resources>
<resource>
<directory>src/main/post-resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
I have a group of projects that have (a) generated beans, and (b) code to work with those beans. I'd like each such project to create two different artifacts: a regular jar artifact that contains all classes, and a custom beans artifact that contains only the generated types.
I put together a quick plugin that creates a second beans artifact using artifact attachments and the "beans" classifier, but it doesn't work well in m2e. For this reason, I think creating a custom packaging type (e.g., "test-jar") is The Right Thing.
To be totally clear about what I'm imagining, this pom would works today and creates two different artifacts with two different packaging types:
<project>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
which you could import with either of the following:
<dependency>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
<!-- <type>jar</type> -->
</dependency>
<dependency>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<version>${version}</version>
<type>test-jar</type>
</dependency>
I'd like to create a plugin that will let me use (for example) beans instead of test-jar to create a similar "paired" artifact.
I've poked around in the maven source code, and you can create custom types. However, "test-jar" seems to be "baked in" to maven, so I can't tell if it has some special features and I can't duplicate this behavior with my own plugin.
Of course, if there's another way to handle this kind of behavior without custom types that m2e understands -- for example, but getting m2e to understand my classifier, although that seems hard -- I'm all ears! :)
How can I make a similar paired packaging type? I've seen this answer regarding how to create custom types, but it only seems to create one artifact from a pom with the given custom packaging type.
OK, figured out how to get a custom type working with an additional artifact from the same POM.
You do use attached artifacts to generate the additional artifact. For my example, I used this call in my goal in my plugin (after I was done building my JAR file):
#Mojo(name="goal-name", defaultPhase=LifecyclePhase.PACKAGE)
public class MyMojo
extends AbstractMojo
{
#Component
private MavenProject project;
#Component
private MavenProjectHelper projectHelper;
#Component(role=Archiver.class, hint="jar")
private JarArchiver archiver;
public void execute() throws MojoExecutionException {
// Do work...
// Create JAR file...
File jarFile=createJarFile(archiver);
projectHelper.attachArtifact(project, "beans-jar", jarFile);
}
}
Note that I specified my custom type beans-jar, and no classifier.
Next, I dropped a components file into my plugin at src/main/resources/plexus/components.xml:
<component-set>
<components>
<component>
<role>org.apache.maven.artifact.handler.ArtifactHandler</role>
<role-hint>beans-jar</role-hint>
<implementation>org.apache.maven.artifact.handler.DefaultArtifactHandler</implementation>
<configuration>
<classifier>beans</classifier>
<extension>jar</extension>
<type>beans-jar</type>
<packaging>jar</packaging>
<language>java</language>
<addedToClasspath>true</addedToClasspath>
</configuration>
</component>
</components>
</component-set>
Here, I specify my custom type beans-jar and a classifier, which appears to be used to name the new attribute in the repository.
This file was based on artifact-handlers.xml from the maven-core project in the main maven repository. At the moment, that file is located here. (I found this file by grepping for test-jar in all .xml files in the maven repository.)
To import that dependency, you use:
<dependency>
<groupId>${groupId}</groupId>
<artifactId>${artifactId}</artifactId>
<type>beans-jar</type>
</dependency>
To import the dependency, you don't need to include the custom plugin.
I would suggest to try a simpler way like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>second-jar</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>second</classifier>
<includes>
<include>**/service/*</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
Via the <include> you can defined which classes would be packaged into the supplemental jar file.
I have a maven project that creates a JCA connector (.rar file). Now to use this connector in another project I build this thing for, I want to import the interfaces for the connector.
Can I somehow add a build target to the connector pom.xml, which creates a new maven artifact with just the interfaces from the connector project (lets say connector-interfaces)?
Thanks!
Maven has a strong rule, that 1 project -> 1 artifact. I suggest to split your project:
connector
+ pom.xml
++ connector-interfaces
+++ pom.xml
++ connector-impl
+++ pom.xml
The pom.xml of the connector would contain 2 modules:
<modules>
<module>connector-interfaces</module>
<module>connector-impl</module>
</modules>
In the module connector-impl, you will need to add a dependency on connector-interfaces. Add this in the pom.xml of connector-impl:
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>connector-impl</artifactId>
<version>${project.version}</version>
</dependency>
If you use the maven release plugin, add this to the parent pom:
<project>
<build>
<plugins>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<autoVersionSubmodules>true</autoVersionSubmodules>
</configuration>
</plugin>
</plugins>
</build>
</project>
That way, maven will only ask for the version 1 time when you release.
Although #WimDeblauwe is suggesting a best practice and it would probably be best to follow it his way, there is another way you can accomplish this. There is an assembly plugin that can be used to do this. Normally it's meant to package your whole project in one jar, but it can also be used to make a subset of classes and make a jar out of that.
It can be a little tricky though. To do it you'll probably need to create a custom descriptor and reference this documentation to see how to format your descriptor file. Here's an example from the documentation of how to point to a custom descriptor:
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly/src.xml</descriptor>
</descriptors>
</configuration>
[...]
</project>
In that src.xml file you'll want to <excludes> all classes except for the interfaces and set <includeDependencies> to false.
Right now, I'm writing a small java application by my own, with few maven pom.xml files. I want to make all my maven packages to compile with jdk 1.6, and I can't find a good way to do it without manually setting it on every single POMs - I'm sick of copy-and-pasting
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
in every single pom.xml file I generate.
Is there a simpler way to resolve this issue?
Create a pom-only (<packaging>pom</packaging>) project that has the compiler settings (and any other default settings) you want. You give treat it like any other project (release it; deploy it to your Maven repo, etc.).
Put a parent declaration at the top of your pom files:
<parent>
<groupId><!-- parent's group id --></groupId>
<artifactId><!-- parent's artifact id --></artifactId>
<version><!-- parent's version --></version>
</parent>
It doesn't help much if all you want to set is compiler settings. But if you find yourself configuring lots of plugins, reports and dependencies in the same way across project, you can create one parent to rule them all.
BTW - be careful about declaring dependencies and plugins in your parent pom file. Usually you'll want to favor dependencyManagement and pluginManagement. See the documentation for more details.
You could specify this plugin and configuration in your ~/.m2/settings.xml, which will then apply it to all projects.
However this has the downside of making your projects no longer portable - attempting to build the same code with the same pom.xml will fail on other machines that don't have the same settings.xml values as you.
I'm sick of copy-and-pasting
Yes, and you should use POM inheritance to avoid this and configure the maven-compiler-plugin in the parent POM.
Another option would be to use the solution suggested by #matt (and he nailed down pros and cons of the use of settings.xml).
In both cases, this is typically a setting that I like to check using the maven-enforcer-plugin and its requireJavaVersion rule that you would configure like this:
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>enforce-versions</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>1.6</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
[...]
</project>
But it can do more (like checking the maven version). Very useful.
I want to make all my maven packages to compile with jdk 1.6
If this is multi-module project just put these settings to top-level POM under pluginManagement.
If you have many independent project just copy-and-paste this configuration. Beware of "smart" solutions like setting this somewhere globally. Some day you will want to use different compiler settings for one or two of your projects and the nightmare will begin :-)
Remember...
Keep things as simple as possible, but no simpler.