Category Archives: Maven

Maven: Bundling and Unpacking Native Libraries

Introduction

Steps to bundle the native libraries to be pushed to Nexus, and to unpack the native libraries on mvn package.

Bundling Native Libraries into a JAR File

Let’s assume we have the following native libraries for multiple platforms:-

➜  tree native 
native
├── linux
│   ├── x86
│   │   └── libnative_synchronization.so
│   └── x86_64
│       └── libnative_synchronization.so
├── macosx
│   └── libnative_synchronization.jnilib
└── win32
    ├── x86
    │   └── native_synchronization.dll
    └── x86_64
        └── native_synchronization.dll

Create a jar that contains these native libraries. The -C options prevents the native folder from being created in the JAR file.

➜  jar cMf my-project-native.jar -C native .   

Pushing JAR to Nexus

When pushing this native JAR file to Nexus, make sure to use the natives-* classifier. In this example, I called it natives-all.

<dependency>
	<groupId>my.project</groupId>
	<artifactId>native</artifactId>
	<version>1.0</version>
	<classifier>natives-all</classifier>
</dependency>

Configuring pom.xml

First, add LWJGL and the native JAR dependencies.

<dependencies>
    <dependency>
        <groupId>org.lwjgl.lwjgl</groupId>
        <artifactId>lwjgl</artifactId>
        <version>2.9.3</version>
    </dependency>
    <dependency>
		<groupId>my.project</groupId>
		<artifactId>native</artifactId>
		<version>1.0</version>
		<classifier>natives-all</classifier>
    </dependency>
</dependencies>

Then, add the following plugin:-

<build>
    <plugins>
		<plugin>
		    <groupId>com.googlecode.mavennatives</groupId>
		    <artifactId>maven-nativedependencies-plugin</artifactId>
		    <version>0.0.7</version>
		    <executions>
		        <execution>
		            <id>unpacknatives</id>
		            <phase>generate-resources</phase>
		            <goals>
		                <goal>copy</goal>
		            </goals>
		        </execution>
		    </executions>
		</plugin>
    </plugins>
</build>

This plugin searches for dependencies with natives-* classifier and unpacks the content to target/natives folder.

Testing

Run mvn clean package.

Inspect target/natives. The native libraries should be unpacked here:-

target/natives
├── META-INF
│   └── MANIFEST.MF
├── OpenAL32.dll
├── OpenAL64.dll
├── jinput-dx8.dll
├── jinput-dx8_64.dll
├── jinput-raw.dll
├── jinput-raw_64.dll
├── jinput-wintab.dll
├── libjinput-linux.so
├── libjinput-linux64.so
├── libjinput-osx.jnilib
├── liblwjgl.dylib
├── liblwjgl.so
├── liblwjgl64.so
├── libopenal.so
├── libopenal64.so
├── linux
│   ├── x86
│   │   └── libnative_synchronization.so
│   └── x86_64
│       └── libnative_synchronization.so
├── lwjgl.dll
├── lwjgl64.dll
├── macosx
│   └── libnative_synchronization.jnilib
├── openal.dylib
└── win32
    ├── x86
    │   └── native_synchronization.dll
    └── x86_64
        └── native_synchronization.dll

Maven Archetype Plugin: Velocity Variable Substitutions Not Resolving

PROBLEM

Let’s assume we have the following package.json in our archetype:-

{
  "name": "${rootArtifactId}",
  "private": true,
  "devDependencies": {
    ...
  }
}

When creating a project from this archetype, the Velocity variable substitution for ${rootArtifactId} doesn’t resolve at all.

SOLUTION

After reading Maven Archetype Plugin’s source code here and here, the Velocity variable substitutions are only performed on the following file extensions:-

List<String> DEFAULT_FILTERED_EXTENSIONS =
    Arrays.asList(
        new String[]
            {
                "java", "xml", "txt", "groovy", "cs", "mdo", "aj", "jsp", "gsp", 
                "vm", "html", "xhtml", "properties", ".classpath", ".project"
            }
    );

In another word, if we have these variables in JSON or JavaScript files, they will not resolved at all.

To fix this, define the needed file extensions in Maven Archetype Plugin configuration:-

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-archetype-plugin</artifactId>
    <version>2.3</version>
    <configuration>
        <archetypeFilteredExtentions>js,json,md,java,xml,txt,groovy,jsp,vm,html,properties</archetypeFilteredExtentions>
    </configuration>
</plugin>

Maven Archetype Plugin: Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.3:create-from-project

PROBLEM

When running mvn archetype:create-from-project with Maven Ear Plugin defined under <pluginManagement>….

<project ...>
    ...
    <build>
        <pluginManagement>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-archetype-plugin</artifactId>
                    <version>2.3</version>
                </plugin>

                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-ear-plugin</artifactId>
                    <version>2.10.1</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

… the following exception occurs…

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.3:create-from-project (default-cli) on project myproject-webapp: null: MojoFailureException -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.3:create-from-project (default-cli) on project myproject-webapp: null
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:212)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
        at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
        at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
        at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120)
        at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:355)
        at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:155)
        at org.apache.maven.cli.MavenCli.execute(MavenCli.java:584)
        at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:216)
        at org.apache.maven.cli.MavenCli.main(MavenCli.java:160)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
        at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
        at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.MojoFailureException
        at org.apache.maven.archetype.mojos.CreateArchetypeFromProjectMojo.execute(CreateArchetypeFromProjectMojo.java:258)
        at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
        at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
        ... 19 more

SOLUTION

It appears this problem happens when using Maven Archetype Plugin 2.3, but works fine when using 2.2.

To fix this, define an empty <modules> under <configuration> to prevent NullPointerException.

<project ...>
    ...
    <build>
        <pluginManagement>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-archetype-plugin</artifactId>
                    <version>2.3</version>
                </plugin>

                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-ear-plugin</artifactId>
                    <version>2.10.1</version>
                    <configuration>
                        <modules/>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

Maven: Skinning Generated Site

PROBLEM

The default Maven generated site looks like web pages created in the 80s:-

SOLUTION

The good news is Maven allows us to change the skin.

To use one of these pre-defined skins, create site.xml at this location:-

myproject
├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │       └── ...
│   ├── site
│   │   └── site.xml

In site.xml, enter the following:-

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/DECORATION/1.6.0"
         xsi:schemaLocation="http://maven.apache.org/DECORATION/1.6.0
         http://maven.apache.org/xsd/decoration-1.6.0.xsd">
    <bannerLeft>
        <name>${project.name}</name>
        <href>${project.url}</href>
    </bannerLeft>

    <publishDate position="right" format="MMMM dd, yyyy"/>
    <version/>
    <poweredBy>
        <logo img="#" alt=""/>
    </poweredBy>

    <skin>
        <groupId>org.apache.maven.skins</groupId>
        <artifactId>maven-fluido-skin</artifactId>
        <version>1.4</version>
    </skin>

    <body>
        <menu ref="reports"/>
        <footer/>
    </body>
</project>

In the above example, we use Maven Fluido Skin.

In Maven 3, site:attach-descriptor has been removed from the built-in lifecycle bindings, so we need to explicitly define attach-descriptor goal in Maven Site Plugin to pick up src/site/site.xml.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-site-plugin</artifactId>
    <version>3.4</version>
    <executions>
        <execution>
            <id>attach-descriptor</id>
            <goals>
                <goal>attach-descriptor</goal>
            </goals>
        </execution>
    </executions>
</plugin>

When you run mvn clean site, oh hey, welcome to 21st century!

JaCoCo Web Report Not Rendering Properly in GitHub Pages

PROBLEM

When pushing JaCoCo web report to GitHub’s gh-pages branch, it does not render properly on the web. For example:-

The GitHub pages are powered by Jekyll. By default, Jekyll does not allow directories or files that begin with a dot, pound sign, tilde or underscore.

Since JaCoCo places all the image and CSS files in a directory called .resources, this directory will not be pushed to GitHub’s gh-pages branch.

SOLUTION UPDATE: 2015-07-23

GitHub Site Plugin has a noJekyll parameter that will create the .nojekyll file at the root path if it is set to true.

<plugin>
    <groupId>com.github.github</groupId>
    <artifactId>site-maven-plugin</artifactId>
    <version>${site-maven-plugin.version}</version>
    <configuration>
        <message>Creating site for ${project.version}</message>
        <server>github</server>
        <noJekyll>true</noJekyll>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>site</goal>
            </goals>
            <phase>site</phase>
        </execution>
    </executions>
</plugin>

SOLUTION 1: Disable Jekyll

One simple fix is to completely disable Jekyll by creating an empty file called .nojekyll at this location:-

myproject
├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │       └── ...
│   ├── site
│   │   └── resources
│   │       └── .nojekyll

SOLUTION 2: Configure Jekyll

While disabling Jekyll works, it may 1) seem rather risky since there maybe other hidden directories/files and 2) possibly send too much garbage to GitHub’s gh-pages branch.

To fix this, we will instruct Jekyll to allow JaCoCo’s .resources directory through by creating a file called _config.yml at this location:-

myproject
├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │       └── ...
│   ├── site
│   │   └── resources
│   │       └── _config.yml

In _config.yml, enter the following:-

# Allow JaCoCo's directory to bypass GitHub's Jekyll-powered pages 
# so that the web report renders properly
include: ['.resources']

OUTCOME

Either solution will produce a nicely rendered JaCoCo web report in GitHub pages.

Maven: Deploying Generated Site to GitHub

INTRO

GitHub provides an incredible feature that allows us to easily push Maven generated site to our project’s GitHub repository.

Here’s how to do it…

STEP 1: Define GitHub credential

Go to ~/.m2/settings.xml and add your GitHub username and password:-

<settings ...>
	<servers>
		<server>
			<id>github</id>
			<username>USERNAME</username>
			<password>PASSWORD</password>
		</server>
	</servers>
</settings>

STEP 2: Define GitHub’s site-maven-plugin

GitHub provides its own site-maven-plugin that can be used to deploy a Maven generated site to a gh-pages branch so that it can be served statically as a GitHub Project Page.

In pom.xml, add the following plugin configuration:-

<project ...>
    ...
    <build>
        <plugins>
            ...
            <plugin>
                <groupId>com.github.github</groupId>
                <artifactId>site-maven-plugin</artifactId>
                <version>0.11</version>
                <configuration>
                    <message>Creating site for ${project.version}</message>
                    <server>github</server>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>site</goal>
                        </goals>
                        <phase>site</phase>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    ...
</project>

STEP 3: Define project URL

In pom.xml, define <url> tag that points to the project’s GitHub repository.

<project ...>
    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <url>https://github.com/USERNAME/PROJECT-NAME</url>
	...
</project>

If this is not defined, Maven will throw the following error when executing mvn clean site:-

Failed to execute goal com.github.github:site-maven-plugin:0.11:site 
(default) on project PROJECT-NAME: No GitHub repository (owner and 
name) configured -> [Help 1]

STEP 4: Generate Maven site and push to GitHub

Finally, run the following command: mvn clean site.

The generated site can be viewed from https://USERNAME.github.io/PROJECT-NAME/ .

That’s it…. very simple.

Jenkins: Getting Karma Generated Test Results to Appear in Maven Project Job

PROBLEM

Jenkins, for some reason, does not pick up Karma generated JUnit test reports even though they are created in the right directory… and apparently, it is a known problem. While Freestyle project job allows us to manually publish these JUnit reports, my intention is to rely on Maven project job to do the same thing.

PREREQUISITES

ENSURING KARMA GENERATED JUNIT REPORT SHOWS UP IN JENKINS…

Instead of manually running karma start command in Jenkins, we will rely on maven-karma-plugin to do this for us. The key here is to specify the correct <phase> so that Jenkins picks up and presents the generated report.

pom.xml

<build>
    <plugins>
        <plugin>
            <groupId>com.kelveden</groupId>
            <artifactId>maven-karma-plugin</artifactId>
            <version>1.6</version>
            <executions>
                <execution>
                    <phase>process-test-classes</phase>
                    <goals>
                        <goal>start</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <karmaExecutable>${basedir}/node_modules/karma/bin/karma</karmaExecutable>
                <configFile>src/test/resources/karma.jenkins.conf.js</configFile>
            </configuration>
        </plugin>
    </plugins>
</build>

CONFIGURING JENKINS

Since Karma test runner requires NodeJS, we will install NodeJS Plugin in Jenkins. This allows us to automatically install NodeJS from Jenkins.

Once installed, go to Manage Jenkins -> Configure System and go to NodeJS section:-

Although this section allows us to specify npm packages to install, I’m having trouble installing certain packages, such as karma-phantomjs-launcher. The phantomJS package invokes node install.js during the installation, however, the node command isn’t available in PATH environment variable at this point. Thus, the installation will always fail. So, the npm packages will be configured at the job level in the next step. Further, it is not a good idea to install Karma-related plugins globally here because every Jenkins job may use slightly different versions of the same plugin (think Spring or Hibernate versions in every job’s pom.xml).

Next, create a Maven project job and configure it.

Configuring Build Environment, Pre Steps and Build

We exposed NodeJS to PATH environment variable so that we can install phantomJS package.

Next, we created a pre-build step to execute npm install, which reads package.json from the job and installs the needed dependencies within the job.

Finally, we will want Maven to invoke test goal so that it runs both Java tests and Karma test runner.

Configuring Coverage Report

We provided the Karma generated coverage report XML file.

OUTCOME

When we run Build Now in Jenkins, both unit test and coverage reports will display both Java and JavaScript execution results.

Since we ran npm install as a pre-build step, the job will now have node_modules directory.