Sunday, June 22, 2014

How to externalize property files of dependent libraries using maven and package it in a tar file alongside jar file

I was working on this module where the environment specific property files were packaged inside the jar files. As usual, it made me itchy to have to do a new build for deployment in each environment, where the only change the build would contain will be property values for new environment.

So the goal here was to externalize the property file and create a tar package for the build so that the tar can be moved from one environment to another environment and the build can be deployed simply by modifying the values in the externalized property file.

This is essential to manage versioning as well! To me, a new build is a new version even if it is just a change in property file as it opens up risk of some code change slipping in!

 

Now, the challenge in externalizing property files was that not just the main artifact of current project contained property files but also a dependency contained an environment specific property file.

So following were the major challenges:

-          Include the dependency without property file (a jar without property file)

-          Get the property file of dependency and copy it in the tar file built by current project

-          Build current project’s artifact (a war file) without property file and include the property file in tar file, alongside the property file of dependency

To add to this, there were other projects using the dependency’s original jar file including property file; so I didn’t want to disrupt the original jar file.

 

Following is the project structure:

 

Main-Project:

-          src/main/java

o   <Java Classes>

-          src/main/resources

o   main-project.properties

 

Dep-project: (dependency-project)

-          src/main/java

o   <Java Classes>

-          src/main/resources

o   dep-project.properties

 

Following are the steps performed to achieve the goal:

1.       Make changes in pom.xml of ‘dep-project’ to perform following actions:

a)      Create a new ext-jar file without property file [Leave original jar (with property file) AS IS]

b)      Create a resource-jar file containing only property files

2.       Make changes in pom.xml of ‘main-project’ to perform following actions:

a)      Include the new jar ext-jar as dependency instead of original dependency jar

b)      Add new resource-jar as dependency with scope ‘provided’

c)       Create new ext-war file without properties files. (keep existing war file as it is)

d)      Extract resource-jar and copy property file of dependency to the tar file.

e)      Use assembly plugin to create tar file

 

 

Now let’s look at the pom.xml changes:

 

Changes in pom.xml for each of the steps mentioned above:

 

Step-1a: Create a new ext-jar file without property file [Leave original jar (with property file) AS IS]

file: dep-project/pom.xml

                     <plugin>

                           <groupId>org.apache.maven.plugins</groupId>

                           <artifactId>maven-jar-plugin</artifactId>

                           <version>2.3.1</version>

                           <executions>

                                  <execution>

                                         <id>ext</id>                            

                                         <goals>

                                                <goal>jar</goal>

                                         </goals>

                                         <phase>package</phase>                                

                                         <configuration>

                                                <classifier>ext</classifier>

                                                <excludes>

                                                       <exclude>src/main/resources/*</exclude>

                                                       <exclude>**/dep-project.properties</exclude>

                                                </excludes>

                                         </configuration>

                                  </execution>

                           </executions>

                     </plugin>

 

Step-1b: Create a resource-jar file containing only property files

file: dep-project/pom.xml

              <plugin>

                <groupId>org.apache.maven.plugins</groupId>

                <artifactId>maven-assembly-plugin</artifactId>

                <executions>

                    <execution>

                        <id>make shared resources</id>

                        <goals>

                            <goal>single</goal>

                        </goals>

                        <phase>package</phase>

                        <configuration>

                            <descriptors>

                                <descriptor>src/main/assembly/resources.xml</descriptor>

                            </descriptors>

                        </configuration>

                    </execution>

                </executions>

            </plugin>

New file: src/main/assembly/resources.xml

<assembly>

  <id>resources</id>

  <formats>

    <format>jar</format>

  </formats>

  <includeBaseDirectory>false</includeBaseDirectory>

  <fileSets>

    <fileSet>

      <directory>src/main/resources</directory>

      <outputDirectory>resources</outputDirectory>

    </fileSet>

    <!-- include profile files if applicable -->

    <fileSet>

      <directory>profiles</directory>

      <outputDirectory>profiles</outputDirectory>

    </fileSet>

  </fileSets>

</assembly>

 

Step-2a: Include the new jar ext-jar as dependency instead of original dependency jar

               file: main-project/pom.xml

        <dependency>

            <groupId>{AS IS}</groupId>

            <artifactId>dep-project</artifactId>

            <classifier>ext</classifier> รจ added classifier based on step-1a

            <version>{AS IS}</version>

        </dependency>

 

 

Step-2b: Add new resource-jar as dependency with scope ‘provided’

file: main-project/pom.xml

              <dependency>

                     <groupId>{AS IS}</groupId>

                     <artifactId>dep-project</artifactId>

                     <classifier>resources</classifier>

                     <type>jar</type>

                     <version>{AS IS}</version>

                     <!-- Make sure this isn't included on the classpath-->

                     <scope>provided</scope>

              </dependency>

 

Step-2c: Create new ext-war file without properties files. (keep existing war file as it is)

                     <plugin>

                           <groupId>org.apache.maven.plugins</groupId>

                           <artifactId>maven-war-plugin</artifactId>

                           <executions>

                                  <execution>

                                         <id>externalized</id>                                 

                                         <goals>

                                                <goal>war</goal>

                                         </goals>

                                         <phase>package</phase>                                

                                         <configuration>

                                                <packagingExcludes>src/main/resources, **/dep-project.properties, **/main-project.properties </packagingExcludes>

                                                <warName>${final.war.name}</warName>

                                                <classifier>ext</classifier>

                                         </configuration>

                                  </execution>

                           </executions>

                     </plugin>

 

Step-2d: Extract resource-jar and copy property file of dependency to the tar file.

                     <!-- unpack jars with dependency resources -->

                     <plugin>

                      <groupId>org.apache.maven.plugins</groupId>

                      <artifactId>maven-dependency-plugin</artifactId>

                      <executions>

                        <execution>

                          <id>unpack-dep-resources</id>

                          <goals>

                                   <goal>unpack-dependencies</goal>

                          </goals>

                          <phase>generate-resources</phase>

                          <configuration>

                                 <outputDirectory>${project.build.directory}/dependency-resources</outputDirectory>

                                <includeGroupIds>{AS IS}</includeGroupIds>

                                 <includeArtifacIds>dep-project</includeArtifacIds>

                                 <includeClassifiers>resources</includeClassifiers>

                                 <includeScope>provided</includeScope>

                                 <excludeTransitive>true</excludeTransitive>

                          </configuration>

                        </execution>

                      </executions>

                  </plugin>

 

Step-2e: Use assembly plugin to create tar file

                <plugin>

                  <groupId>org.apache.maven.plugins</groupId>

                  <artifactId>maven-assembly-plugin</artifactId>

                  <version>2.2-beta-5</version>

                  <executions>

                    <execution>

                      <phase>package</phase>

                           <goals>

                                  <goal>attached</goal>

                           </goals>

                    </execution>

                  </executions>

                  <configuration>

                    <descriptors>

                      <descriptor>src/main/assembly/binary-deployment.xml</descriptor>

                    </descriptors>

                  </configuration>

                </plugin>

File: src/main/assembly/binary-deployment.xml

<assembly>

  <id></id>

  <formats>

    <format>tar.gz</format>

  </formats>

  <includeBaseDirectory>true</includeBaseDirectory>

  <fileSets>

    <fileSet>

      <directory>${project.build.directory}/classes</directory>

      <outputDirectory></outputDirectory>

      <includes>

        <include>main-project.properties</include>

        <!-- Include dependency resources -->

        <include>dep-project.properties</include>

      </includes>

    </fileSet>

       <fileSet>

              <directory>${project.build.directory}</directory>

              <outputDirectory />

              <includes>

                     <include>${final.war.name}-ext.war</include>

              </includes>

       </fileSet>

  </fileSets>

</assembly>

 

Regards,

Sarang