Maven building for different environments - java

I have a Java project that currently builds nicely with Maven. But, I'm using an Ant script to do other tasks that I think Maven could also do. To simplify the build workflow, I'd like to get Maven doing everything I need. All this is automated with Jenkins.
The challenge is that I want Maven to build a single artifact and deploy it to multiple environments on-demand via command line. Currently, I do this by building with Maven, then using an Ant script that will create a copy of the WAR artifact with some files changed depending on the environment I chose. This adapts for things like JDBC connection strings, properties files, etc. that are different in each environment.
I understand Maven can use profiles, as explained in this answer. However, I'm stuck on figuring out how to make a secondary WAR file for the environment. e.g. I start with "myproject.war" created from the build step, and I want to create "myproject-dev.war", then deploy it to the dev Tomcat server. I figure I can use the antrun maven plugin to make the file changes, but I don't know how to hook it into the lifecycle. Then, I'd need to use the tomcat plugin to deploy.
Any suggestions on how this could be configured?

Let us assume you have a structure like the following (just as an example)
.
|-- pom.xml
`-- src
|-- main
| |-- java
| |-- resources
| |-- environment
| | |-- test
| | | `-- database.properties
| | |-- qa
| | | `-- database.properties
| | `-- production
| | `-- database.properties
| `-- webapp
You can define a assembly descriptor like the following:
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>test</id>
<formats>
<format>war</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<unpack>true</unpack>
<useProjectArtifact>true</useProjectArtifact>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<outputDirectory>WEB-INF</outputDirectory>
<directory>${basedir}/src/main/environment/test/</directory>
<includes>
<include>**</include>
</includes>
</fileSet>
</fileSets>
</assembly>
Furthermore you need a maven-assembly-plugin configuration like the following:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>test</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>${project.basedir}/src/main/assembly/test.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</project>
If you add a separate assembly-descriptor for each environment and add a line with the descriptor you can create with a single call the packages for each environment.
One drawback of this you have several assembly-descriptors which are aside from a few lines identical.
So going more you can use the iterator-maven-plugin to reduce the configuration and mainenance hassle like this:
<plugin>
<groupId>com.soebes.maven.plugins</groupId>
<artifactId>iterator-maven-plugin</artifactId>
<version>0.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>iterator</goal>
</goals>
<configuration>
<items>
<item>test</item>
<item>prod</item>
<item>dev</item>
</items>
<pluginExecutors>
<pluginExecutor>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
</plugin>
<goal>single</goal>
<configuration>
<descriptors>
<descriptor>src/assembly/iterator.xml</descriptor>
</descriptors>
</configuration>
</pluginExecutor>
</pluginExecutors>
</configuration>
</execution>
</executions>
</plugin>
So furthermore you need an appropriate assembly descriptor like this:
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>${item}</id>
<formats>
<format>war</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<unpack>true</unpack>
<useProjectArtifact>true</useProjectArtifact>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<outputDirectory>WEB-INF</outputDirectory>
<directory>${basedir}/src/main/environment/${item}/</directory>
<includes>
<include>**</include>
</includes>
</fileSet>
<fileSet>
<outputDirectory>WEB-INF</outputDirectory>
<directory>${project.build.directory}/environment/${item}/</directory>
<includes>
<include>**</include>
</includes>
</fileSet>
</fileSets>
</assembly>
This makes it simple and convenient to maintain such a build. A full working example can looked at the integration tests of iterator-maven-plugin.

Related

Maven: How to extract specific files from a dependency and include them in your own project's generated jar?

I have a project that needs specific xml files contained in a dependency to be included in the root of the jar for my own project.
To better illustrate. MyProject has a dependency DependencyA.jar with the following structure:
DependencyA.jar
|
+-- folder1
| |
| +-- file11.xml
| |
| +-- file12.xml
|
+-- folder2
| |
| +-- file21.xml
|
+-- Other contents
I'm trying to generate MyProject.jar so that it has the following structure
MyProject.jar
|
+-- file11.xml
|
+-- file12.xml
|
+-- file21.xml
|
+-- Other contents
I'm not aware of any maven plugin I can use to create this structure. My idea is to hook to the process-resources phase of maven, extract the xml files from the DependencyA.jar, and copy them to ${project.build.directory}/classes so that it's packaged in the root of the MyProject.jar. Not sure if that would work or what plugin I can use to do the extraction.
I was able to get the desired behavior with the maven dependency plugin's unpack goal:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>unpack</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.example</groupId>
<artifactId>DependencyA</artifactId>
<version>1.0</version>
<includes>
folder1/*.xml,
folder2/*.xml
</includes>
<fileMappers>
<fileMapper implementation="org.codehaus.plexus.components.io.filemappers.FlattenFileMapper"/>
</fileMappers>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
The FlattenFileMapper took care of removing the folder structure and placing all the xml files in the root of my project's jar file.
One way to achieve this would be to use the Maven Assembly Plugin to create a custom assembly that includes the XML files from the DependencyA.jar in the root of your MyProject.jar. The assembly plugin allows you to specify the files you want to include in the assembly, and where they should be placed in the final JAR.
You can add the following to your MyProject's pom.xml to configure the assembly plugin:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptors>
<descriptor>src/main/assembly/xml-files.xml</descriptor>
</descriptors>
</configuration>
</plugin>
</plugins>
</build>
Then create a file called xml-files.xml in the src/main/assembly directory with the following contents:
<assembly>
<formats>
<format>jar</format>
</formats>
<fileSets>
<fileSet>
<directory>path/to/dependencyA/folder1</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
<fileSet>
<directory>path/to/dependencyA/folder2</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>
This will create a custom assembly that includes the files in the folder1 and folder2 of the DependencyA.jar in the root of your MyProject.jar.
You can run the following command to create the jar with the custom assembly:
mvn clean package
The jar file will be in the target folder.

How to add artifacts from a submodule build directory?

I have the following project structure:
project-root
|
|__lib
| |
| |__target
| | |____lib.jar
| | |____lib.so
| |___src
| | |____ ...
| |
| |_pom.xml
|
|___model
| |
| |_pom.xml
| |
| ...
|
|__pom.xml
So I have 2-modules project: lib and model where model produces the tar.gz distribution using maven-assembly-plugin. The thing is the building of lib causes to produce native library lib.so which needs to be copied into the tar.gz in order to be used.
How to make maven-assembly-plugin to include artifact from one sub-module's build directory into the dirstribution (e.g. tar.gz in my case)
In your assembly descriptor file, You can add lib.so file to your tar package. (I am assuming you already have a descriptor for maven assembly to create tar.gz package)
I am modifying default jar-with-dependencies descriptor here for an example.
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>custom-descriptor</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>/</outputDirectory>
<useProjectArtifact>true</useProjectArtifact>
<unpack>true</unpack>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
<!-- You need to add following to your current descriptor -->
<fileSets>
<fileSet>
<directory>${project.build.directory}/../../lib/target</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*.so</include>
</includes>
</fileSet>
</fileSets>
</assembly>
We need to give relative path to directory section. (I don't know any other way for non published files) OutputDirectory determines in which folder the file will be copied in the created archive. (You can leave it as "/" If you want it in the root of the archive.
When using assembly plugin( You probably have it like this)
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptors>
<!-- descriptor file previously created. -->
<descriptor>custom-descriptor.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
.....
</build>
In this case custom-descriptor.xml file is next to pom.xml file. If its placed somewhere else, relative path should be given in the descriptor tags.
Ps: This will work if lib is built before model.

Maven Assembly Plugin - Include classes from another module

I have a multimodule project and am trying to generate an assembled file in SubProject2 that includes some of its classes plus some DTOs defined in SubProject1. However, Proc.jar file only has Proc.class and com\myproy\spr\dto*.class, whereas SubProject1's files (com\myproy\dto*.class) are not included and cannot come up with a way to make it work.
Projects' layout:
Parent
|-pom.xml
|
SubProject1
|-pom.xml
|-src\main\java\com\myproy\dto
|-src\main\java\com\myproy\util
|
SubProject2
|-pom.xml
|-src\assembly\def.xml
|-src\main\java\com\myproy\spr
|-src\main\java\com\myproy\spr\dto
def.xml:
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0
http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>Proc</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>target/classes</directory>
<includes>
<include>com/myproy/spr/Proc.class</include>
<include>com/myproy/spr/dto/*.class</include>
<include>com/myproy/dto/*.class</include>
</includes>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>
SubProject2->pom.xml
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4.1</version>
<executions>
<execution>
<id>distro-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/assembly/def.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
Correct me if I understand you incorrectly. You want some specific functionality which is in the SubProject1. And you want only this functionality (not more) and you can not include the whole SubProject1 as dependency.
So maybe, if I understand it correctly, you can extract this functionality in another project (something like Commons), and add it as dependency in SubProject1 and SubProject2.
Why don't add the project as a jar dependency ?¿.

multiple src/main/resources folders without profiles (I wish to build multiple versions at once)

I'm working on trying to have my project automatically build two different versions of a project. What I have done is I have added two different plug-in executions as follows
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>exploded</goal>
</goals>
</execution>
</executions>
<configuration>
<resources>
<resource>
<directory>src/main/resources/first</directory>
</resource>
</resources>
<webappDirectory>${webappDirectory}</webappDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>war</goal>
</goals>
</execution>
</executions>
<configuration>
<resources>
<resource>
<directory>src/main/resources/second</directory>
</resource>
</resources>
<webappDirectory>${project.build.directory}/deployment</webappDirectory>
</configuration>
</plugin>
It builds both webappDirectories and it created the war file as expected, but the resources doesn't get included correctly (it seems to ignore the resource definitions above).
I am guessing its because I am doing this in the wrong phase, at what phase do I need to override the default resources directory?
(please notice, I do not wish to have two different profiles, I want to have both done at once)
I have wrote an blog entry exactly about that problem but the essence is about to have the maven project (in your case the war module) with the following structure:
.
|-- pom.xml
`-- src
|-- main
| |-- java
| |-- resources
| |-- environment
| | |-- test
| | | `-- database.properties
| | |-- qa
| | | `-- database.properties
| | `-- production
| | `-- database.properties
| `-- webapp
The most important thing is to create the different configuration via the maven-assembly-plugin with a configuration like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>test</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>${project.basedir}/src/main/assembly/test.xml</descriptor>
</descriptors>
</configuration>
</execution>
<execution>
<id>qa</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>${project.basedir}/src/main/assembly/qa.xml</descriptor>
</descriptors>
</configuration>
</execution>
<execution>
<id>production</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>${project.basedir}/src/main/assembly/production.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
what you also need are assembly descriptors like the following:
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>test</id>
<formats>
<format>war</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<unpack>true</unpack>
<useProjectArtifact>true</useProjectArtifact>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<outputDirectory>WEB-INF</outputDirectory>
<directory>${basedir}/src/main/environment/test/</directory>
<includes>
<include>**</include>
</includes>
</fileSet>
</fileSets>
</assembly>
Of course you have to name them accordingly and obviously you have to change the directory within the descriptor as well. Based on that it is now possible to create multiple artifacts (in your case war files) with different configurations during the package cycle. So you simply call your project build via:
mvn clean package
Finally i recommend not to use the src/main/resources folder, cause that folder should be used for production information which does not change for the different configuration. Furthermore the src/test/resources will be used for the test resources.

Packaging a jar into a dist dir with separated external resources and dependencies

Here's what I'm trying to achieve - a dist directory (or a zip file) that looks like this:
dist/
|-- application-1.0.jar
|-- conf/
|-- application.properties
|-- log4j.properties
|-- lib/
|-- *.jar
Basically:
An executable jar is produced (with appropriate classpath in the manifest)
I want to exclude src/main/resources from being automatically packaged with the jar, so that application.properties can be modified
I want to have external dependencies in the lib/ directory
I came up with a solution using a profile with plugins attached to the package phase, but would using the assembly plugin be a better solution?
The solution using the assembly plugin has a few parts:
The pom includes configuring the jar plugin (maven-jar-plugin), and configuring the assembly plugin (maven-assembly-plugin).
During maven's packaging phase, the jar plugin is called to construct the application jar.
Then the assembly plugin is run, and combines the constructed jar, resources and dependencies into a zip file as defined by the assembly file (distribution-zip.xml).
In the pom, configure the plugins:
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.2</version>
<configuration>
<archive>
<!-- Make an executable jar, adjust classpath entries-->
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>./lib/</classpathPrefix>
<mainClass>com.acme.KillerApp</mainClass>
</manifest>
<!--Resources will be placed under conf/-->
<manifestEntries>
<Class-Path>./conf/</Class-Path>
</manifestEntries>
</archive>
<!--exclude the properties file from the archive-->
<excludes>
<exclude>*.properties</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<configuration>
<descriptors>
<descriptor>${basedir}/assembly/distribution-zip.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
...
The contents of the assembly file distribution-zip.xml (with thanks to Neeme Praks) combines the created jar, resources and dependencies:
<assembly>
<id>dist</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<dependencySets>
<dependencySet>
<!--Include runtime dependencies-->
<outputDirectory>lib</outputDirectory>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<!--Get the generated application jar-->
<directory>${project.build.directory}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<!--Get application resources-->
<directory>src/main/resources</directory>
<outputDirectory>conf</outputDirectory>
</fileSet>
<fileSet>
<!--Get misc user files-->
<directory>${project.basedir}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>README*</include>
<include>LICENSE*</include>
<include>NOTICE*</include>
</includes>
</fileSet>
</fileSets>
</assembly>
The resulting distributable zip file is created like target/killer-app-1.0-dist.zip!
You need to use two plugins to accomplish this: maven-jar-plugin and maven-assembly-plugin.
Useful pom.xml samples:
how make JAR executable and set manifest classpath
how to exclude files from JAR file
assembly plugin usage
(I would recommend you to separate the user-editable properties files to separate directory, but it is a matter of taste.)
Sample assembly configuration, to get you started:
<assembly>
<id>dist</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<baseDirectory>dist</baseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
<fileSets>
<fileSet>
<directory>src/conf</directory>
<outputDirectory>conf</outputDirectory>
</fileSet>
<fileSet>
<directory>src/run</directory>
<outputDirectory></outputDirectory>
<excludes>
<exclude>*.sh</exclude>
</excludes>
</fileSet>
</fileSets>
<files>
<file>
<source>src/run/run.sh</source>
<outputDirectory></outputDirectory>
<fileMode>0755</fileMode>
</file>
</files>
</assembly>

Categories

Resources