Category Archives: Maven

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.

Advertisements

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.

Maven: Plugin Execution Not Covered by Lifecycle Configuration

PROBLEM

When running a Maven project in Eclipse or Spring Tool Suite (STS), we get an exception that is similar to this:-

Plugin execution not covered by lifecycle configuration:
org.jacoco:jacoco-maven-plugin:0.6.4.201312101107:prepare-agent (execution: pre-unit-test, phase: initialize)

SOLUTION

This problem stems from m2eclipse (m2e) plugin, which provides Maven support in Eclipse-based IDEs.

STEP 1: Suppress the Error Message

The simplest solution is to instruct m2e to silently ignore the plugin execution by adding the following configuration in the project root pom.xml:-

IMPORTANT: Please make sure to enter the correct plugin’s groupId, artifactId, versionRange and goals that causes the problem in the first place.

<pluginManagement>
    <plugins>
        <plugin>
            <groupId>org.eclipse.m2e</groupId>
            <artifactId>lifecycle-mapping</artifactId>
            <version>1.0.0</version>
            <configuration>
                <lifecycleMappingMetadata>
                    <pluginExecutions>
                        <pluginExecution>
                            <pluginExecutionFilter>
                                <groupId>org.jacoco</groupId>
                                <artifactId>jacoco-maven-plugin</artifactId>
                                <versionRange>[0.0.1,)</versionRange>
                                <goals>
                                    <goal>prepare-agent</goal>
                                </goals>
                            </pluginExecutionFilter>
                            <action>
                                <ignore />
                            </action>
                        </pluginExecution>
                    </pluginExecutions>
                </lifecycleMappingMetadata>
            </configuration>
        </plugin>
    </plugins>
</pluginManagement>

STEP 2: Learn More About This Problem

Eclipse provides a great documentation regarding this issue. You can learn the “what”, “why” and “how” at http://wiki.eclipse.org/M2E_plugin_execution_not_covered

STEP 3: A More Elegant Fix

The problem with the above fix is we clutter our pristine Maven project configuration with an IDE plugin specific configuration that has nothing to do with the project in the first place.

So, the best solution is ditch Eclipse-based IDEs and switch to IntelliJ IDEA… because you either have idea, or don’t.

Maven: Unable to Execute Spock Specs

PROBLEM

When running mvn clean test, Maven Surefire Plugin doesn’t pick up *Spec.groovy test files.

SOLUTION

By default, Maven Surefire Plugin is configured to execute test files with the following patterns: **/Test*.java, **/*Test.java and **/*TestCase.java.

To fix this, we need to modify the inclusion list for this plugin. Since both Java and Groovy files get compiled down to *.class, it is probably easier to just include *.class instead of *.java or *.groovy.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.17</version>
    <configuration>
        <includes>
            <include>**/Test*.class</include>
            <include>**/*Test.class</include>
            <include>**/*TestCase.class</include>
            <include>**/*Spec.class</include>
        </includes>
    </configuration>
</plugin>

Maven + Jetty: Enabling SSL on Jetty Maven Plugin

PROBLEM

In order to run our web application using HTTPS, we need to enable SSL first on Jetty.

SOLUTION #1: Generating Certificate on the Fly

One clean solution by @PascalThivent is to recreate the certificate whenever Jetty starts. Not to steal any credits from him, but here’s a slightly modified configuration using more recent plugin versions:-

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>keytool-maven-plugin</artifactId>
    <version>1.5</version>
    <executions>
        <execution>
            <phase>generate-resources</phase>
            <id>clean</id>
            <goals>
                <goal>clean</goal>
            </goals>
        </execution>
        <execution>
            <phase>generate-resources</phase>
            <id>generateKeyPair</id>
            <goals>
                <goal>generateKeyPair</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>
        <dname>cn=localhost</dname>
        <!--Both `keypass` and `storepass` must be at least 6 characters long. -->
        <keypass>jetty8</keypass>
        <storepass>jetty8</storepass>
        <alias>jetty8</alias>
        <keyalg>RSA</keyalg>
    </configuration>
</plugin>

<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>8.1.14.v20131031</version>
    ...
    <configuration>
        <connectors>
            <connector implementation="org.eclipse.jetty.server.bio.SocketConnector">
                <port>7777</port>
            </connector>
            <connector implementation="org.eclipse.jetty.server.ssl.SslSocketConnector">
                <port>7443</port>
                <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>
                <keyPassword>jetty8</keyPassword>
                <password>jetty8</password>
            </connector>
        </connectors>
    </configuration>
</plugin>

In this case, the generated certificate is stored under the project’s build directory (“target” directory) every time we run mvn clean jetty:run. This is a great solution because it allows me to easily pass my project to my peers without the need to worry about the generated certificate.

However, a downside to this approach is because the certificate is regenerated again and again, my browser keeps prompting me to add an exception whenever I refresh the web link.

… and after adding the exception for like 5 times, it begins to get very annoying.

SOLUTION #2: Generating Certificate Once and Reuse it

Instead of regenerating the certificate again and again, I decided to generate the certificate that will last for 99999 days and store it under src/main/config directory.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>keytool-maven-plugin</artifactId>
    <version>1.5</version>
    <configuration>
        <keystore>${project.basedir}/src/main/config/jetty-ssl.keystore</keystore>
        <dname>cn=localhost</dname>
        <!--Both `keypass` and `storepass` must be at least 6 characters long. -->
        <keypass>jetty8</keypass>
        <storepass>jetty8</storepass>
        <alias>jetty8</alias>
        <keyalg>RSA</keyalg>
        <validity>99999</validity>
    </configuration>
</plugin>

<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>8.1.14.v20131031</version>
    ...
    <configuration>
        <connectors>
            <connector implementation="org.eclipse.jetty.server.bio.SocketConnector">
                <port>7777</port>
            </connector>
            <connector implementation="org.eclipse.jetty.server.ssl.SslSocketConnector">
                <port>7443</port>
                <keystore>${project.basedir}/src/main/config/jetty-ssl.keystore</keystore>
                <keyPassword>jetty8</keyPassword>
                <password>jetty8</password>
            </connector>
        </connectors>
    </configuration>
</plugin>

To generate (or regenerate) the certificate, run mvn keytool:clean keytool:generateKeyPair.

With this approach, I just need to add an exception ONCE in my browser regardless of the number of times I restart Jetty… or when I’m still actively working on this project after 99999 days.

Maven: Deploying Java Sources to Nexus

By default, mvn deploy will only package and deploy class JAR to Nexus. To deploy the source code too, we need to add the following plugin into pom.xml:-

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-source-plugin</artifactId>
    <version>2.2.1</version>
    <executions>
        <execution>
            <id>attach-sources</id>
            <goals>
                <goal>jar</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Now, mvn deploy will deploy both class JAR and source code JAR to Nexus:-

In IntelliJ, when jumping into a class from the JAR, we are given the option to “Download Sources” or “Attach Sources…”.

For some reason, in order for “Download Sources” to work, we need to uncheck “Use Maven3 to import project”.

Now, when we click on “Download Sources”, it will download the sources directly from Nexus and display the source code.

Maven JAR Plugin: Excluding Package(s) from JAR

Let’s assume we want to exclude com.choonchernlim.epicapp.scratch package when we create the JAR file.

Add the following in pom.xml:-

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <executions>
        <execution>
            <id>default-jar</id>
            <phase>package</phase>
            <goals>
                <goal>jar</goal>
            </goals>
            <configuration>
                <excludes>
                    <exclude>**/scratch/**</exclude>
                </excludes>
            </configuration>
        </execution>
    </executions>
</plugin>

The key here is to specify the execution ID called default-jar.