Create a simple app with user-accessible config files using Maven - maven-2

I need to produce a simple app for my customer configure and run at their site. I am using the Spring framework, so I have a number of config files that must be on the class path. I am using Maven2 with Netbeans as my IDE.
I am able to create and run my app using Netbeans/Maven and I am using the Application Assembler Maven plugin to generate the runnable application. All this works fine except that my Spring config files have to be placed in src/main/resources which means that they get packaged into the resulting JAR file.
I need my customer to be able to modify the config files to do their testing, but it's not reasonable to ask them to modify the copies that are packaged in the JAR.
There are perhaps a number of solutions, but it seems to me that the simplest would be to get Maven to not package the app and the config files into a JAR at all, just leaving them in something like a classes directory from which they can be run. This would allow the user to modify the config files easily. Unfortunately I can't figure out how to get Maven to "package" the app in this manner, or how to get the AppAssembler to generate the resulting runnable.
Here is an extract of my pom.xml that may help illustrate what I am trying to do:
...
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>
... stuff deleted ...
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.2</version>
<configuration>
<!-- Set the target configuration directory to be used in the bin scripts -->
<configurationDirectory>conf</configurationDirectory>
<!-- Copy the contents from "/src/main/config" to the target
configuration directory in the assembled application -->
<copyConfigurationDirectory>true</copyConfigurationDirectory>
<!-- Include the target configuration directory in the beginning of
the classpath declaration in the bin scripts -->
<includeConfigurationDirectoryInClasspath>
true
</includeConfigurationDirectoryInClasspath>
<platforms>
<platform>windows</platform>
</platforms>
<programs>
<program>
<mainClass>org.my.path.App</mainClass>
<name>app</name>
</program>
</programs>
</configuration>
</plugin>
</plugins>
</build>
...

Neither single packed jar file or bunch of unpacked classes files are good format for professional client delivery. Look at those brilliant apache apps like tomcat, ant and maven, they are shipped as a tar.gz or zip file, after download, simply extract them and you will get a nice and clean directory structure:
conf --> put config file like *.properties, logback.xml here
doc --> readme.txt, userguide.doc etc
lib --> put you core.jar with dependency jar file here
run.bat --> run script for Windows
run.sh --> run script for Unix
We can do these kinds of stuff with Maven as well. Note that you should design and implement your core jar to read *.properties from the conf directory properly. then use maven-assembly-plugin pack you app into this classical directory structure.
Sample pom.xml for a command-line app:
<!-- Pack executable jar, dependencies and other resource into tar.gz -->
<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>
Sample binary-deployment.xml for a command-line app:
<!--
release package directory structure:
*.tar.gz
conf
*.xml
*.properties
lib
application jar
third party jar dependencies
run.sh
run.bat
-->
<assembly>
<id>bin</id>
<formats>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/java</directory>
<outputDirectory>conf</outputDirectory>
<includes>
<include>*.xml</include>
<include>*.properties</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/bin</directory>
<outputDirectory></outputDirectory>
<filtered>true</filtered>
<fileMode>755</fileMode>
</fileSet>
<fileSet>
<directory>src/main/doc</directory>
<outputDirectory>doc</outputDirectory>
<filtered>true</filtered>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
<useProjectArtifact>true</useProjectArtifact>
<unpack>false</unpack>
<scope>runtime</scope>
</dependencySet>
</dependencySets>
</assembly>

If not misleading, I think you want to let the jar and config to be separated, with jar exposed for client's testing.
The following can do this for you using copy-maven-plugin, it can accomplish almost tasks what assembly-plugin would do ,ex: copy, dependency and much more - download, upload ,move ,... .
<plugin>
<groupId>com.github.goldin</groupId>
<artifactId>copy-maven-plugin</artifactId>
<version>0.2.5</version>
<executions>
<execution>
<id>create-archive</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
</execution>
</executions>
<configuration>
<resources>
<!--copy your scripts to ${myOutPutPath}/bin-->
<resource>
<targetPath>${myOutPutPath}/bin</targetPath>
<directory>${project.basedir}/src/main/scripts</directory>
<includes>
<include>*</include>
</includes>
</resource>
<resource>
<!--copy your configs-->
<targetPath>${myOutPutPath}/conf</targetPath>
<directory>${project.basedir}/src/main/config</directory>
<include>*</include>
</resource>
</resources>
</configuration>
</plugin>
Package main jar and put to your ${myOutPutPath}
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<!-- The configuration of the plugin -->
<configuration>
<outputDirectory>${myOutPutPath}</outputDirectory>
<!-- Configuration of the archiver -->
<archive>
<!-- Manifest specific configuration -->
<manifest>
<!-- Classpath is added to the manifest of the created jar file. -->
<addClasspath>true</addClasspath>
<!--
Configures the classpath prefix. This configuration option is
used to specify that all needed libraries are found under lib/
directory.
-->
<classpathPrefix>lib/</classpathPrefix>
<!-- Specifies the main class of the application -->
<mainClass>com.xinguard.snmp.SNMP_ETL</mainClass>
</manifest>
<!-- you need to add some classpath by yourself, like conf here for client to use-->
<manifestEntries>
<Class-Path>conf/</Class-Path>
</manifestEntries>
</archive>
</configuration>
</plugin>
then package the lib jar to lib directory under jar directory.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${myOutPutPath}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>

Related

Trouble getting started with maven assembly plugin

I'm sorry to sound ignorant here, but I'm new to Maven, and have been banging my head against something that I'm sure is quite simple.
The docs say:
[...] a project could produce a ZIP assembly which contains a project's JAR artifact in the root directory, the runtime dependencies in a lib/ directory, and a shell script to launch a stand-alone application.
which is exactly what I want to do! But I can't seem to make it happen.
My POM is as follows:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.anearalone</groupId>
<artifactId>myapp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
[...]
<build>
<finalName>myapp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assemble/dist.xml</descriptor>
</descriptors>
<archive>
<manifest>
<mainClass>com.anearalone.myapp.CLI</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
[...]
</project>
and the referenced dist.xml looks like this:
<assembly>
<id>dist</id>
<formats>
<format>zip</format>
</formats>
<files>
<file>
<outputDirectory>/</outputDirectory>
<source>src/main/bin/arkify.sh</source>
<fileMode>755</fileMode>
</file>
</files>
<dependencySets>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<includes>
<include>*:jar</include>
</includes>
<outputDirectory>/lib</outputDirectory>
</dependencySet>
<dependencySet>
<useProjectArtifact>true</useProjectArtifact>
<includes>
<include>com.anearalone:myapp:jar:0.0.1-SNAPSHOT</include>
</includes>
<outputDirectory>/</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>
This achieves the layout I want in the zip file (though I'm quite sure I'm not getting there in the correct way) but I get two jars in target/ (one in the zip archive, the other in the root), and neither of them includes my mainClass entry in the resultant MANIFEST.MF.
If I change the project.packaging to "pom", which I thought might be correct, of course the extra jar (in the root of target/ goes away, but I get these warning:
[WARNING] Cannot include project artifact: com.anearalone:myapp:pom:0.0.1-SNAPSHOT; it doesn't have an associated file or directory.
[WARNING] The following patterns were never triggered in this artifact inclusion filter:
o 'com.anearalone:myapp'
... and indeed my artifact is not in the archive, and there are still no entries added to MANIFEST.MF.
Anyone have time to help out a beginner?
If I understand your problem correctly, your ZIP is correctly created, but the my-app-0.0.1-SNAPSHOT contained in it (as well as the JAR directly located in target/ directory) does not include your main class in the MANIFEST.MF file?
In fact, the assembly plugin is not dedicated to execute such a task. This is the task of the JAR plugin, which provides a way to indicates, in the MANIFEST.MF the main class of your project. You simply must add this configuration in your current pom.xml:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
...
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>my.app.MainClass</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
Regarding your try to change the packaging of the project to a pom packaging: it was a bad idea ;) Indeed, the pom packaging is used for project without any other resources than the pom.xml itself. It is really useful for pom.xml that are defined as the parent of others projects, or to aggregate multiples modules.

building a jar and including it in a zip with maven-assembly-plugin

I have a mavenized java project (Maven2) which I want to build into a jar, which is easy enough by supplying the jar-with-dependencies descriptorRef in the pom.xml.
However I also need to deploy my project in a zip with some .exe and .bat files, among others, from a bin folder that call the jar. (I am using Tanuki but it does not matter for the use case I think)
In other words, I need a build in which first my sources (and dependencies) are packaged into a jar and that jar is then put into a zip with some additional files from the bin folder.
What should I put in my pom.xml and 'assembly'.xml?
Maven-assembly-plugin is the right tool to do that.
You have to declare this plugin in the "build" section of your pom, and to create another configuration file "assembly.xml" at the root of your project. In this file, your will define the content of your zip file.
The configuration options are described on the official site: http://maven.apache.org/plugins/maven-assembly-plugin/
Here is a basic configuration example of this plugin that should suit your needs.
POM config :
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>zipfile</finalName>
<descriptors>
<descriptor>${basedir}/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
Assembly config :
<assembly>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>to_complete</directory>
<outputDirectory />
<includes>
<include>**/*.jar</include>
<include>**/*.bat</include>
<include>**/*.exe</include>
</includes>
</fileSet>
</fileSets>
</assembly>

Maven, filtering NON-resource file

I share a config file between several modules and I don't want the config file to be baked into any of the JARs.
How can i make Maven do (resource) filtering on the file which is not specified as a resource but is in a config folder on the same level as the root POM?
You could use the Maven Resources Plugin and its resources:copy-resources mojo. From the Examples:
Copy Resources
You can use the mojo copy-resources to
copy resources which are not in the
default maven layout or not declared
in the build/resources element and
attach it to a phase
<project>
...
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<id>copy-resources</id>
<!-- here the phase you need -->
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/extra-resources</outputDirectory>
<resources>
<resource>
<directory>src/non-packaged-resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
...
</build>
...
</project>
Another option would be to use the Maven AntRun Plugin and Ant filtering capabilities (e.g. with the Filter and/or the Copy tasks) but the above looks just fine.

Maven War Plugin - add external resources from another jar

If I wanted to pull out a resource from another jar, say an image or XML file, is that possible? Would I use the Maven Assembly Plugin or the War Plugin to do this? I want it to end up in a WAR file.
Walter
You could use the Dependency plugin in your war module and bind the dependency:unpack goal to the generate-resources phase. Check the Unpacking specific artifacts example.
I want to extract some resource form artefactory to used in other proyect, we need 2 plugins maven-dependency-plugin to extract and maven-clean-plugin to clean proyect. call first plugin execute the goal dependency:unpack.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.6</version>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>com.company.artifactory</artifactId>
<version>${project.version}</version>
<type>jar</type>
<overWrite>true</overWrite>
<includes>dirInsideJar/**/*.*</includes>
<outputDirectory>src/main/resources</outputDirectory>
</artifactItem>
</artifactItems>
<!-- other configurations here -->
</configuration>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
<configuration>
<filesets>
<fileset>
<directory>src/main/resources/dirInsideJar</directory>
</fileset>
</filesets>
</configuration>
</plugin>
The usage of the above is explained in this article.

How to create source distribution with self sustainable maven build?

What I want to do is to create source code distribution of my application with all dependencies and burn it on DVD. So that I could build it in 100 years (well, ok, you know what I mean...). No online dependencies on libraries or maven plugins!
I know that Ant would be better for this, but I'm using maven in my project. I'm not going to switch to Ant just for that, I'm asking how to do this with maven. Or, if there is a way how to generate self sustainable Ant build that I could put on DVD that would be great too.
(there is ant:ant plugin but it just generates Ant build.xml that points dependencies to local maven repo)
The approach I've taken is that I wanted to create special local repository that I can put on DVD and then build project with mvn -o -Dmaven.repo.local=repo/on/dvd. I was trying to make such repository with dependency:copy-dependencies anduseRepositoryLayout param set to true. But it doesn't copy freaking maven plugins that my build depends on...
The only way I can think of to include the plugins is to specify a different local repository for the build on the command line and ensure all the dependency sources etc are downloaded, then create an archive including the project's contents and the custom repository.
Here is a pom that downloads the sources and javadocs (it downloads them to the project's target directory, which we exclude from the archive because they will also be in the local repository). The assembly descriptor bundles the project's contents and the local repository into a single (pretty large) archive.
Note the processing is all in a profile because you really don't want this running on every build. If temporary local repository is in the target directory you can easily clean the mess up afterwards with a mvn clean.
To activate the profile do something like the following:
mvn package -Parchive -Dmaven.repo.local=.\target\repo
Here's the pom:
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>name.seller.rich</groupId>
<artifactId>test-archive</artifactId>
<version>0.0.1</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>archive</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>sources</id>
<phase>pre-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<classifier>sources</classifier>
<failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
<!--the target directory won't be included, but the sources will be in the repository-->
<outputDirectory>${project.build.directory}/sources</outputDirectory>
</configuration>
</execution>
<execution>
<id>javadocs</id>
<phase>pre-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<classifier>javadoc</classifier> <failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
<outputDirectory>${project.build.directory}/javadocs</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/archive.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
And here's the assembly:
<assembly>
<id>archive</id>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<outputDirectory>/</outputDirectory>
<excludes>
<exclude>target/**</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${maven.repo.local}</directory>
<outputDirectory>repo</outputDirectory>
</fileSet>
</fileSets>
</assembly>
Watch this:
Maven Assembly Plugin
Quote from the homepage:
Do you want to create a binary
distribution from a Maven project that
includes supporting scripts,
configuration files, and all runtime
dependencies? You need to use the
Assembly Plugin to create a
distribution for your project.
It's well configurable. I used it especially for making self-running demo versions of web-applications with an embedded jetty server and user documentation.
I don't have a complete answer. Last time I looked at this, I thought that cleaning out the localRepository at the start of the build (or using a separate one) and the running mvn dependency:go-offline.
If you're really keen, you'll also want to bundle maven itself and a JDK into the distribution. This likely takes it out of scope of a pure maven build.