Maven validate - Get child module pom directories - maven

A business team outside of the development team needs some information out of a source controlled repo that we house source in. We're using Maven to perform our builds, and using Git for source control. What I want to do is get the reactor build order, and feed that into a script to automate some searching in Git to provide the business team what they need, but I need to make sure I'm telling Git to search the appropriate directories (and not just the root directory of the entire repo).
So obviously I can do something like mvn validate to provide me:
foo> mvn validate
[INFO] Reactor Build Order:
[INFO]
[INFO] foo
[INFO] sub_foo_1
[INFO] sub_foo_2
...
Except I have no way to take that and hand that off to Git for additional information because I don't know what directories any of the child modules live in.
Is there any way I can find out the path to the individual pom files that are part of the reactor build order?

I'm posting this as an answer, but it wouldn't have been my first choice; hoping somebody else will have something better. I was really hoping there was a native Maven way to do [something like] this.
Basically, I created a small plugin to print out the information.
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/maven-v4_0_0.xsd">
<groupId>foo.bar.baz.demo</groupId>
<version>1.2.3-SNAPSHOT</version>
<artifactId>test_plugin</artifactId>
<packaging>maven-plugin</packaging>
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.2.1</version>
</dependency>
</dependencies>
</project>
Code:
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.project.MavenProject;
#Mojo(name = "print_pom_dir")
public class TestMojo extends AbstractMojo {
public void execute() throws MojoExecutionException {
MavenProject object = (MavenProject) this.getPluginContext()
.get("project");
getLog().info(
"Pom directory: " + object.getBasedir().getAbsolutePath());
}
}
Where I can put a build execution in the parent such as:
<build>
<plugins>
<plugin>
<groupId>foo.bar.baz.demo</groupId>
<version>1.2.3-SNAPSHOT</version>
<artifactId>test_plugin</artifactId>
</plugin>
</plugins>
</plugins>
So all child modules inherit it.
Then all I have to do is run mvn foo.bar.baz.demo:test_plugin:1.2.3-SNAPSHOT:print_pom_dir from the root pom, and stdout gets my data for my to regexp out.
I wonder if putting it to a file or something in the target area might be cleaner, but I'm not too clear on best practices for Maven and whether or not changing the target directory for something not related to the build is standard, or how to get the root to know about the output from all child executions of the plugin for that matter.

Related

How to use log4j with maven and java9 in Eclipse Oxygen?

I try to migrate a Java8 project to Java9. The auto generated module-info.java contains an entry
requires log4j;
and an error is shown:
log4j cannot be resolved to a module
=> How do I correctly include log4j as a module dependency with Java9?
(I have the same issue for following dependencies:
requires hibernate.core;
requires hibernate.jpa.2.1.api;
requires jcommander;
requires junit;
requires reflections;
)
What I did so far:
Installed Java 9.0.1
Upgraded Eclipse to Oxygen.1a Release (4.7.1a)
Changed Compliance level of my Java project to 9
Generated module-info.java with Right click on project=>Configure=>Generate module-info.java
Updated the plugins in my pom.xml file (also see https://cwiki.apache.org/confluence/display/MAVEN/Java+9+-+Jigsaw) and set java version to 9:
<!-- plugin for compile phase (and test-compile phase) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<!-- specify current java version here: -->
<source>9</source>
<target>9</target>
</configuration>
</plugin>
Updated log4j version in pom.xml file since log4j 1.2 does not seem to work with Java9 (see https://blogs.apache.org/logging/entry/moving_on_to_log4j_2)
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.1</version>
</dependency>
Errors in module-info.java
Even if the compliance level of the project has been set to Java9, there might be shown misleading errors for module-info.java in Eclipse.
I expected that an update of the maven project would only be required if I change the pom.xml file. Now I learned that changing the module-info.java also requires a manual update of the maven project.
=> update the maven project (Alt+F5)
After that update my errors vanished.
I also learned that it is better to first update the versions in the pom.xml file and then generate the module-info.java. Otherwise the module-info.java will include non-existing modules like "requires log4j" instead of "requires log4j.api"
Another misleading error in module-info.java might occur due to pom packaging, see below.
Unresolved imports
For the case that an import can not be resolved the question might be "Which corresponding (new) module do I need for that (old) import?". What might help here:
Search at following page for the required page:
http://cr.openjdk.java.net/~mr/jigsaw/ea/module-summary.html
(lists all exported packages of the JDK-modules)
Use JDeps on the (old) *.jar file to get a list of required jar files, e.g.
jdeps --class-path "lib" -recursive MY-OLD.jar >output.txt
Use jar to find the module for a jar file
jar --describe-module --file REQUIRED-JAR-FILE.jar
Also see:
What are the predefined modules in JDK9 or Which module do I need to fix dependency problems?
https://blog.codefx.org/tools/jdeps-tutorial-analyze-java-project-dependencies/#Getting-To-Know-JDeps
Re-Export dependencies
In order to automatically make log4j visible for a grandparent project
grandparent => parent => log4j
you might want to use
requires transitive log4j.api
in parent instead of
requires log4j.api
in grandparent. Also see:
What's the difference between requires and requires transitive statements in Java 9 module declaration
POM packaging
My main issue seems to be that my Java8 pom.xml file used pom packaging:
<packaging>pom</packaging>
If I remove that line, no errors are shown in module-info.java
Also see this extra question: How to use maven with Java9.0.1 and pom packaging in Eclipse Oxygen 1a Release (4.7.1a)?
New log4j version
A. In addition to the change "requires log4j" => "requires log4j.api" I had to adapt the calling code for the new log4j version that is compatible to Java9:
private static Logger sysLog = Logger.getLogger(Main.class);
to
private static Logger sysLog = LogManager.getLogger(Main.class);
B. log4j 2 does not have PropertyConfigurator. Also see this related question:
PropertyConfigurator in log4j2
C. log4j 2 does not support log4j.properties files. Previously I used
src/main/resources/META-INF/log4j.properties
for configuration. Now I use
src/main/resources/log4j2.xml
Also see
Log4j 2 doesn't support log4j.properties file anymore?
Converting log4j.properties to log4j.xml
Working example as a reference
pom.xml:
<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>Log4JWithJava9</groupId>
<artifactId>Log4JWithJava9</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<release>9</release>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.1</version>
</dependency>
</dependencies>
</project>
module-info.java:
module Log4JWithJava9 {
requires javafx.base;
requires log4j.api;
}
Main.java:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Main {
private static Logger sysLog = LogManager.getLogger(Main.class);
public static void main(String[] args) {
}
}
Eclipse plugins
In order to add log4j to an eclipse plugin, I copied the file log4j-api-2.10.0.jar in a folder "lib" and added the jar file to Java Build Path=>Libraries=>ModulePath
Instead of requires log4j.api I had to use requires org.apache.logging.log4j
I was able to make this work somehow.
I downloaded the latest Eclipse Photon Release (4.8.0), I don't know if this fix will work with older versions.
I changed the module-info.java to have the following lines:
requires log4j.api;
requires log4j.core;
I had to switch to an earlier version of log4j, 2.8.2.
I changed all my imports that say import org.apache.logging.log4j.*; to not use the wildcard. So they changed to: import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
That's it, it works with both Maven and Eclipse.

Maven plugin dependency cannot use parent pom property

I'm hitting a weird edge use case with Maven & curious why it's behaving the way it does.
I'm defining a property in my parent project like so:
<properties>
<some.property.version>1.0.0.0</some.property.version>
</properties>
Now, in a module, I set a version of a dependency for a plugin like so:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>bob</artifactId>
<version>1.0.0.0</version>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>example</artifactId>
<version>${some.property.version}</artifactId>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
This causes Maven to spit out an error:
[ERROR] 'build.plugins.plugin[org.apache.maven.plugins:bob].dependencies.dependency.version' for org.example:example:jar must be a valid version but is '${some.property.version}'. # line 350, column 16
What's bizarre to me is if I move the property being defined down into the module itself, Maven compiles just fine. Is this a bug? Or are there restrictions of visibility to parent pom properties in a plugin for a module?
An insanely fast response from the Apache Maven's distribution list! The parent pom had been refactored, and the module was pointing to the stale parent's artifactId. Kudos to Robert!
Hi,
This makes me wonder if the "right" parent is used, so please double
check the groupId, artifactId and version. If both parent and module
are part of the same multi-module, be sure that the relativePath is
correct (defaults to ../pom.xml) You could also use "mvn
org.apache.maven.plugins:maven-help-plugin:2.2:effective-pom" to
verify that the property is really there with the expected value. If
this is all as expected, then it seems to be a bug.
thanks, Robert

maven release:perform with Perforce does not create target/checkout directory

I can not make the maven-release plugin work with Perforce. The release:prepare seems to work correctly: a Perforce label is created and it contains the correct files. Yet when I run release:perform it fails because the target/checkout directory is empty.
I've done some experiments. If I only sync to my pom.xml and then run
mvn scm:checkout
then my other files are checked out into the P4 root directory just as if I had done a
p4 sync ...
Yet Maven outputs
[INFO] Checkout working directory: /home/chris/perforce/pips/target/checkout
[INFO] sync -f //depot/pips/...
Maven thinks it's checking out the target/checkout but it is not. I dug into the P4Maven code a little bit and noticed this in P4Executor.java
public String getRepoPath(P4ScmProviderRepository repo, File basedir) {
// Handles a case where executing release:prepare on a module with an
// invalid SCM location. The release.properties contains the invalid URL
// for checkout during release:perform and the basedir is not the module
// root. So, the trailing target/checkout directory must be removed.
if (basedir.toString().replace('\\', '/').endsWith("/target/checkout")) {
String dir = basedir.toString();
basedir = new File(dir.substring(0, dir.length()
- "/target/checkout".length()));
if (getLogger().isDebugEnabled()) {
logger.debug("Fixing checkout URL: " + basedir);
}
}
This code is pretty clear. P4Maven won't check things out to target/checkout. If you tell it to checkout to target/checkout it will simply remove the "target/checkout" and checkout to the root directory. This is consistent with what I see being done. I also see the "Fixing checkout URL" message when I run release:perform
How then do people work around this?
Below is my pom.xml. I am using
Perforce Server version: P4D/LINUX26X86_64/2014.2/935585 (2014/09/16). p4d is running locally. Authentication is disabled and I don't need a password.
Nexus 2.10.0-02 running locally.
Ubuntu Linux.
Maven 3.2.3
java 1.7.0_55
Thanks.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.ap
ache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.icap</groupId>
<artifactId>pips</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>pips</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<scm>
<connection>scm:p4:192.168.1.8:1666://depot/pips</connection>
<developerConnection>scm:p4:192.168.1.8:1666://depot/pips</developerConnection>
</scm>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-plugin</artifactId>
<version>1.4</version>
<dependencies>
<dependency>
<groupId>com.perforce</groupId>
<artifactId>p4maven</artifactId>
<version>[2011,2012)</version>
</dependency>
</dependencies>
<configuration>
<connectionType>connection</connectionType>
<username>chelck</username>
<includes>**</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.1</version>
<dependencies>
<!-- P4Maven -->
<dependency>
<groupId>com.perforce</groupId>
<artifactId>p4maven</artifactId>
<version>[2011,2012)</version>
</dependency>
</dependencies>
<configuration>
<connectionType>connection</connectionType>
<username>chelck</username>
<includes>**</includes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<fork>true</fork>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<distributionManagement>
<repository>
<id>deployment</id>
<name>Internal Releases</name>
<url>http://localhost:8081/nexus/content/repositories/releases/</url>
</repository>
<snapshotRepository>
<id>deployment</id>
<name>Internal Releases</name>
<url>http://localhost:8081/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>
</project>
I have struggled for several days on perforce / maven / release integration and finally got it working. Here are my findings, they might help you and others with similar issues.
Do not use the perforce plugin as described on perforce.com (http://www.perforce.com/perforce/r11.1/manuals/p4maven/index.html). Although it is the first link to come out of a search on google, my understanding is that this plugin is outdated.
In your configuration, if you refer to scm:p4:... you are using the com.perforce plugin. Instead, use org.apache.maven.scm/maven-scm-provider-perforce which uses scm:perforce:... syntax. Your <scm> should look like this:
<properties>
<p4path>//perforce-path/toyour/project</p4path>
</properties>
<scm>
<connection>scm:perforce:yourp4server:1666:${p4path}</connection>
<developerConnection>scm:perforce:yourp4server:1666:${p4path}</developerConnection>
<url>perforce:yourp4server:1666:${p4path}</url>
</scm>
The newer plugin does not require username/password, it will use your active P4 credentials.
I am using maven 3.2.5 and did not have to specify any plugin dependency. The built-in scm recognized the :perforce: scm and downloaded maven-scm-provider-perforce 1.9.2 which is the latest as of this writing. You can make sure you are using an up to date plugin by deleting the maven-scm-provider-perforce folder in .m2\repository\org\apache\maven\scm and then doing a mvn scm:status - it will download the missing plugin and you can see which one it picked.
Here is my maven-release-plugin definition:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.1</version>
</plugin>
I do not have a maven-scm-plugin definition in my pom (I used the default settiings from the superpom).
Test on a non-module project.
You should be able to do the following without error:
- mvn scm:status to check that your scm setup is working correctly
- mvn release:prepare
- mvn release:perform
If you get errors, rollback (mvn release:rollback), make sure everything is back to normal, and rerun with mvn -X to get more details.
In my case, a non-modular project worked without much trouble.
Test on the parent of a reactor project.
There are a few things that were not obvious to me at first and that I found out:
When the is defined in the parent pom, the child module name gets appended to the path for the child module. If your child is not physically under the parent (in my case, it was beside), you need to redefine a configuration in the child pom. In my case, I was using a property (p4path above), simply changing its value was not enough. This was counter-intuitive since the parent pom is normally just a "include", and I expected the parent definitions to be "copy-pasted" in the child module, but in this particular case, it is modified to include the module name at the end of the scm path when it is copy-pasted. [More on modules that do not sit under the parent directory below]
A temporary client spec is created by the perforce scm plugin. This was also counter intuitive, I expected the scm plugin to use my current client-spec. Instead, it will create a new one on the fly, and populate it with the path you provided only and name it username-hostname-MavenSCM-localpath. I was initially surprised by that and tried to change it so it would use by client-spec by customizing the maven.scm.perforce.clientspec.name property, this behaviour was actually correct. With this temporary client-spec, the plugin can limit the lookup for changes, the submit and tagging to this directory. It also makes it easy to track what was done by this plugin in the perforce history.
Creation of the temporary client spec will fail if the modules are not under the parent directory. As I described above, my modules were sitting beside the parent. My <module> tag in the parent pom referred to them using <module>../mymodule</module> which worked fine in eclipse for everything else. The scm perforce plugin includes modules in the client spec by appending the path to the module to the path of the parent. Since Maven resolves the ../ to an absolute path, the client spec ends up containing something like this: //perforce-path/toyour/project/C:\Path\To\Your\Project/mymodule/ and the client spec fails. I could not find a work around for this issue other than moving my modules under their parent. Since the client spec was invalid, I had no file being checked-out under target/checkout. You will get the content of your generated client spec on the console by using mvn -X when invoking release:perform.
Moral of the story: You want to make sure that all your modules sit under their parent directory. When you do this, everything becomes simple and magical: you can leave the declaration in your parent pom and the module path gets magically appended for the module scm paths. You can refer to your modules using <module>mymodule</module> which gets correctly appended in the client-spec. Tagging of the parent in perforce ends up also tagging the modules since they are contained within.

Generating the .war file from maven dependency project

Here I have two projects those are project(1) and project(2).
I am going to generate a .war file for the project(1) but it depends on project(2).
I built the project(2) as a .jar file and added it to the project(1)'s build path but while runnig mvn install it results in compilation errors like:
package com.disha.db.dao.orm.gen does not exist` the package is in the project(2).
Can any one please help to me.
You have to delegate dependency management to Maven and that's actually where it comes in hand, otherwise you can move on packaging and resolving inter-projects dependencies by hand and let the Maven alternative be dropped.
You should make sure you have provided the correct Project Object Module description for your projects (pom.xm) along with tha packaging type.
Then since you want the project(2) to be availble for project(1) at compilation time, you have to declare, project(2) as a dependency of project(1).
project(2) pom.xml (I will refer to it as project-2 since 'project(2)' does not match a valid id pattern.):
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>project2.group.id</groupId>
<artifactId>project-2</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
</project>
Now the project(1) will refer to the project-2 artifact as a dependency with scope compile:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>project1.group.id</groupId>
<artifactId>project-1</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>project2.group.id</groupId>
<artifactId>project-2</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
Note: Update the group and artifact IDs with ones you are using.

Maven Artifact.getFile() returns NULLin a multi module project

I am developing a Maven plugin in a multi module project, what I want to use later in the same project. This is the project's layout:
sk:a:1.0:pom
|
--sk:a0:1.0:jar (the custom plugin)
|
--sk:a1:1.0:pom
| |
| --sk:a11:1.0:jar (a simple JAVA jar)
| |
| --sk:a12:1.0:jar (a simple JAVA jar)
|
sk:a2:1.0:pom
The sk:a2:1.0 has a dependencies on sk:a11:1.0 and on sk:a12:1.0 and the plugin sk:a0:1.0:
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>sk</groupId>
<artifactId>a</artifactId>
<version>1.0</version>
</parent>
<artifactId>a2</artifactId>
<dependencies>
<dependency>
<groupId>sk</groupId>
<artifactId>a11</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>sk</groupId>
<artifactId>a12</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>sk</groupId>
<artifactId>a0</artifactId>
<version>1.0</version>
<executions>
...
</executions>
</plugin>
</plugins>
</build>
</project>
The custom plugin from sk:a0:1.0 in its execute() calls the
Set<Artifact> artifacts = project.getDependencyArtifacts();
method and iterates on all the artifacts, with:
for(Iterator<Artifact> iterator = artifacts.iterator(); iterator.hasNext();)
{
Artifact artifact = iterator.next();
getLog().info((artifact.getFile() == null ? "artifact NULL" : "artifact OK"));
}
and now it displays "artifact NULL". In my plugin I need to able to read the artifact's files (like a java.io.File). How could I get the File for each dependency?
Thank you,
SK
It looks like the problem is that Maven has not yet resolved all of the project's dependencies, at the point at which your mojo is being executed.
So for example if your mojo runs in the compile phase, then maven has not yet resolved the test dependencies (those marked with test in your pom), and for all of these test artifacts no file will be available yet.
You can use the #requiresDependencyResolution <required-classpath> mojo javadoc tag, to tell maven to resolve the required dependencies.
From the documentation it states the following about this annotation:
Flags this Mojo as requiring the dependencies in the specified class path to be resolved before it can execute.
So in your Mojo you can simply add the following annotation in your mojo's javadoc:
/**
* #goal your-goal
* #requiresDependencyResolution test
*/
public class YourMojo extends AbstractMojo
This basically will tell maven to resolve all dependencies that would be included in the test classpath (which is basically all dependencies). So your artifact.getFile() call should now always return a File.

Resources