8/26/2013

Maven assemble package and distribution

For example we have one project called "performance-test". And it has several modules, "shared", "user-creation", "performance" like below:


The parent pom.xml has modules:
<groupId>com.test</groupId>
<artifactId>performance-test</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>

<name>performance-test</name>

<modules>
    <module>shared</module>
    <module>user-creation</module>
    <module>performance</module>
</modules>
While the modules is like:
<parent>
    <groupId>com.test</groupId>
    <artifactId>performance-test</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath>
</parent>

<artifactId>shared</artifactId>
<name>shared</name>


And the user-creation and performance module has several main classes that used to be separately executed to do the performance test.

First, for module "user-creation" and "performance", we need to package all dependencies into one executable jar file.(Simple way. We can also pick another way by copying all the dependencies to later lib folder and configure the classpath.)

Simply by configuring this plugin in pom.xml of "user-creation" and "performance":

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-assembly-plugin</artifactId>
 <version>2.4</version>
 <configuration>
  <appendAssemblyId>true</appendAssemblyId>
 </configuration>
 <executions>
  <execution>
   <id>assemblyJar</id>
   <phase>package</phase>
   <goals>
    <goal>single</goal>
   </goals>
   <configuration>
    <descriptorRefs>
     <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
   </configuration>
  </execution>
 </executions>
</plugin>

In this way, we can get the executable jar files.

Extension:

When we have multiple modules and each module may have its own resources.We may want to put all the resources into one place that could make the property management easier and clearer.

For example, we may put the central resource folder under the parent project:


To support structure like this, we need one environment variable, for example, "resource.path" to tell the program where to find the central resource folder.

When we make the deployment, we can put the resources folder anywhere we want, as long as we define the variable.

We can define the variable in command line before we run the code.

-Dresource.path="C:/SOME_PATH"

Also, we can add it to system variable.

Extension:

To make the execution of jar easy, we can also write some bat files and put them into folder "bin" and put the folder under parent project, like below:
With this structure, after we make the build, we may want to get one instance folder which has structure like:

"bin": holds all the bat file.

"lib": holds all the jars.

"config": holds all the resources.

"log": holds all the log files, runtime generated.

"records": holds all the records generated by the code.
...

If all the code are put under one project instead of modules, we can easily get this by using the maven-assembly-plugin, configure it in the pom file of the project.

But when we have multiple modules and in the mean time, need to assemble libraries from different modules, we need to create another module "distribution" to access all the other sub-modules and assemble the instance folder.

The new structure of the project will be like:

The parent pom.xml will have modules:
<groupId>com.test</groupId>
<artifactId>performance-test</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>

<name>performance-test</name>

<modules>
    <module>shared</module>
    <module>user-creation</module>
    <module>performance</module>
    <module>distribution</module>
</modules>

Because the parent pom is executed before all the modules' pom and the modules are built in sequence. So we have to define another module to make the final assemble.

The pom of the distribution will be like:

<parent>
 <groupId>com.test</groupId>
 <artifactId>performance-test</artifactId>
 <version>1.0.0-SNAPSHOT</version>
 <relativePath>../pom.xml</relativePath>
</parent>

<artifactId>distribution</artifactId>
<packaging>pom</packaging>
<name>distribution</name>

<url>http://maven.apache.org</url>
<properties>
 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
 <dependency>
  <groupId>com.test</groupId>
  <artifactId>user-creation</artifactId>
  <version>${project.version}</version>
 </dependency>
 <dependency>
  <groupId>com.test</groupId>
  <artifactId>performance</artifactId>
  <version>${project.version}</version>
 </dependency>
</dependencies>
<build>
 <directory>${basedir}/../instance</directory>
 <plugins>
  <plugin>
   <artifactId>maven-assembly-plugin</artifactId>
   <version>2.4</version>
   <executions>
    <execution>
     <id>distri-assembly</id>
     <phase>package</phase>
     <goals>
      <goal>single</goal>
     </goals>
     <configuration>
      <descriptors>
       <descriptor>${basedir}/../assemble/distribution-jar.xml</descriptor>
      </descriptors>
     </configuration>
    </execution>
   </executions>
  </plugin>
 </plugins>
</build>
We can see that we change the output directory of "distribution" to "%PARENT_PROJECT%/instance" by specify:
<directory>${basedir}/../instance</directory>

In this way, we will get the instance folder under the root of the parent project. This does not matter actually, we can also just leave it generated under target.

The distribution-jar.xml should be like:
<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>bin</id>
 <formats>
  <format>dir</format>
  <format>zip</format>
 </formats>
 <includeBaseDirectory>false</includeBaseDirectory>
 <moduleSets>
  <moduleSet>

   <!-- Enable access to all projects in the current multimodule build! -->
   <useAllReactorProjects>true</useAllReactorProjects>

   <!-- Now, select which projects to include in this module-set. -->
   <includes>
    <include>${project.groupId}:user-creation</include>
    <include>${project.groupId}:performance</include>
   </includes>

   <binaries>
    <attachmentClassifier>jar-with-dependencies</attachmentClassifier>
    <outputDirectory>${basedir}/../lib</outputDirectory>
    <unpack>false</unpack>
   </binaries>

  </moduleSet>
 </moduleSets>
 <fileSets>
  <fileSet>
   <directory>${basedir}/../bin</directory>
   <outputDirectory>bin</outputDirectory>
   <filtered>true</filtered>
  </fileSet>
  <fileSet>
   <directory>${project.build.directory}/${project.artifactId}-${project.version}-bin/lib</directory>
   <outputDirectory>lib</outputDirectory>
  </fileSet>
  <fileSet>
   <directory>${basedir}/../resources</directory>
   <excludes>
    <exclude>keystore/*</exclude>
   </excludes>
   <outputDirectory>config</outputDirectory>
   <filtered>true</filtered>
  </fileSet>
  <fileSet>
   <directory>${basedir}/../resources/keystore</directory>
   <outputDirectory>config/keystore</outputDirectory>
   <filtered>false</filtered>
   <lineEnding>keep</lineEnding>
  </fileSet>
 </fileSets>
</assembly>

We can also add some small bat help file to project, so the final structure will be like:


QuickStart.bat:
mvn clean install -Dmaven.test.skip=true
pause
upgradeVersion.bat:
mvn versions:set -DnewVersion=%1

No comments:

Post a Comment