Category Archives: Maven

Maven GPG Plugin: Prevent Signing Prompt or “gpg: signing failed: No such file or directory” Error

PROBLEM

Given the following Maven settings.xml:-

<?xml version="1.0"?>
<settings>
	<profiles>
		<profile>
			<activation>
				<activeByDefault>true</activeByDefault>
			</activation>
			<properties>
				<gpg.executable>/usr/local/bin/gpg</gpg.executable>
				<gpg.passphrase>XXXXXXXXXXXXXXXXXX</gpg.passphrase>
			</properties>
		</profile>
	</profiles>
</settings>

… and the following Maven GPG Plugin configuration in pom.xml:-

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-gpg-plugin</artifactId>
    <version>1.6</version>
    <executions>
        <execution>
            <id>sign-artifacts</id>
            <phase>verify</phase>
            <goals>
                <goal>sign</goal>
            </goals>
        </execution>
    </executions>
</plugin>

When running mvn clean deploy, you either get a prompt for you to enter the GPG passphrase:-

┌────────────────────────────────────────────────────────────────┐
│ Please enter the passphrase to unlock the OpenPGP secret key:  │
│ "Shitty Author <shitty@email.com>"                             │
│ 2048-bit RSA key, ID 9F1A27DFE94D5473,                         │
│ created 2015-05-08.                                            │
│                                                                │
│                                                                │
│ Passphrase: __________________________________________________ │
│                                                                │
│         <OK>                                    <Cancel>       │
└────────────────────────────────────────────────────────────────┘

… or, get the following error:-

gpg: signing failed: No such file or directory

The long story short, Maven GPG Plugin isn’t using the passphrase defined in the Maven settings.xml… AND THIS UPSETS THE HONEY BADGER!

SOLUTION

If you 1) initially had it working in the past, and 2) have tried all sorts of solutions from the web, and still couldn’t get it working, chances are you have unconsciously upgraded GPG version from 2.0 to 2.1.

You can check your GPG version by running the following command:-

✗ gpg --version 
gpg (GnuPG) 2.1.21
libgcrypt 1.7.6
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: /Users/shitty-author/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

To fix this, GPG 2.1 requires --pinentry-mode to be set to loopback in order to pick up gpg.passphrase value defined in Maven settings.xml.

So, update Maven GPG Plugin configuration in pom.xml to the following:-

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-gpg-plugin</artifactId>
    <version>1.6</version>
    <executions>
        <execution>
            <id>sign-artifacts</id>
            <phase>verify</phase>
            <goals>
                <goal>sign</goal>
            </goals>
            <configuration>
                <gpgArguments>
                    <arg>--pinentry-mode</arg>
                    <arg>loopback</arg>
                </gpgArguments>
            </configuration>
        </execution>
    </executions>
</plugin>

When re-running mvn clean deploy, it should deploy the artifact properly now.

Advertisements

JAXB2: Adding toString() to Generated Java Classes

PROBLEM

By default, the generated Java class prints the memory address when toString() is invoked.

However, sometimes it is helpful to have a more meaningful toString() for debugging purposes.

SOLUTION

To fix this, configure maven-jaxb2-plugin to generate toString() based on the fields in the class:-

<project ...>
  ...
  
  <properties>
    <org.jvnet.jaxb2_commons.version>0.11.1</org.jvnet.jaxb2_commons.version>
  </properties>
  
  <dependencies>
    <dependency>
      <groupId>org.jvnet.jaxb2_commons</groupId>
      <artifactId>jaxb2-basics-runtime</artifactId>
      <version>${org.jvnet.jaxb2_commons.version}</version>
    </dependency>
  </dependencies>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.jvnet.jaxb2.maven2</groupId>
        <artifactId>maven-jaxb2-plugin</artifactId>
        <version>0.13.1</version>
        <executions>
          <execution>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <schemaLanguage>WSDL</schemaLanguage>
          <generatePackage>my.package.wsdl</generatePackage>
          <bindingDirectory>${project.basedir}/src/main/resources</bindingDirectory>
          <bindingIncludes>
            <include>jaxb-binding.xjb</include>
          </bindingIncludes>
          <schemas>
            <schema>
              <fileset>
                <directory>${project.basedir}/src/main/resources</directory>
                <includes>
                  <include>web-service.wsdl</include>
                </includes>
              </fileset>
            </schema>
          </schemas>
          <args>
            <arg>-XtoString</arg>
          </args>
          <plugins>
            <plugin>
              <groupId>org.jvnet.jaxb2_commons</groupId>
              <artifactId>jaxb2-basics</artifactId>
              <version>${org.jvnet.jaxb2_commons.version}</version>
            </plugin>
          </plugins>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

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.