In Maven, how do I customize lifecycle phases? - java

I have separated a Java EE project in the following submodules:
project-war
project-ejb
project-ear
project-test
I also have a root pom which includes the above modules. Since I have tests in a separate project, theres no point in running the test phases in the 3 first modules, as theres no point in compiling or packaging the last module since it only contains tests for the other 3 modules. My question is : How can I remove the test phases from the first 3 modules and how can I remove the other phases from the test project?

You can do that by setting up different profiles: http://maven.apache.org/guides/introduction/introduction-to-profiles.html
exp:
<profile>
<id>deploywar</id>
<build>
<plugins>
<plugin>
<groupId>net.fpic</groupId>
<artifactId>tomcat-deployer-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<id>pos</id>
<phase>install</phase>
<goals>
<goal>deploy</goal>
</goals>
<configuration>
<host>${deploymentManagerRestHost}</host>
<port>${deploymentManagerRestPort}</port>
<username>${deploymentManagerRestUsername}</username>
<password>${deploymentManagerRestPassword}</password>
<artifactSource>
address/target/addressservice.war
</artifactSource>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<!-- Defines the QA deployment information -->
<profile>
<id>qa</id>
<activation>
<property>
<name>env</name>
<value>qa</value>
</property>
</activation>
<properties>
<deploymentManagerRestHost>10.50.50.50</deploymentManagerRestHost>
<deploymentManagerRestPort>58090</deploymentManagerRestPort>
<deploymentManagerRestUsername>
myotherusername
</deploymentManagerRestUsername>
<deploymentManagerRestPassword>
myotherpassword
</deploymentManagerRestPassword>
</properties>
</profile>
Which you would call the deploywar profile in a cli with mvn -Pdeploywar -Denv=dev clean install

Related

Quarkus and provided dependencies (sapjco3.jar)

I currently try to migrate an old Java EE solution that connects via RFC to a SAP-System to an approach with Quarkus.
As the project uses maven, I face again the issue of the sapjco3.jar library from SAP that prevents the library from being renamed.
If I add the library as dependency like that
<dependency>
<groupId>com.sap</groupId>
<artifactId>sapjco3</artifactId>
<version>3.1</version>
</dependency>
it will be added to the lib-directory with the name com.sap.sapjco3-3.1.jar.
Unfortunately that ends up in an excpetion
java.lang.ExceptionInInitializerError: JCo initialization failed with java.lang.ExceptionInInitializerError: Illegal JCo archive "com.sap.sapjco3-3.1.jar". It is not allowed to rename or repackage the original archive "sapjco3.jar".
There are already some articles on Stackoverflow describing that issue and there is also a SAP-note on that: https://apps.support.sap.com/sap/support/knowledge/en/2182414
So I solved the issue in the "old" approach, setting the dependency to provided and copying it with the maven-dependency-plugin.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>process-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeArtifactIds>sapjco3</includeArtifactIds>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<stripVersion>true</stripVersion>
</configuration>
</execution>
</executions>
</plugin>
Unfortunately that does not work with quarkus.
In development if I use
mvn quarkus:dev
the provided seems to be ignored and I still get the message.
JCo initialization failed with java.lang.ExceptionInInitializerError: Illegal JCo archive "sapjco3-3.1.jar". It is not allowed to rename or repackage the original archive "sapjco3.jar".
If I pack it and try to deploy it to a docker-container
mvn clean package
it basically works to copy the jar-file to the lib-directory, if I change the copy to another phase in the pom.xml
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
but I get build warnings first:
[WARNING] [io.quarkus.arc.processor.BeanArchives] Failed to index com.sap.conn.jco.ext.DestinationDataProvider: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader
[INFO] [io.quarkus.arc.processor.IndexClassLookupUtils] Class for name: com.sap.conn.jco.ext.DestinationDataProvider was not found in Jandex index. Please ensure the class is part of the index.
and later the following error:
java.lang.NoClassDefFoundError: com/sap/conn/jco/ext/DestinationDataProvider
Is there a solution with Quarkus to deal with provided dependencies and copy them manually with maven?
I have the SAP library working with Quarkus in a project. The solution is not specific to Quarkus (the same solution is used by Spring Boot projects).
First, define the library with a system scome so that its name is not modified.
<dependency>
<groupId>com.sap</groupId>
<artifactId>sapjco3</artifactId>
<version>3.1</version>
<scope>system</scope>
<systemPath>${project.build.directory}/dependency/sapjco3.jar</systemPath>
</dependency>
Then, configure the maven-dependency-plugin to copy the library in this path in the initialize phase and to include it in the final package. It also contains configuration for the native lib used by the sapjco3.jar library.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<id>copy-jco-libs-unit-tests</id>
<phase>initialize</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<stripVersion>true</stripVersion>
<outputDirectory>${lib.directory}</outputDirectory>
<artifactItems>
<artifactItem>
<groupId>com.sap.conn.jco</groupId>
<artifactId>sapjco3</artifactId>
<version>${sap.jco.version}</version>
<overWrite>true</overWrite>
<destFileName>sapjco3.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<id>copy-native-lib-for-unit-tests</id>
<phase>process-sources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<stripVersion>true</stripVersion>
<outputDirectory>${native.lib.directory}</outputDirectory>
<artifactItems>
<artifactItem>
<groupId>com.sap.conn.jco</groupId>
<artifactId>sapjco3</artifactId>
<version>${sap.jco.version}</version>
<type>${envType}</type>
<classifier>${envClassifier}</classifier>
<overWrite>true</overWrite>
<destFileName>${native.lib.filename}.${envType}</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
Last, configure which native lib should be included thanks to profiles, one profile by OS/CPU arch is needed. Here is the configuration:
<profiles>
<!-- Manage JCO native deps by OS arch -->
<profile>
<id>windows-x86_64</id>
<activation>
<os>
<family>windows</family>
<arch>x86_64</arch>
</os>
</activation>
<properties>
<envClassifier>ntamd64</envClassifier>
<envType>dll</envType>
<native.lib.filename>sapjco3</native.lib.filename>
</properties>
</profile>
<profile>
<id>windows-amd64</id>
<activation>
<os>
<family>windows</family>
<arch>amd64</arch>
</os>
</activation>
<properties>
<envClassifier>ntamd64</envClassifier>
<envType>dll</envType>
<native.lib.filename>sapjco3</native.lib.filename>
</properties>
</profile>
<profile>
<id>linux-x86_64</id>
<activation>
<os>
<name>linux</name>
<arch>x86_64</arch>
</os>
</activation>
<properties>
<envClassifier>linuxx86_64</envClassifier>
<envType>so</envType>
<native.lib.filename>libsapjco3</native.lib.filename>
</properties>
</profile>
<profile>
<id>linux-amd64</id>
<activation>
<os>
<name>linux</name>
<arch>amd64</arch>
</os>
</activation>
<properties>
<envClassifier>linuxx86_64</envClassifier>
<envType>so</envType>
<native.lib.filename>libsapjco3</native.lib.filename>
</properties>
</profile>
<profile>
<id>macosx-x86_64</id>
<activation>
<os>
<name>mac os x</name>
<arch>x86_64</arch>
</os>
</activation>
<properties>
<envClassifier>darwinintel64</envClassifier>
<envType>dylib</envType>
<native.lib.filename>libsapjco3</native.lib.filename>
</properties>
</profile>
<profile>
<id>macosx-amd64</id>
<activation>
<os>
<name>mac os x</name>
<arch>amd64</arch>
</os>
</activation>
<properties>
<envClassifier>darwinintel64</envClassifier>
<envType>dylib</envType>
<native.lib.filename>libsapjco3</native.lib.filename>
</properties>
</profile>
With all this, test works, mvn quarkus:dev works, and a package done with mvn clean package works.
Be careful that the initialize phase of Maven is not launched by default, you need to call mvn initialize once for the library copy to happens.
We were struggling and working around with the naming-problem of the sapjco3.jar until I stumbled upon a footnote on this link: https://help.mulesoft.com/s/article/It-is-not-allowed-to-rename-or-repackage-the-original-archive-sapjco3-jar
Name your artifact com.sap.conn.jco.sapjco3.jar and it will solve the naming problem, as the driver accepts this name even with versions numbers added.
Your next stumbling block will then be the native library though ... I don't have a solution for that part in a quarkus environment, as I didn't try that yet.

How to skip integration tests by default but still run them on-demand in multi-module maven project?

I have a multi module project having the following structure:
Project:
- module1
- module2
- integration-test
- parent pom
What is the correct way of achieving the following:
run unit tests from all modules(except integration-test) using mvn clean install
run integration tests on demand(may be by using maven-failsafe plugin or via a maven profile? )
fail the build when integration tests fail.
By Default integration tests should not be run using mvn clean install
integration-test module has only the integration tests.
I have tried multiple hacks using maven-failsafe plugin and maven-sunfire-plugin(for unit tests) but not able to achieve the above in standard way.
Following is how the relevant portion of integration-test pom looks like:
<dependencies>
<!-- dependencies required for this module-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.12</version>
<executions>
<execution>
<id>add-integration-test-sources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>src/test/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-integration-test-resources</id>
<phase>generate-test-resources</phase>
<goals>
<goal>add-test-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/test/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>run-its</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
currently when i run mvn clean install it runs integration tests too. When i run mvn -Prun-its clean verify, it is running unit tests from other modules too. what am i missing?
You can skip execution of the Integration tests simply by setting -DskipITs=true when running your build like so:
mvn clean install -DskipITs=true
this will run all other tests but your ITs (see here for doc).
If you only want to run
mvn clean install
you can set the default for skipITs in your pom.xml
<properties>
<skipITs>true</skipITs>
</properties>
This way you can override it on demand with
mvn clean install -DskipITs=false
To run only ITs without Unittests you can configure the -Property of the maven-surefire-plugin like so
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skip>${skipUnitTests}</skip>
</configuration>
</plugin>
so if you run
mvn clean install -DskipITs=false -DskipUnitTests=true
Note that skipUnitTests will be false by default so no need to declare a property for that.
If you'd rather use a Profile it should work like that
<profile>
<id>ITs</id>
<properties>
<skipUnitTests>true</skipUnitTests>
<skipITs>false</skipITs>
</properties>
</profile>
and run the build like so
mvn clean install -PITs
Of course you could also use the plugin-configuration for maven-surefire-plugin with true directly in the profile so there'd be no need for the extra property, like
<profile>
<id>ITs</id>
<properties>
<skipITs>false</skipITs>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</profile>
I don't know if that's the best solution but you can achieve that using profiles. For example in the main pom.xml you add just the other modules in the <modules> section and then add another profile:
<modules>
... standard modules ...
</modules>
<profiles>
<profile>
<id>tests</id>
<modules>
<module>module1</module>
... standard modules repeated (it might not be needed>...
<module>module2</module>
<module>module-integration-test</module>
</modules>
</profile>
</profiles>
Then you run maven with that profile if you wnat to run tests.
maven -P tests clean install
That would work if you want to run integration tests AND the other modules. If you want to run just the integration tests you can do it like that:
<modules>
<!-- EMPTY -->
</modules>
<profiles>
<profile>
<id>defaultModule</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>module1</module>
... standard modules repeated (it might not be needed>...
<module>module2</module>
</modules>
</profile>
<profile>
<id>tests</id>
<modules>
<module>module-integration-test</module>
</modules>
</profile>
</profiles>
This way with mvn clean install you will run with defaultModule (which is activeByDefault) and if you specify -P tests you will run just tests
The best is to make a separate module which contains the integration tests which looks like already shown in your own question. Now how to handle the integration tests being running or not ...
The integration-test module looks like this:
<?xml version="1.0"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>groupOfYourParant</groupId>
<artifactId>integration-test</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>integration-test</artifactId>
<packaging>jar</packaging>
<name>Mod-IT</name>
<dependencies>
<dependency>
<groupId>groupId</groupId>
<artifactId>TheArtifact</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Supplemental deps only needed in this module -->
<dependency>
<groupId>....</groupId>
<artifactId>....</artifactId>
<version>...</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>run-its</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
By using this you can simply control to run the integration tests via:
mvn -Prun-its clean verify
It's important that you have define the dependencies to other modules which are needed by the integration tests module or where it made be sure that those modules have to be built before this integration test module.
Furthermore you can now configure here supplemental things which are needed for integration tests and which should be run for example in the pre-integration-test and/or in post-integration-test phase.
If you put the test code into src/test/java plus optional resources into src/test/resources you can now define dependencies within your integration test module separately from any other module.
You should of course define the versions for your plugins in a pluginManagement in your parent pom to define all plugins which are being used during a build.

Stacking properties by specifying multiple Maven profiles on the command line

I finally got my test automation running using JUnit4 #Category for each test; they are marked as either PriorityHigh, PriorityMedium, or PriorityLow.
In my pom.xml I have each set up as a profile:
<profile>
<id>PriorityHigh</id>
<properties>
<testcase.category>com.categories.PriorityHigh</testcase.category>
</properties>
</profile>
<profile>
<id>PriorityMedium</id>
<properties>
<testcase.category>com.categories.PriorityMedium</testcase.category>
</properties>
</profile>
<profile>
<id>PriorityLow</id>
<properties>
<testcase.category>com.categories.PriorityLow</testcase.category>
</properties>
</profile>
Which is then used in the plugin section:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<configuration>
<groups>${testcase.category}</groups>
<systemPropertyVariables>
<automation.driver>${browser.name}</automation.driver>
</systemPropertyVariables>
</configuration>
</plugin>
My issue is when I want to test both Medium and High, I specify
-P PriorityHigh,PriorityMedium
But instead of adding/concatenating, they overwrite and so only Medium tests run. To add extra difficulty, since pom.xml complains that ${testcase.category} only exists in profiles, and no default, I had to add this:
<testcase.category>com.categories.PriorityHigh,com.categories.PriorityMedium,com.categories.PriorityLow</testcase.category>
in case no profile is specified.
So two questions:
How to get the profiles to stack correctly in the "groups" node?
A better way to work it if no profile is specified (all tests should run)?
The easiest thing to do would be to ditch profiles and just use system properties:
<properties>
<testcase.category>com.categories.PriorityHigh,com.categories.PriorityLow</testcase.category>
</properties>
...
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<groups>${testcase.category}</groups>
...
</configuration>
</plugin>
Then:
mvn verify -Dtestcase.category=com.categories.PriorityHigh
# runs PriorityHigh tests
mvn verify -Dtestcase.category=com.categories.PriorityHigh,com.categories.PriorityLow
# runs PriorityHigh and PriorityLow tests
mvn verify
# runs PriorityHigh and PriorityLow tests
If you don't want to have to specify the fully qualified category class name on the Maven command line, you could use the Build Helper plugin to qualify the names for you:
<properties>
<testcase.category>PriorityHigh,PriorityLow</testcase.category>
</properties>
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>build-fq-testcase-category</id>
<goals>
<goal>regex-property</goal>
</goals>
<configuration>
<name>fq.testcase.category</name>
<regex>([^,]+)</regex>
<value>${testcase.category}</value>
<replacement>com.categories.$1</replacement>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<groups>${fq.testcase.category}</groups>
</configuration>
</plugin>
Then:
mvn verify -Dtestcase.category=PriorityHigh
# just run PriorityHigh tests
mvn verify
# run PriorityLow and PriorityHigh tests
# etc.

maven scm plugin migration from SVN to GIT

I had a maven project structure including following projects
server
client
node
This is my module structure in main pom
<modules>
<module>server</module>
<module>node</module>
<module>help</module>
<module>client</module>
</modules>
I defined an automatic jar creation mechanism that runs on server machine. It checkouts code for each module, compiles, builds, etc. In each module, I defined maven scm blug in for checking out latest code for related module (Server, client , node).
For example, this is pom.xml for server side.
<scm>
<connection>scm:svn:https://ip/svn/repositoryName/trunk/projectName/server/src</connection>
<developerConnection>scm:svn:https://ip/svn/repositoryName/trunk/projectName/server/src</developerConnection>
</scm>
And this is profile for refreshing
<profile>
<id>environment-refresh-sources</id>
<activation>
<property>
<name>refreshSources</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-plugin</artifactId>
<version>1.8.1</version>
<configuration>
<username>maven.user</username>
<password>password</password>
<checkoutDirectory>${basedir}/src</checkoutDirectory>
</configuration>
<executions>
<execution>
<id>CheckoutFromSVN</id>
<phase>initialize</phase>
<goals>
<goal>checkout</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
We have this configuration for each module. Just scm connection is changing. For example: this is for client.
<connection>scm:svn:https://ip/svn/repositoryName/trunk/projectName/client/src</connection>
Now I am trying to migrate from SVN to GIT. But I am not able to configure maven plug in to work in this way.
This is my scm configuration for GIT.
<scm>
<connection>scm:git:http://maven.user#ip/scm/sig/project.git</connection>
<developerConnection>scm:git:http://maven.user#ip/scm/sig/project.git</developerConnection>
</scm>
This is my new plugin for git. I have added branch part. Now it checkouts master branch including server, client etc. modules. How can I work in same way with GIT.
<artifactId>maven-scm-plugin</artifactId>
<version>1.8.1</version>
<configuration>
<connectionType>developerConnection</connectionType>
<scmVersion>master</scmVersion>
<scmVersionType>branch</scmVersionType>
<username>maven.user</username>
<password>password</password>
<checkoutDirectory>${basedir}/src</checkoutDirectory>
</configuration>
<executions>
<execution>
<id>CheckoutFromGIT</id>
<phase>initialize</phase>
<goals>
<goal>checkout</goal>
</goals>
</execution>
</executions>

How to distinguish between different wsdl locations in one pom.xml - Maven

Is there a possibility to distinguish between multiple configurations of wsdl webservices in maven?
I have one application which can run on test, stage and prod environments. And I have to use one webservice. The webservice has 3 different wsdl locations. For test, stage and prod.
Is there a way in maven to say if I want to build my application for prod just use the webservice location for prod. And the same also for stage and test?
I have a wsdl import configuration which works fine for a single non-dynamical part.
<plugin>
<groupId>org.jvnet.jax-ws-commons</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<goals>
<goal>wsimport</goal>
</goals>
<configuration>
<wsdlFiles>
<wsdlFile>wsdlFile_live.wsdl</wsdlFile>
</wsdlFiles>
<vmArgs>
<vmArg>-Djavax.xml.accessExternalDTD=all</vmArg>
<vmArg>-Djavax.xml.accessExternalSchema=all</vmArg>
</vmArgs>
<packageName>com.example.schema</packageName>
<wsdlLocation>http://liveLocation/?wsdl</wsdlLocation>
</configuration>
<id>wsimport-generate-_live.wsdl</id>
<phase>generate-sources</phase>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>webservices-api</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
<configuration>
<sourceDestDir>${project.build.directory}/generated-sources/jaxws-wsimport</sourceDestDir>
<xnocompile>true</xnocompile>
<verbose>true</verbose>
<extension>true</extension>
<catalog>${basedir}/src/jax-ws-catalog.xml</catalog>
</configuration>
Creating profiles in maven is one possibility to build different applications with different scopes.
<profiles>
<profile>
<id>prod</id>
<build>
....
</build>
</profile>
<profile>
<id>test</id>
<build>
....
</build>
</profile>
</profiles>
In the profile property you can set dependencies, resourves, plugins, configurations and so on.
To build a specific profile you have to type mvn -P followed by the profile ID
In my case it looks like this: mvn -Ptest clean install or mvn -Pprod clean install
You can use environment variables to store your wsdl file and pass to maven system properties. For example, say on Linux you want to access environment variable MY_VARIABLE. You can use a system property in your pom file.
<properties>
...
<!-- Default value for my.variable can be defined here -->
<my.variable>foo</my.variable>
...
...
${my.variable}
Set the property value on the maven command line:
mvn clean package -Dmy.variable=$MY_VARIABLE

Categories