How to timestamp an output artifact in Maven? - maven-2

I am trying to find out if Maven has some built-in plug-in that can be used to time-stamp artifacts. I created an assembly file and am using the maven-assembly plugin to create a final distribution (jars,docs,scripts, etc). I want to name this distribution file as domain_year_month_day.zip. How can I append the day portion of a timestamp to the end of the final zip file that is being produced. Thanks.

You don't need the maven-timestamp-plugin with newer versions of maven. Since 2.1'ish, Maven has provide the special property maven.build.timestamp.
You set the format in the pom properties with something like this:
<maven.build.timestamp.format>yyyy-MM-dd'T'HH.mm.ss</maven.build.timestamp.format>
And then use ${maven.build.timestamp} wherever you need a timestamp property. See http://maven.apache.org/guides/introduction/introduction-to-the-pom.html for details.

You could use the maven-timestamp-plugin to set a property (e.g. timestamp) and use it later in the final name of your assembly.
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>create-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<finalName>domain_${timestamp}</finalName>
<descriptors>
<descriptor>src/main/assembly/my-descriptor.xml</descriptor>
</descriptors>
<attach>true</attach>
</configuration>
</execution>
</executions>
</plugin>
As an alternative, you could put some Groovy code in your POM using the GMaven plugin:
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>set-custom-property</id>
<phase>initialize</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
def timestamp = new Date().format('MM_dd_yy')
project.properties.setProperty('timestamp', timestamp)
</source>
</configuration>
</execution>
<execution><!-- for demonstration purpose -->
<id>show-custom-property</id>
<phase>generate-resources</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
println project.properties['timestamp']
</source>
</configuration>
</execution>
</executions>
</plugin>
A sample output showing the property:
$ mvn generate-resources
[INFO] Scanning for projects...
[INFO]
...
[INFO] --- gmaven-plugin:1.3:execute (set-custom-property) # Q4081274 ---
[INFO]
[INFO] --- gmaven-plugin:1.3:execute (show-custom-property) # Q4081274 ---
11_02_10
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...
And again, use this property later in the build name of your assembly.

As ${maven.build.timestamp} seems buggy in maven, the workaround is as follows:
Create a new variable (I chose "build.timestamp", here) - and, optionally, specify the format :
pom.xml
<project>
...
<properties>
...
<build.timestamp>${maven.build.timestamp}</build.timestamp>
<maven.build.timestamp.format>yyyyMMdd</maven.build.timestamp.format>
<!-- default is: yyyyMMdd-HHmm -->
</properties>
<build>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>some-assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make</id>
<phase>package</phase>
<goals>
<goal>assembly</goal>
</goals>
</execution>
</executions>
</plugin>
...
Use the custom variable from anywhere:
some-assembly.xml
<?xml version="1.0"?>
<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>release-${build.timestamp}</id>
<baseDirectory>/</baseDirectory>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.build.directory}/${project.artifactId}-${project.version}</directory>
</fileSet>
</fileSets>
</assembly>

if you use Hudson/Jenkins you can just use the variable ${BUILD_ID} for getting sort of timestamp to any properties file u want to edit.
information to the other environment variables Hudson/Jenkins supports, take a look here:
http://wiki.hudson-ci.org/display/HUDSON/Building+a+software+project

Related

Parent properties inside maven antrun plugin

There is a multi-module project. Inside the child I need to do some complicated stuff (integration test with deploying to application server and so on). So there is an integrationtest child, and from this module I need the root of the parent to reach other modules. I do not want to use "..". There is a property in integrationtest POM:
<properties>
<main.basedir>${project.parent.basedir}</main.basedir>
...
</properties>
And there is an antrun plugin with the following content:
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>render-parameter-sql</id>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echoproperties/>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
In the output, the main.basedir is not resolved:
main:
[echoproperties] #Ant properties
[echoproperties] #Thu Oct 28 09:32:13 CEST 2010
[echoproperties] ant.core.lib=C\:\\Users\\gaborl\\.m2\\repository\\org\\apache\\ant\\ant\\1.8.1\\ant-1.8.1.jar
...
[echoproperties] main.basedir=${project.parent.basedir}
[echoproperties] maven.dependency.antlr.antlr.jar.path=C\:\\Users\\gaborl\\.m2\\repository\\antlr\\antlr\\2.7.6\\antlr-2.7.6.jar
After becoming really angry I decided to ask you how to get around this...
I don't know exactly why the ${project.parent.basedir} is not "available" from AntRun, maybe it's just not supported (see http://jira.codehaus.org/browse/MNG-3597).
Here is an horrible workaround using gmaven:
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>set-custom-property</id>
<phase>validate</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties.setProperty('main.basedir', project.parent.basedir.toString())
</source>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>render-parameter-sql</id>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<echo>project.artifactId : ${project.artifactId}</echo>
<echo>project.parent.basedir : ${project.parent.basedir}</echo>
<echo>main.basedir : ${main.basedir}</echo>
<echo>project.basedir : ${project.basedir}</echo>
<echo>project.build.directory : ${project.build.directory}</echo>
</target>
</configuration>
</execution>
</executions>
</plugin>
I'm not proud of it, but it kinda "works" (if a string representation of the path to the parent basedir is ok for you):
$ mvn validate
[INFO] Scanning for projects...
...
[INFO] --- maven-antrun-plugin:1.6:run (render-parameter-sql) # Q4040778 ---
[INFO] Executing tasks
main:
[echo] project.artifactId : Q4040778
[echo] project.parent.basedir : ${project.parent.basedir}
[echo] main.basedir : /home/pascal/Projects/stackoverflow
[echo] project.basedir : /home/pascal/Projects/stackoverflow/Q4040778
[echo] project.build.directory : /home/pascal/Projects/stackoverflow/Q4040778/target
[INFO] Executed tasks
...
But I need to say that what you want to do (from this module I need the root of the parent to reach other modules) is a bad practice, modules should be self contained and not tightly coupled.
I do not recommend using what I posted :)

Maven: add a folder or jar file into current classpath

I am using maven-compile plugin to compile classes. Now I would like to add one jar file into the current classpath. That file stays in another location (let's say c:/jars/abc.jar . I prefer to leave this file here). How can I do that?
If I use classpath in the argument:
<configuration>
<compilerArguments>
<classpath>c:/jars/abc.jar</classpath>
</compilerArguments>
</configuration>
it will not work because it will override the current classpath (that includes all the dependencies)
This might have been asked before. See Can I add jars to maven 2 build classpath without installing them?
In a nutshell: include your jar as dependency with system scope. This requires specifying the absolute path to the jar.
See also http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
The classpath setting of the compiler plugin are two args. Changed it like this and it worked for me:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<compilerArgs>
<arg>-cp</arg>
<arg>${cp}:${basedir}/lib/bad.jar</arg>
</compilerArgs>
</configuration>
</plugin>
I used the gmavenplus-plugin to read the path and create the property 'cp':
<plugin>
<!--
Use Groovy to read classpath and store into
file named value of property <cpfile>
In second step use Groovy to read the contents of
the file into a new property named <cp>
In the compiler plugin this is used to create a
valid classpath
-->
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.12.0</version>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<!-- any version of Groovy \>= 1.5.0 should work here -->
<version>3.0.6</version>
<type>pom</type>
<scope>runtime</scope>
</dependency>
</dependencies>
<executions>
<execution>
<id>read-classpath</id>
<phase>validate</phase>
<goals>
<goal>execute</goal>
</goals>
</execution>
</executions>
<configuration>
<scripts>
<script><![CDATA[
def file = new File(project.properties.cpfile)
/* create a new property named 'cp'*/
project.properties.cp = file.getText()
println '<<< Retrieving classpath into new property named <cp> >>>'
println 'cp = ' + project.properties.cp
]]></script>
</scripts>
</configuration>
</plugin>
From docs and example it is not clear that classpath manipulation is not allowed.
<configuration>
<compilerArgs>
<arg>classpath=${basedir}/lib/bad.jar</arg>
</compilerArgs>
</configuration>
But see Java docs (also https://www.cis.upenn.edu/~bcpierce/courses/629/jdkdocs/tooldocs/solaris/javac.html)
-classpath path Specifies the path javac uses to look up classes needed to run javac or being referenced by other classes you are
compiling. Overrides the default or the CLASSPATH environment variable
if it is set.
Maybe it is possible to get current classpath and extend it,
see in maven, how output the classpath being used?
<properties>
<cpfile>cp.txt</cpfile>
</properties>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.9</version>
<executions>
<execution>
<id>build-classpath</id>
<phase>generate-sources</phase>
<goals>
<goal>build-classpath</goal>
</goals>
<configuration>
<outputFile>${cpfile}</outputFile>
</configuration>
</execution>
</executions>
</plugin>
Read file (Read a file into a Maven property)
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
def file = new File(project.properties.cpfile)
project.properties.cp = file.getText()
</source>
</configuration>
</execution>
</executions>
</plugin>
and finally
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<compilerArgs>
<arg>classpath=${cp}:${basedir}/lib/bad.jar</arg>
</compilerArgs>
</configuration>
</plugin>
We mixed two of the answers found here to solve a similar problem. Our project needs a JAR only in compile stage, but add a local dependency, using system scope, it is unuseful because Maven refuse the artifact publication with an error related to a missing dependency.
The snippets used are the following:
<properties>
<classpathfile>${basedir}/classpathfile.classpath</classpathfile>
</properties>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.9</version>
<executions>
<execution>
<id>build-classpath</id>
<phase>generate-sources</phase>
<goals>
<goal>build-classpath</goal>
</goals>
<configuration>
<outputFile>${classpathfile}</outputFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
def file = new File(project.properties.classpathfile)
project.properties.originalClassPath = file.getText()
</source>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgs>
<arg>-cp</arg>
<arg>${originalClassPath}${path.separator}${basedir}/../../../bin/POM_RUNTIME_PLACEHOLDER/ExtraJar.jar</arg>
</compilerArgs>
</configuration>
</plugin>
Maven is able to compile and successfully deploy the artifacts.
If anyone is interested the full POM is available in GitHub under project NuReflector, defaultPOM.template under src/NuReflector.

Pom.xml - tar task

Is it possible to add a task to the pom.xml file that will create a tar.gz / .zip file.
for eample:
<tar type="tar.gz" source="resources/sql" tofile="target/sql.tar.gz"/>
Thanks
Use the maven-assembly-plugin
Create a src/main/assembly/bin.xml as detailed at http://maven.apache.org/plugin-developers/cookbook/generate-assembly.html and http://maven.apache.org/plugins/maven-assembly-plugin/descriptor-refs.html#bin
Put your resources sql files in the includes and give the format of output as tar.gz
Next, in your pom.xml put the reference to this plugin
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly/bin.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
http://maven.apache.org/plugins/maven-assembly-plugin/usage.html
Last, call this using
mvn package

Maven-assembly-plugin: custom jar filenames

I use the assembly plugin to create several jars with some classes in it. I need custom names for the resulting jars: app_business.jar app_gui.jar core.jar etc.
Currently I have to following configuration:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<finalName>app_business</finalName>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
<attach>true</attach>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
an the assembly.xml file:
<assembly>
<id>app_business</id>
<formats>
<format>jar</format>
</formats>
<baseDirectory>target</baseDirectory>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.build.outputDirectory}</directory>
<outputDirectory></outputDirectory>
<includes>
<include>org/xyz/**</include>
</includes>
</fileSet>
</fileSets>
</assembly>
this creates a file app_business.jar which is perfect. But i have no idea how to create my other files. The option appendAssemblyId doesn't help me, as it creates filenames in the format AppName-app_business.jar. I really need the exact filesname app_business.jar.
Any Idea? Thank you very much!
You can move the configuration element below the execution element of the plugin declaration. This means the configuration will only be applied to that execution. You can then add additional executions of the assembly plugin for your other assemblies.
Here is an example of the modified configuration with two executions, each referencing a different assembly:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-business-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<finalName>app_business</finalName>
<descriptors>
<descriptor>src/main/assembly/business-assembly.xml</descriptor>
</descriptors>
<attach>true</attach>
</configuration>
</execution>
<execution>
<id>make-gui-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<finalName>app_gui</finalName>
<descriptors>
<descriptor>src/main/assembly/gui-assembly.xml</descriptor>
</descriptors>
<attach>true</attach>
</configuration>
</execution>
</executions>
</plugin>
With this configuration, two additional jars (app _business.jar and app _gui.jar) will be created in the target directory, though be aware if you install the project, only the last artifact assembled will be installed (this could of course be a problem).
To avoid this you would need to change the appendAssemblyId properties to true. The closest you can get in this case is to change the finalNames to "app" and the IDs to "gui" and "business", resulting in app-gui.jar and app-business.jar being packaged and all artifacts being installed.
The standard maven plugins are meant for general, repetitive work. They gather all information from the POM and they are smart (in the sense that you don't need to configure much).
If you need special tasks, then I suggest to use the ant plugin which allows you to embed a piece of Ant code in the POM. This allows you to run the jar task.

Maven - extract /test/resources/my.zip in /target during the Test Phase

I've some test resources (that are specific for a particular task) zipped in /test/resources/my.zip.
I want to extract the zip content to /target during the maven Test Phase.
Do you know what should I specify in the pom.xml to achieve this?
One solution is to use the maven-antrun-plugin to run the unzip Ant task. The following configuration in the build section of your POM should be pretty much what you need (but I haven't tested it):
<build>
<plugins>
<!-- ... -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-test-resources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<unzip src="test/resources/my.zip" dest="target/" overwrite="true"/>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
<!-- ... -->
</plugins>
</build>