Maven unpack dependencies from Ant plugin mojo - maven

I have a Maven ant plugin that bundles up a library of Ant tasks. One of them has a lot of CI tasks.
I have the plugin working and can hit the task by running
mvn -U ci:options
This brings up a menu for the different operations.
The issue i'm having is that I need to resolve dependencies in the pom before the task is executed.
From reading up I would have thought that I could add
<execution>
<goal>dependency:unpack-dependencies</goal>
</execution>
To the pluginMetaData xml file that defines the mojo, though this doesn't seem to do anything
ci.mojos.xml
<pluginMetadata 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/plugin-metadata-1.0.0.xsd">
<mojos>
<mojo>
<!-- target name to call in ant script -->
<call>run</call>
<!-- mojo goal name -->
<goal>options</goal>
<execution>
<goal>dependency:unpack-dependencies</goal>
</execution>
<parameters>
<parameter>
<name>artifactId</name>
<property>artifactId</property>
<required>true</required>
<readonly>true</readonly>
<type>java.lang.String</type>
<defaultValue>${project.artifactId}</defaultValue>
<description>Project Artifact Id</description>
</parameter>
....
ci.build.xml
<property name="project.home" location="."/>
<property name="target.dir" value="${project.home}/target"/>
<property name="build.dir" value="${target.dir}/build"/>
<property name="dependency.dir" value="${target.dir}/dependency"/>
<!-- Add contrib to the classpath -->
<taskdef resource="net/sf/antcontrib/antlib.xml"/>
<!-- Include ant utils from the shared resource -->
<include file="${dependency.dir}/shared_ant/build.xml"/>
<!-- Continuous Integration Options -->
<target name="run" description="Continuous Integration Options">
<ci.options/>
</target>
</project>
Any help greatly appreciated.
James

Related

Jacoco code coverage with Sonar and maven for integration tests in separate module

I've configured my project with SonarQube and Jacoco for code coverage. Everything works well except one thing. I divided project in many maven sub modules:
project (pom.xml) -
|-moduleA (no pom here)
| |- it (pom.xml) - integration tests for "impl" module
| |- impl (pom.xml) - implementaion + Unit tests
|-moduelB
| |- it (pom.xml) - integration tests for "impl" module
| |- impl (pom.xml) - implementaion + Unit tests
|-moduleC ...
I start build using following command:
mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent -Dmaven.compiler.debug=true install sonar:sonar
The problem is that Jacoco can easily support Unit test coverage inside the same module (so in my case Unit Tests inside impl module) but cannot analyze coverage from it module which itself does not contain any implementation classes. It just contains integration tests for impl module. It's clear for me why it doesn't work. I'm getting such info in Jacoco logs:
No JaCoCo analysis of project coverage can be done since there is no class files.
After I've defined sonar.java.binaries property inside moduleA/it/pom.xml like this:
<properties>
<sonar.java.binaries>../impl/target/classes</sonar.java.binaries>
</properties>
coverage analysis of it still fails, but message in Jacoco logs changes to:
[INFO] Analysing D:\project\moduleA\it\target\jacoco.exec
[WARNING] Coverage information was not collected. Perhaps you forget to include debug information into compiled classes?
I must mention that D:\project\moduleA\it\target\jacoco.exec file exists. I also checked that compiled classes in D:\project\moduleA\impl\target\classes contain debug data (all lines, vars and source).
I was trying with different paths inside sonar.java.binaries but result is always the same. I've tried:
../impl/target/classes
../../impl/target/classes
../../../impl/target/classes
../impl/target
...
How can I configure Jacoco (Sonar) to allow it to find classes binaries in different maven sub module related to jacoco.exec file?
Based on this question, I resorted to the Maven Ant plugin with JaCoCo's ant task library, which allows richer inclusion trees:
<project ...>
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<dependencies>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.ant</artifactId>
<version>${jacoco.version}</version>
</dependency>
<dependency>
<groupId>ant-contrib</groupId>
<artifactId>ant-contrib</artifactId>
<version>20020829</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>report-it-coverage</id>
<phase>post-integration-test</phase>
<goals><goal>run</goal></goals>
<configuration>
<skip>${skipITs}</skip>
<target name="report-it-coverage">
<taskdef name="jacoco-report" classname="org.jacoco.ant.ReportTask" classpathref="maven.plugin.classpath" />
<taskdef resource="net/sf/antcontrib/antcontrib.properties" classpathref="maven.runtime.classpath" />
<available file="${jacoco.it-coverage.data}" property="jacoco.exec.file.exists" />
<if>
<equals arg1="${jacoco.exec.file.exists}" arg2="true" />
<then>
<echo>Analyzing ITs coverage data from '${jacoco.it-coverage.data}'</echo>
<jacoco-report>
<executiondata>
<file file="${jacoco.it-coverage.data}" />
</executiondata>
<structure name="ROOT">
<classfiles>
<fileset dir="${project.basedir}/${project.parent.relativePath}">
<include name="**/target/classes/my/company/package/**/*.class" />
<exclude name="**/target/classes/my/test/glue/**/*" />
</fileset>
</classfiles>
</structure>
<html destdir="${project.reporting.outputDirectory}/jacoco-it" />
<xml destfile="${project.build.directory}/jacoco.xml" />
</jacoco-report>
</then>
<else>
<echo>Missing ITs coverage data file '${jacoco.it-coverage.data}'</echo>
</else>
</if>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Spring Boot uber jar packaging classes to root instead of BOOT-INF/classes

Hi Spring Boot Experts -
I am trying to create a spring boot uber jar that needs to be deployed to a apache storm cluster. But, the catch is that Storm is expecting all the class files in the root of the jar while the packaged app files are under "BOOT-INF/classes" when packaged using the "spring-boot-maven-plugin".
Is there a way I can have my app classes packaged directly under the root instead of "BOOT-INF/classes"?
I tried using the "maven-assembly-plugin" with the "spring-boot-maven-plugin" as shown below which creates the Uber jar with all the class files from the dependency jars packaged at the root of the uber jar, but the app classes are still at BOOT-INF/classes.
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.apache.storm</groupId>
<artifactId>storm-core</artifactId>
</exclude>
</excludes>
<requiresUnpack>
<dependency>
<groupId>com.myorg</groupId>
<artifactId>my-app-artifact</artifactId> <!-- This does not help! :( -->
</dependency>
</requiresUnpack>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
So, for my future self or for anyone who is trying to find an answer for a similar question. Here are the different things that I realized during my research for this -
Storm wants an executable java jar file
Spring Boot provides a custom jar packaging. While it confirms with java jar packaging, Spring Boot loads the classes from the BOOT-INF/classes
So, to make a Spring Boot jar work on the storm cluster while behaving as Spring Boot - we would need to create a copy of all the classes from BOOT-INF/classes to the root of the jar file.
Is this possible? and the answer is yes.
Using the approach describe here, I was able to create a Spring Boot jar with the BOOT-INF/classes copied to the root of the Spring Boot jar. This approach requires ant build.xml, ivy settings and an ivy.xml as shown below. (disclaimer: config tested only till packaging on not on the storm cluster)
Since we are able to create a Spring Boot Jar hacked with classes at the root -
Should we do it? NO.
Here are the reasons -
Spring strongly advises not taking this approach to not end up with unwanted class overwrite and class versioning issues for classes with same names across jar files and with different versions.
Spring Boot Jar packaging is not a format intended for using as a dependency jar. Read the first line here. Hence for dependency use cases, you need to stick with your plain old java modules. Spring Boot is for more of standalone executables or for deployment on containers like tomcat.
Good luck!
build.xml
<project
xmlns:ivy="antlib:org.apache.ivy.ant"
xmlns:spring-boot="antlib:org.springframework.boot.ant"
name="spring-boot-sample-ant"
default="build">
<description>
Sample ANT build script for a Spring Boot executable JAR project. Uses ivy for
dependency management and spring-boot-antlib for additional tasks. Run with
'$ ant -lib ivy-2.2.jar spring-boot-antlib.jar' (substitute the location of your
actual jars). Run with '$ java -jar target/*.jar'.
</description>
<property name="spring-boot.version" value="1.4.2.RELEASE" />
<property name="lib.dir" location="${basedir}/target/lib" />
<property name="start-class" value="com.my.main.class" />
<target name="resolve" description="--> retrieve dependencies with ivy">
<ivy:retrieve pattern="${lib.dir}/[conf]/[artifact]-[type]-[revision].[ext]" />
</target>
<target name="classpaths" depends="resolve">
<path id="compile.classpath">
<fileset dir="${lib.dir}/compile" includes="*.jar" />
</path>
</target>
<target name="init" depends="classpaths">
<mkdir dir="target/classes" />
</target>
<target name="compile" depends="init" description="compile">
<javac srcdir="src/main/java" destdir="target/classes" classpathref="compile.classpath" />
</target>
<target name="clean" description="cleans all created files/dirs">
<delete dir="target" />
</target>
<target name="build" depends="compile">
<spring-boot:exejar destfile="target/${ant.project.name}-${spring-boot.version}.jar" classes="target/classes">
<spring-boot:lib>
<fileset dir="${lib.dir}/runtime" />
</spring-boot:lib>
</spring-boot:exejar>
</target>
<target name="unjar_dependencies" depends="compile">
<unzip dest="target/classes">
<fileset dir="${lib.dir}/compile">
<include name="my-app-common-0.1-SNAPSHOT.jar" />
</fileset>
</unzip>
</target>
<!-- Manual equivalent of the build target -->
<target name="manual" depends="compile, unjar_dependencies">
<jar destfile="target/manual/${ant.project.name}-${spring-boot.version}.jar" compress="false">
<mappedresources>
<fileset dir="target/classes" />
<globmapper from="*" to="BOOT-INF/classes/*"/>
</mappedresources>
<mappedresources> <!-- **** this mapped resources block does what I was looking for **** -->
<fileset dir="target/classes" />
<globmapper from="*" to="/*"/>
</mappedresources>
<mappedresources>
<fileset dir="src/main/resources" erroronmissingdir="false"/>
<globmapper from="*" to="BOOT-INF/classes/*"/>
</mappedresources>
<mappedresources>
<fileset dir="${lib.dir}/runtime" />
<globmapper from="*" to="BOOT-INF/lib/*"/>
</mappedresources>
<zipfileset src="${lib.dir}/loader/spring-boot-loader-jar-${spring-boot.version}.jar" />
<manifest>
<attribute name="Main-Class" value="org.springframework.boot.loader.JarLauncher" />
<attribute name="Start-Class" value="${start-class}" />
</manifest>
</jar>
</target>
</project>
ivysettings.xml
<ivysettings>
<settings defaultResolver="chain" />
<resolvers>
<chain name="chain" returnFirst="true">
<!-- NOTE: You should declare only repositories that you need here -->
<filesystem name="local" local="true" m2compatible="true">
<artifact pattern="${user.home}/.m2/repository/[organisation]/[module]/[revision]/[module]-[revision].[ext]" />
<ivy pattern="${user.home}/.m2/repository/[organisation]/[module]/[revision]/[module]-[revision].pom" />
</filesystem>
<ibiblio name="ibiblio" m2compatible="true" />
<ibiblio name="spring-milestones" m2compatible="true" root="http://repo.spring.io/release" />
<ibiblio name="spring-milestones" m2compatible="true" root="http://repo.spring.io/milestone" />
<ibiblio name="spring-snapshots" m2compatible="true" root="http://repo.spring.io/snapshot" />
</chain>
</resolvers>
</ivysettings>
ivy.xml
<ivy-module version="2.0">
<info organisation="org.springframework.boot" module="spring-boot-sample-ant" />
<configurations>
<conf name="compile" description="everything needed to compile this module" />
<conf name="runtime" extends="compile" description="everything needed to run this module" />
<conf name="loader" description="Spring Boot loader used when manually building an executable archive" />
</configurations>
<dependencies>
<dependency org="org.springframework.boot" name="spring-boot-starter" rev="${spring-boot.version}" conf="compile">
<exclude org="ch.qos.logback" name="logback-classic"/>
</dependency>
<dependency org="org.springframework.boot" name="spring-boot-loader" rev="${spring-boot.version}" conf="loader->default" />
<dependency org="org.apache.storm" name="storm-core" rev="1.0.2">
<exclude org="org.apache.logging.log4j" name="log4j-slf4j-impl"/>
<exclude org="org.apache.logging.log4j" name="log4j-core"/>
</dependency>
<dependency org="com.mycompany" name="app-common" rev="0.1-SNAPSHOT"/>
<dependency org="org.apache.storm" name="storm-kafka" rev="1.0.2"/>
<dependency org="org.apache.kafka" name="kafka_2.10" rev="0.10.1.0"/>
<dependency org="org.apache.kafka" name="kafka_2.10" rev="0.10.1.0"/>
<dependency org="org.apache.httpcomponents" name="httpcomponents-client" rev="4.5.2"/>
<dependency org="org.eclipse.paho" name="org.eclipse.paho.client.mqttv3" rev="1.1.0"/>
<dependency org="com.amazonaws" name="aws-java-sdk-s3" rev="1.11.53"/>
<dependency org="com.jcraft" name="jsch" rev="0.1.54"/>
<dependency org="io.netty" name="netty-handler" rev="3.7.0.Final"/>
</dependencies>
</ivy-module>
Is there a way I can have my app classes packaged directly under the root instead of "BOOT-INF/classes"?
Yes, you just need to use Spring Boot 1.3. Back to maven... in your pom.xml if you declare your parent like this:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.5.RELEASE</version>
</parent>
then your classes (and other files) will be placed at the root level. This is the "old way" for spring boot.
In version 1.4 they changed the spring boot jar structure to use the BOOT-INF directory. So, if you use <version>1.4.1.RELEASE</version> for example, then your classes will be under BOOT-INF/classes. An undesirable side effect is that your configuration files (e.g., application.properties, application-myprofile.properties, etc.) will also be under BOOT-INF/classes, even though they are not Java classes.

How to fully disable javadoc checking with checkstyle maven plugin

i want to use the Maven Checkstyle plugin with a custom configuration that tells Checkstyle to not warn or error on missing Javadoc. Is there a way to do this?
Just found it myself. To fully ignore all javadoc checking for everthing, add this to your checkstyle configuration:
<!-- No need for Javadoc -->
<module name="JavadocType">
<property name="severity" value="ignore"/>
</module>
<module name="JavadocMethod">
<property name="severity" value="ignore"/>
</module>
<module name="JavadocVariable">
<property name="severity" value="ignore"/>
</module>
One good option would be configuring a suppressions filter.
Plugin configuration:
<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">
<!-- ... -->
<build>
<plugins>
<!-- ... -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.17</version>
<executions>
<execution>
<id>verify</id>
<phase>verify</phase>
<configuration>
<encoding>UTF-8</encoding>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<linkXRef>false</linkXRef>
<suppressionsLocation>
checkstyle-suppressions.xml
</suppressionsLocation>
<suppressionsFileExpression>
checkstyle.suppressions.file
</suppressionsFileExpression>
</configuration>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!-- ... -->
</project>
checkstyle-suppressions.xml file:
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.0//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_0.dtd">
<suppressions>
<suppress checks="Javadoc" files="."/>
</suppressions>
Then running
$ mvn verify
Does not output any Javadoc-related Checkstyle errors.
Many other examples on suppressions filters may be found in checkstyle repository.
Below is the example with the Gradle 7.3.2 setup.
build.gradle
plugins {
id 'checkstyle'
}
Folder structure (root level).
├── config
   └── checkstyle
   ├── checkstyle.xml
   └── suppressions.xml
Below is the entry in checkstyle.xml to include suppressions.xml
<module name="SuppressionFilter">
<property name="file" value="${config_loc}/suppressions.xml" />
</module>
And now you can have the suppressions in the suppressions.xml file to ignore a particular type of check.
for e.g. Below code base will ignore the mentioned checks for all files (regex can be applied to ignore particular matching set of files.)
<suppressions>
<suppress files="." checks="JavadocMethod"/>
<suppress files="." checks="JavadocPackage"/>
<suppress files="." checks="JavadocVariable"/>
<suppress files="." checks="MissingJavadocMethod"/>
<suppress files="." checks="JavadocPackage"/>
</suppressions>
Now execute the Checkstyle task on consolidated project files by
gradle check
If specific execution is needed then use checkstyleMain or checkStyleTest to execute checkstyle report on Source files or test files respectively.

EMMA code coverage for all modules

I am new to maven and our project has several modules. my goal is generate one coverage report for the entire project (that has all the modules). below is the rough skeleton of our Based on project's 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>1.0.0</modelVersion>
<groupId>com.xx.test</groupId>
<artifactId>test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>test</name>
<modules>
<module>module1</module>
<module>module2</module>
<module>module3</module>
<module>module4</module>
<module>module5</module>
</modules>
<build>
.
.
.
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>emma-maven-plugin</artifactId>
<version>1.0-alpha-3</version>
</plugin>
</plugins>
</reporting>
</project>
as you see above i have added emma plug-in and executed following : mvn emma:emma
This is generating individual coverage reports for each module under the dir called 'target'. is there a way i can consolidate all these reports into one report?
any pointers..?
Following is the approach to achieve required results
add a JaCoCo plugin to the parent pom.xml so all projects could generate a coverage report.
create new module ‘coverage’ for appending all the results of the Jacoco plugin…this is just one time thing.
in pom.xml of this new module, insert the required plugins to join all the coverage information
a. define all properties where all the classes,sources,generated-sources etc, since the JaCoCo report plugin requires you set the location of the build directory, class directory, source directory or generated-source directory
b. we are using ANT task with Maven. JaCoCo Ant task will merge results from multiple JaCoCo file results
now from parent pom.xml run : mvn clean install
if module has any test cases , ‘jacoco.exec’ file will be generated for that module (this file has all the coverage information that is required for reporting), individual module code coverage reporting is also generated at this point (under \target\site\jacoco)
and finally from ‘coverage’ pom.xml run : mvn clean install
a. this is going to run ANT reporting task within maven that will grab all the generated ‘jacoco.exec’ files for all the modules, merge the results and generate the report.
Below is the ANT reporting target that can be run within maven
<?xml version="1.0" encoding="UTF-8" ?>
<project name="maven-antrun-" default="main" >
<target name="main">
<echo message="Generating JaCoCo Reports"/>
<taskdef name="report" classname="org.jacoco.ant.ReportTask">
<classpath path="./target/jacoco-jars/org.jacoco.ant.jar"/>
</taskdef>
<mkdir dir="./target/coverage-report"/>
<report>
<executiondata>
<fileset dir="../module1/target">
<include name="jacoco.exec"/>
</fileset>
<fileset dir="../module2/target">
<include name="jacoco.exec"/>
</fileset>
</executiondata>
<structure name="Project Code Coverage">
<group name="project">
<classfiles>
<fileset dir="../module1/target/classes"/>
<fileset dir="../module2/target/classes"/>
</classfiles>
<sourcefiles encoding="UTF-8">
<fileset dir="../module1/src/main/java"/>
<fileset dir="../module2/src/main/java"/>
</sourcefiles>
</group>
</structure>
<html destdir="./target/coverage-report/html"/>
<xml destfile="./target/coverage-report/coverage-report.xml"/>
<csv destfile="./target/coverage-report/coverage-report.csv"/>
</report>
</target>
</project>
Is there any way to do this generically for multiple submodules in the ant report task? Specifying the exact directories works OK, but not ** dirset references. This works OK:
<classfiles>
<dirset dir="${basedir}">
<include name="module1/target/classes"/>
<include name="module2/target/classes"/>
OR
<dirset dir="">
<include name="module1/target/classes"/>
<include name="module2/target/classes"/>
OR
<dirset dir="" includes="module1/target/classes,module2/target/classes"/>
These do not work (0 classes, or 'Error while creating report' with no further details):
<dirset dir="">
<include name="**/target/classes"/>
OR
<dirset dir="${basedir}">
<include name="**/target/classes"/>
OR
<dirset dir="" includes="**/target/classes"/>
OR
<dirset dir="${basedir}" includes="**/target/classes"/>
Interestingly, sourcefile ** references produces the HTML source links OK, I thought it worked exactly the same as classfiles:
<sourcefiles>
<dirset dir="" includes="**/src/main/java"/>
This does not work:
<sourcefiles>
<dirset dir="${basedir}" includes="**/src/main/java"/>

bring -f option to multi module

there is -f option in maven, that allows to specify alternate pom.xml file. Is there a possibility, that I can also bring this behaviour to the executed modules? Now it looks like, that when I have this structure: projectA: pom.xml pom.xml2
projectB: pom.xml pom.xml2
And when I run maven with -f pom.xml2 option as reactor with projectB specified as module, it looks like that it picks pom.xml2 from the projectA, and it picks pom.xml from projectB. Is there a way, how can I propagate the -f option to the modules?
Thanks for answering.
Because we can specified pom file in module definition.1
Here's an example for using alternative pom file in module.
<modules>
<module>child1/pom-jdk14.xml</module>
<module>child2/pom-jdk14.xml</module>
</modules>
As Jörn Horstmann comments I would try lots of things to get this working with profiles in one pom.
If that's not possible the only way I can think of to get this working is to bypass the normal maven mechanism by using a "switching pom" with profiles. This pom is put as pom.xml in each module and has a profile for each of your pom.xml2 (or others) and in that profile executes another maven build f.e. via the antrun-plugin with the -f for the pom you need:
<profile>
<id>xml2</id>
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>build pom.xml2</id>
<phase>prepare-package</phase> <!-- whatever suits you -->
<configuration>
<target>
<echo level="info" message="Building pom.xml2..." />
<exec executable="cmd" dir=".">
<arg value="/c" />
<arg value="mvn" />
<arg value="-f" />
<arg value="pom.xml2" />
<arg value="install" /> <!-- enter which phase you need -->
</exec>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

Resources