Maven

  • Mavenis a powerful project management tool that is based on POM (project object model). It is used for projects build, dependency and documentation.
  • It simplifies the build process like ANT. But it is too much advanced than ANT.
  • Current version of Maven is 3.

A JAR (Java ARchive)

  • JAR is a package file format typically used to aggregate many Java class files and associated metadata and resources (text, images, etc.) into one file for distribution.

TO CREATE A JAR FILE WITH MAVEN

Create a simple Java project

  • Create a Java project from the Maven quick start template.
javascript code
$ mvn archetype:generate -DgroupId=com.wikitechy.core.utils -DartifactId=dateUtils
-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
[ad type=”banner”]
  • The following files and folder structure will be created.
css code
.
|____dateUtils
| |____pom.xml
| |____src
| | |____main
| | | |____java
| | | | |____com
| | | | | |____wikitechy
| | | | | | |____core
| | | | | | | |____utils
| | | | | | | | |____App.java
| | |____test
| | | |____java
| | | | |____com
| | | | | |____wikitechy
| | | | | | |____core
| | | | | | | |____utils
| | | | | | | | |____AppTest.java

Above folder structure is not enough, create a log4j.properties file and put it in src/main/resources/log4j.properties, just create the resources folder manually.

log4j.properties

java code
# Root logger option
log4j.rootLogger=DEBUG, stdout

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

Make it support Eclipse.

java code
$ mvn eclipse:eclipse
[ad type=”banner”]

Update Pom.xml

Update pom.xml to declare both log4j and the jodatime dependencies, for output to a jar format, make sure the packaging is set to “jar”.

pom.xml

html code
<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>com.wikitechy.core.utils</groupId>
<artifactId>dateUtils</artifactId>
<!-- Output to jar format -->
<packaging>jar</packaging>

<version>1.0-SNAPSHOT</version>
<name>dateUtils</name>
<url>http://maven.apache.org</url>
<properties>
<jdk.version>1.7</jdk.version>
<jodatime.version>2.5</jodatime.version>
<junit.version>4.11</junit.version>
<log4j.version>1.2.17</log4j.version>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>

<build>
<finalName>dateutils</finalName>
<plugins>

<!-- download source code in Eclipse, best practice -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
</configuration>
</plugin>
<!-- Set a JDK compiler level -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>

<!-- Make this jar executable -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<!-- DO NOT include log4j.properties file in our Jar -->
<excludes>
<exclude>**/log4j.properties</exclude>
</excludes>
<archive>
<manifest>
<!-- Jar file entry point -->
<mainClass>com.wikitechy.core.utils.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>

</plugins>
</build>

</project>

Update App.java

App.java

java code
package com.wikitechy.core.utils;

import org.apache.log4j.Logger;
import org.joda.time.LocalDate;

public class App
{

private static final Logger logger = Logger.getLogger(App.class);

public static void main(String[] args)
{
System.out.println(getLocalCurrentDate());
}
private static String getLocalCurrentDate() {

if (logger.isDebugEnabled()) {
logger.debug("getLocalCurrentDate() is executed!");
}

LocalDate date = new LocalDate();
return date.toString();

}

}
  • Thus this project has two dependencies : log4j and jodatime.

Working with Dependencies

How to add dependencies in a jar

  • we can put both log4j.jar and jodatime.jar inside the final.jar, but our classes are unable to call other classes which is inside the unpack log4j.jar, Java jar is designed like this, unless we can create a special class loader like one-jar plugin.
  • Alternatively, use maven-assembly-plugin to extract all dependency jars into raw classes, and group it together.
  • Try one-jar plugin, it will create a fat-jar, which includes the entire project’s dependencies into a single jar file.

Solution

  • Copy the entire project’s dependencies to a pre-defined folder, and define the dependency class path in the jar’s manifest file.
  • The updated and final pom.xml, to use maven-dependency-plugin to copy all dependencies to target/dependency-jars/ folder, and use maven-jar-plugin to add the dependency class path.

pom.xml

html code
<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>com.wikitechy.core.utils</groupId>
<artifactId>dateUtils</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>dateUtils</name>
<url>http://maven.apache.org</url>
<properties>
<jdk.version>1.7</jdk.version>
<jodatime.version>2.5</jodatime.version>
<junit.version>4.11</junit.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>

<build>
<finalName>dateutils</finalName>
<plugins>

<!-- download source code in Eclipse, best practice -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
</configuration>
</plugin>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>

<build>
<finalName>dateutils</finalName>
<plugins>

<!-- download source code in Eclipse, best practice -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>false</downloadJavadocs>
</configuration>
</plugin>
<!-- Set a compiler level -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
<!-- Make this jar executable -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/log4j.properties</exclude>
</excludes>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.wikitechy.core.utils.App</mainClass>
<classpathPrefix>dependency-jars/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>

<!-- Copy project dependency -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!-- exclude junit, we need runtime dependency only -->
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/dependency-jars/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>

</plugins>
</build>

</project>
[ad type=”banner”]

final Jar file

Package

javascript code
$ mvn package
  • Review the folder structure in the target folder

A dateutils.jar is created, and the entire project runtime dependencies (excluded junit) are copied to target/dependency-jars/ folder.

List out the dateutils.jar content :

javascript code
$ jar tf target/dateutils.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/wikitechy/
com/wikitechy/core/
com/wikitechy/core/utils/
com/wikitechy/core/utils/App.class
META-INF/maven/
META-INF/maven/com.wikitechy.core.utils/
META-INF/maven/com.wikitechy.core.utils/dateUtils/
META-INF/maven/com.wikitechy.core.utils/dateUtils/pom.xml
META-INF/maven/com.wikitechy.core.utils/dateUtils/pom.properties

Extracts and review the content of MANIFEST.MF, the dependencies are added in the Class-Path.

javascript code
META_INF/MANIFEST.MF
Manifest-Version: 1.0
Built-By: wikitechy
Build-Jdk: 1.7.0_05
Class-Path: dependency-jars/joda-time-2.5.jar dependency-jars/log4j-1.2.17.jar
Created-By: Apache Maven 3.1.1
Main-Class: com.wikitechy.core.utils.App
Archiver-Version: Plexus Archiver
[ad type=”banner”]

Run it

javascript code
$ java -jar target/dateutils.jar

log4j:WARN No appenders could be found for logger (com.wikitechy.core.utils.App).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
2014-10-19

Where is log4j.properties?

To exclude the log4j.properties in the jar file, to avoid issues like multiple log4j.properties files in classpath.

We can still pass in the log4j properties via the log4j.configuration system property like this :

javascript code
$ java -jar -Dlog4j.configuration=file:/full_path/log4j.properties target/dateutils.jar

17:09:15,385 DEBUG App:18 - getLocalCurrentDate() is executed!

Categorized in: