Showing posts with label Maven. Show all posts
Showing posts with label Maven. Show all posts

May 5, 2011

The Butler Dispute, Round 2

Nearly 4 month have passed by since the renaming of Hudson project to Jenkins, which marks the climax of the dispute between the old Hudson developers and the guys from Oracle and Sonatype.

Jenkins has made a great job since then, delivering 15 releases in a weekly schedule. Build number 1.400 was hit in March, wich is not a particular significant release but shows how well things go on. The way from Hudson to Jenkins is as easy as it could be, and it seems like many users are going it.

Indeed there are a lot of reasons why to choose Jenkins over Hudson, just to name a few:

  • Support by the fabulous Hudson core development team – with Kohsuke Kawaguchi, the creator of Hudson, and other brave guys.
  • Strong community activity – measured in figures like commit counts and mailing list traffic, see this post for some numbers.
  • Most of the plugins moved over to Jenkins – 5 of the top 5 and 19 of the top 25 plugins continue primary development with Jenkins, see here for some statistics.
  • High quality and regular releases – the weekly schedule led to 15 high quality releases, each of them providing a couple of bug fixes and new features (see changelog). Moreover, a few weeks ago, Jenkins governance board proposed to start another release line for most stable baselines with a 3-months schedule.

Even the Hudson board seems to have observed that Jenkins outperforms Hudson in many ways, at least they are thinking about "how to make it more attractive for plug-in developers to support both Hudson and Jenkins" (see this message on Hudson-Dev list). The author's perception is that "Hudson also appears to be slowing down development wise" and "another place where Hudson appears to be slowing down, is when you compare changelogs". Some of the ideas deal with copying approaches that are working fine for the Jenkins project.

Hence, it seems Jenkins is the winner of the battle and has in fact benefited from the fork.... until today.

Because today, Oracle submitted a proposal to move Hudson to the Eclipse Foundation. This is, well, somewhat astonishing since that means Oracle will lose both control and the Hudson trademarks – which was the main background of the original dispute with the community.

As part of the proposal, other big players have announced support for the project, including IBM, VMware, Tasktop and Intuit. That means, moving the Hudson project to Eclipse will for sure result in higher attention and more resources (developers).

Does this change anything? Will Jenkins be the unlucky loser, after all? I don't think so. The heavens didn't really smile on Hudson since the fork (kind of bad karma) and I don't see why the move to Eclipse should change that. It's all about people, not code.

Moreover, Jenkins has been invited by Sonatype to reunite with Hudson. But... why should they do that? Jenkins is a vibrant project today, so what is the benefit? Also, there have been some deep disappointments on personal level that are not forgotten yet.

It's going to be interesting!

March 21, 2011

The Way From Hudson To Jenkins

Some time has gone by since the Hudson/Jenkins fork... and there has been even more talk in the community. However, slowly the dust settles, everybody is getting back to business. And finally, we decided to switch from Hudson to Jenkins! This is about why and how.

Why move to Jenkins?

But wait: who has forked, anyways? Is it Jenkins that forked Hudson, or is it Hudson that did the fork of Jenkins? There is some evidence that the community just did a rename of the project (due to trademark conflicts), and after that Oracle forked Jenkins, using the Hudson name they claim holding the trademark on.

You may think this question is a purely theoretical one, but actually it's not. I'll have to legitimate the decision to move to Jenkins to my stakeholders, and using a fork would be a "smell". Project forks are usually not as good as the "original", are possibly done out of selfish reasons, are considered to harm the community etc. Hence, not moving to a fork but instead following the "real" project is a good reason for the move to Jenkins.

An even better one is "project vibrancy", that is the pace of development and level of support provided by the community. This is usually measured by indicators such as the number of commits, the mailing list traffic, the quantity, quality and regularity of releases etc. See this post for such an analysis on commit counts and mailing lists post counts. This is more than four weeks old now and covers not more than two weeks, but nevertheless the result is obvious: Jenkins moves much faster than Hudson does, and community is much more agile. This is confirmed by following the dev mailing lists of both: for Hudson, most of the relevant posts are by either Oracle or Sonatype engineers – seems the Hudson community has become pretty small... Moreover, as this post shows, most of the top plugins will continue primary development under Jenkins.

Last not least, I really respect Kohsuke Kawaguchi (the original creator of Hudson) and what he has done for us. I feel ashamed by how Oracle is dealing with him and the rest of the core team, that's why I have a strong tendency to follow the "good guys" with Jenkins.

As I blogged before, Maven integration is probably one of the most important features of any CI server (at least for me). I guess Sonatype is doing better with Maven integration – it's "The Maven Company", right? – and they are working with Oracle on Hudson. At least, they are putting huge efforts into rock-solid integration. However, after having seen a Sonatype Webinar about their plans with Hudson, I'm not that convinced any more. Current features looked a bit awkward and also does the GWT based UI they are using. So, from my point of view, this point is not yet decided.

Counting it all together, there are some really good reasons to move from Hudson to Jenkins, so we did.

How to upgrade

Now... how do you actually migrate from Hudson to Jenkins? Well, it couldn't be easier. There is a Wiki page about Upgrading from Hudson to Jenkins. To make it short, the involved steps are:


  1. Backup your current installation – just for the good feeling.

  2. Change Update Site: In your Hudson, go to Manage Hudson > Plugin Management > Advanced > Update Site and enter "http://updates.jenkins-ci.org/update-center.json" as URL for Jenkins update site.

  3. Choose to upgrade automatically on Manage Hudson page, just as you did so many times to update Hudson. This will download the new JAR.

  4. Restart Hudson, eh, Jenkins.... and there it is!

That's it. Took less than 5 min! Hudson indeed is a drop-in replacement, so you usually do not have to change anything (environment variables, system properties, start scripts, job configuration etc).

Well, there is only one thing: the name of the WAR file is still hudson.war! Is Oracle aware of this? ;-)

February 27, 2011

The Butler Dispute

I thought it would be time to resurrect my blog, after not having posted for a couple of months. That was mainly because I have been really busy with some interesting stuff I should post about in the future, like Xtext upgrade...

Oracle vs. the Community

But today, I just bumped the ongoing dispute between Hudson and Jenkins guys. We are using Hudson since around 2008, coming from Cruise Control. We really liked the web interface, being able to setup everything by just using your browser. Of course, the features also have been impressive since then. Setting up a build farm is just fun with Hudson.

You probably know that there has been a fork of Hudson which is named Jenkins (others say Hudson has been renamed to Jenkins and then forked into Hudson). This all started with the Hudson team being unhappy with the infrastructure provided by java.net (which is driven by Oracle since Sun acquisition), due to its poor reliability. The community talked about moving parts of the project to other servers, and first candiate was issue tracking. Suddenly, the project is locked due to the migration of java.net projects to new Kenai infrastructure, which was announced by Oracle but somehow missed by the project owners. Frustrated by the migration, the community decides to move code to GitHub and mailing list to Google Groups. See "Who's driving this thing?" for the facts.

This is the point where Oracle steps in, claiming to have a trademark on the name. If the project decides to move, it must be using another name: "Because it is open source, we can't stop anybody from forking it. We do however own the trademark to the name so you cannot use the name outside of the core community. We acquired that as part of Sun." (BTW, that might not be true after all). Later he stated that "the final decision of what to do w.r.t. infrastructure belongs to Oracle".

Guess what: this really concerned the community. There have been some talks between key community members and Oracle representives, in an attempt to agree on a "proposal for a stable structure and arrangement" which later would be proposed to the community. But, with no success. That finally led to the decision of the community to move to GitHub and at the same time rename the project to another butler's name: "Jenkins".

Of course, Oracle's view on the subject is a bit different...

Welcome Jenkins!

Well, so now you have the choice: use Hudson, or use Jenkins. You know, competition is usually a good thing, so let the race begin. The majority of the community seems to have made the switch to Jenkins (given the blogs and mailing list traffic). This is because Oracle's behavior is not quite understood and does not cast a positive light on their comprehension of Oracle's role in the Hudson community.

However, Oracle is putting enormous resources (people and hardware) into the Hudson project. And what's even more important, Sonatype is helping to drive Hudson to the next level. Sonatype? Right, that's the company behind Maven and all the great Maven tools like Nexus and m2eclipse.

Maven Support – the Killer Feature?

One of the most important features (for me, but also for possibly the majority of other users) is Maven 3 support. Sure, Hudson/Jenkins already support Maven 3 since version 1.392 (end of 2010, see changelog). But hey, Sonatype entered the scene, and they will surely do better.

Sonatype, too, have put some full-time engineers into the Hudson project, making sure that "Hudson users can look forward to a long, bright future". See this or this post. Sonatype in the end of the day wants to earn money with Hudson (and Maven), so I expect to see outstanding features related to Maven 3 support, Eclipse integration and workflow extensions for Hudson. See here for some of their ideas.

Well, this really makes a thrilling game. I honestly appreciate what Kohsuke Kawaguchi and others have built up with Hudson from the ground up, and would like to see them win on the "evil company that pushed them out of the project". And by the way, Sonatype seems to be in good companion when talking about being evil – they removed the oldest commiter of m2eclipse from the project a year ago.

So, is this again the good vs. evil story? I don't know. In the end, both projects will have their users. And they will learn and benefit from each other. So let's wait and see.... Time will tell. It's going to be an interesting year, though!

October 15, 2010

Maven 3 and Plugin Mysteries

You probably know that Maven 3 has landed. Before testing it with our projects, I was curious about the plugins that are defined in the Maven master POM's pluginManagement section and hence are locked-down with respect to their version. Since all projects inherit from this master POM, they will use the respective version of the plugins if not explicitely overwritten anywhere in the project's POM hierarchy.

Maven 3 is a bit more strict concerning automatic version resolution of invoked plugins. Other than Maven 2, it will always use the latest release (i.e. non-SNAPSHOT) version of a plugin if there was no explicit version specified in the POM or on the command line. Moreover, it will issue a warning when missing plugin versions are detected "to encourage the addition of plugin versions to the POM or one of its parent POMs". This is to increase reproducability of builds.

Thus, in Maven 3 the desired build stability is ensured by urging the POM author to give explicit plugin versions, and doesn't any more rely on a full list of plugins (with versions) defined in the master POM. That's why I expected to find a small or even empty pluginManagement section. Well, let's see.

To find out what's in the pluginManagement of master POM, you just have to create a minimal POM and show the effective POM (that results from the application of interpolation and inheritance, including master POM and active profiles) by calling help:effective-pom for this simple project.

So, what do we get? The following list shows the plugin versions that are defined in the Maven 2.2.1 master POM, the Maven 3 master POM, as well as the most recent version of those plugins.

Well, we can see some interesting details here:

  • The number of plugins defined in the master POMs pluginManagement section is drastically less for Maven 3 than for Maven 2.2.1 – that's what we expected. However, there are still a few.

  • Which pluings are listed and which are not? It seems like the plugins for the most basic lifecycle phases (like clean, install, deploy) are predefined, but others are not (like compile or jar). Is there any policy?

  • What is really odd: for some of the plugins that are predefined, there is a newer version available than is listed in the Maven 3 master POM (colored red). Why could that be? I have not checked, but Maven 3 is out for a few days now, so I suspect for most of those plugins the new version has been available before. Is that intentionally? Are the new versions not considered "good" or "stable" by the Maven guys? Or did they just forgot to upgrade? Or did not found it important in any kind?

  • Another thing I can't explain: when you look on the Maven 3 Project Plugin Management site, there are listed a lot more plugins, and some are even of other version than what we got by showing the effective POM for a minimal project POM. How could this be? I have no clue...

In a previous post, I have listed the plugins predefined by Maven 3.0-alpha5. Interestingly, there have been a lot more of them (like for Maven 2.2.1), but the "stale version" question was the same...

October 13, 2010

World of Versioning

Today, we had a discussion on how to name a hotfix release of our framework product, built with Maven (you knew I'm a fan of Maven, didn't you?). It's a very basic question, but still an interesting one and it opens a whole universe of ideas, opinions and rules...

The previous versions of our product had been named like this:

1.3.0. 1.3.1 ... 1.4.0, 1.4.1, ... 1.5.0, 1.5.1, 1.5.2, ... 1.5.6

They all are based on a release plan and contain bugfixes as well as improvements and new features. For each of those versions, we have written release notes and built a site.

Now, what do we do when there is the need to release a bugfix version of a regular release we built a few days ago? There are some options:

  1. 1.5.7 – i.e. increment last number; however, this doesn't seem to fit well because the bugfix release is of another character than standard releases
  2. 1.5.6.1 – i.e. add an additional numerical identifier
  3. 1.5.6.a – i.e. add another non-numerical identifier
  4. 1.5.6-patch1 – i.e. add another qualifier describing it's actually a patch release

When searching the Net for version number rules in the Maven world, you'll stumble upon the DefaultArtifactVersion class in the core of Maven which expects that version numbers will follow a specific format:

<MajorVersion [> . <MinorVersion [> . <IncrementalVersion ] ] [> - <BuildNumber | Qualifier ]>

Where MajorVersion, MinorVersion, IncrementalVersion and BuildNumber are all numeric and Qualifier is a string. If your version number does not match this format, then the entire version number is treated as being the Qualifier (see Versions Maven Plugin).

This means, options 1 and 4 of above would be a viable alternative in the Maven world. However, note that there is some discussion about this Maven schema. It suffers from inconsistent/unintuitive parsing, lexically sorting of qualifiers and some other flaws. This would yield to unexpected comparison results especially when using Maven SNAPSHOT versions. The Proposal given on that page seems to be integrated with Maven 3.

Actually, we wouldn't have this discussion if the third level would not be named Incremental version in Maven world, but rather bugfix version or patch version. There is a Semantic Versioning Specification (SemVer) that recommends this version schema:

A normal version number MUST take the form X.Y.Z where X, Y, and Z are integers. X is the major version, Y is the minor version, and Z is the patch version. Each element MUST increase numerically. For instance: 1.9.0 < 1.10.0 < 1.11.0.

There are some rules describing when to increase which part. The main idea is to use the first numerical (major version) to indicate backwards incompatible changes to the public API, and in contrast the last numerical (patch version) suggests that only backwards compatible bug fixes have been introduced.

This SemVer schema is fully compatible with Maven (regardless of SNAPSHOT versions). If we had used this, we would probably have ended up in a "higher" version number like 5.4.0, but now the upcoming patch would have the version number 5.4.1 without any consideration.

By the way, a lot of public recommendations for software versioning follow this <major>.<minor>.<patch> schema. See this question and Wikipedia for more information on Software Versioning.

So. What do we do now? We'll release a version 1.5.6-patch1 for the patch, but think about changing our versioning according to SemVer, i.e. to upgrade the major number when introducing incompatible changes, and the minor number in most other cases.

March 26, 2010

Having Fun with Encoding!

Compiler Plugin

Recently, I edited our main company root POM to upgrade some plugins to new versions. Of course, we are following best practice to lock down the plugin version, so when a new version is available we only need to adjust the parent POM. Nearly all version updates were on the last build number digit, which is the z in x.y.z version string – so I didn't expect much difficulties.

However, for the compiler plugin, it was a jump from version 2.0.2 to 2.1, and indeed it turned out that some of the test cases failed compiling with strange encoding issues when using the new compiler plugin version.

Specify Encoding

We are following the suggestion to specify a POM property for source file encoding, for not being forced to configure encoding for all relevant plugins individually. Moreover, we were exactly using what's shown in the example:

<project>
...
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
...
</properties>
...
</project>
That is, we assumed our source files were UTF-8 encoded, which is the most widely used encoding for unicode characters. But, for some of the projects, that's actually not the case since we are using Eclipse with the default setting for text file encoding which is Cp1252 (Western European) on our german Windows.

Why didn't we ever notice that? Well, it happens that both the UTF-8 as well as Cp1252 encodings are backwards compatible with ASCII. We are coding most of the stuff in english (concerning package, class, method, attribute and parameter names, and even Javadoc comments), so the resulting byte stream will never be different for both encodings. However, some of the files used german umlauts in line comments which are exactly the files that can't be compiled any more with new compiler plugin version.

When looking at the debug output of compiler plugin 2.0.2 mojo configuration, you can see that the encoding is not explicitely set, probably meaning that the platform default encoding is used (which is again Cp1252 on all build machines):

[DEBUG] Configuring mojo 'org.apache.maven.plugins:maven-compiler-plugin:2.0.2:compile' -->
[DEBUG] (f) basedir = ...
[DEBUG] (f) buildDirectory = ...
[DEBUG] (f) classpathElements = [...]
[DEBUG] (f) compileSourceRoots = [...]
[DEBUG] (f) compilerId = javac
[DEBUG] (f) debug = true
[DEBUG] (f) failOnError = true
[DEBUG] (f) fork = false
[DEBUG] (f) optimize = true
[DEBUG] (f) outputDirectory = ...
[DEBUG] (f) outputFileName = xxx-0.2.0-SNAPSHOT
[DEBUG] (f) projectArtifact = xxx:jar:0.2.0-SNAPSHOT
[DEBUG] (f) showDeprecation = false
[DEBUG] (f) showWarnings = false
[DEBUG] (f) source = 1.6
[DEBUG] (f) staleMillis = 0
[DEBUG] (f) target = 1.6
[DEBUG] (f) verbose = false
[DEBUG] -- end configuration --

The new version 2.1 of compiler plugin is now considering what has been configured in project.build.sourceEncoding property, and hence tries to compile the Cp1252 coded source file with UTF-8 encoding which doesn't work when umlauts are used.

Specify Correct Encoding

Of course, the solution is to specify the correct encoding in project.build.sourceEncoding property, matching the encoding that is used in the development environment when writing the source files.

Oh, yes, Cp1252 is quite similar to ISO 8859-1 encoding (only some special characters on positions 0x80–0x9F are different which we don't use), so in fact we are using ISO 8859-1 now to allow builds on non-Windows platforms as well.

Certainly, it would be nice if the plugins had a history on their site where you can find this type of changes for new versions, without having to search in the Jira...

January 13, 2010

Concurrent Builds with Hudson

Multiple Build Executors

We are using the Hudson Continuous Integration Server for our integration builds and are quite happy with it. It is fast, stable, feature-rich, extensible, well integrated with Maven and has an appealing user interface.

One of the nice features that we are using regularly is the Build Executor setting that allows you to specify the number of simultaneous builds. This is useful to increase throughput of Hudson on multi-core processor systems, where the number of executors should (at least) match the number of available cores.

However, Maven isn't really designed for running multiple instances simultaneously since the local respository isn't multi-process safe. The chance for conflicts seems small (multiple processes must access the same dependency at the same time, at least one of them writing). However, in praxis, we encounter this type of concurrency issue at least once a day now, which is starting to hurt us! The build is failing with a message like this:

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Failed to resolve artifact.

GET request of: some/group/some-artifact-1.2.3-SNAPSHOT.jar from my-repo failed
some.group:some-artifact:jar:1.2.3-SNAPSHOT
...

Caused by I/O exception: ...some-artifact-1.2.3-SNAPSHOT.jar.tmp (The requested operation cannot be performed on a file with a user-mapped section open)

or this:

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Failed to resolve artifact.

Error copying temporary file to the final destination: Failed to copy full contents from ...some-artifact-1.2.3-SNAPSHOT.jar.tmp to ...\some-artifact-1.2.3-SNAPSHOT.jar

The reason is, the JAR file is locked by another process that is executing some long-lasting test cases, for instance. At the same time, a second build tries to download a new version of this snapshot into the local repository, which is done with the help of the mentioned .tmp file.

Safe Maven Repository

The only way to avoid this type of issue is to use separate local Maven Repositories for each of the processes. You can tell Maven to use a custom local repository location by specifying the localRepository setting in your settings.xml file.

In Hudson, this is even more convenient. There is a checkbox Use private Maven repository in the advanced part of the Build section of Maven projects. Just click that to setup a private local Maven repo for that project. You should consider to do so when you run into the described issue now and then.

Obviously using private repos will increase the total amount of disk space due to caching the same dependencies in multiple places. Additionally, the first build will take significantly more time because everything has to be downloaded once. However, both consequences are well acceptable given the better stability and isolation of projects.

Instead of clicking the Hudson checkbox for all your projects, you should consider to setup the local Maven repo in your settings.xml instead. This has a number of advantages:


  • You don't have to setup the option for each and every project, but have it in a central place.
  • You can use a common root for all local Maven repos, like d:/maven-repo. This allows you to easily purge all your local repositories from time to time, in order to reduce disk space as well as validate the content (i.e. make sure the build is still running in a clean environment and all required artifacts are in your corporate Maven repository).

For instance, here is what works fine for us:

<localRepository>d:/builds/.m2/${env.JOB_NAME}/repository</localRepository>

This is using a Hudson environment variable (JOB_NAME) to create subfolders for the actual projects aka jobs. See here for a list of available variables.

Oh yes, what I suggest is also encouraged by Brian Fox in his Maven Continuous Integration Best Practices blog post, so you should consider twice to adopt this best practice :o)

January 2, 2010

Cargo Maven Plugin: Not Made for JBoss

Again...

Well, actually this blog was supposed to be about Java in general and all the ups and downs I experience during my daily work. However, I've not been doing much other than Maven configuration and build management lately, so here is another Maven related post. Sorry folks.

As already shown in this post, I have been doing integration tests with JBoss by using the Cargo Maven plugin to start the JBoss locally and deploy the application to it. This all works quite as soon as you have figured out how to configure Cargo for JBoss.

But Remotely Now!

Now, the next step is to deploy our EAR file which is generated during nightly build to a running JBoss instance on a separate computer. This is different because no JBoss configuration has to be created locally and no JBoss has to be started. Instead, the EAR file must be transferred to a remote server where JBoss is already running, and JBoss must be persuaded to deploy this file.

That sounds feasible, and I've done exactly this before for other servers like Tomcat, so I did not expect any issue here. However, I was wrong.

Itch #1

First trouble was caused by my lack of knowledge regarding JBoss. With standard installation, you are not able to connect to the server remotely and all the services are bound to localhost only (see here or here). This is intentionally, to prevent unprotected installations appearing all over the net. You have to pass the option -b 0.0.0.0 when starting JBoss to allow remote connections to the services, but take care to secure your JBoss accordingly!

Itch #2

Okay, after this has been configured, I tried to use Cargo to deploy my EAR file to JBoss. This is the configuration I ended up with:

<!-- *** Cargo plugin: deploy the application to running JBoss *** -->
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.0</version>
<configuration>
<wait>false</wait>
<!-- Container configuration -->
<container>
<containerId>jboss5x</containerId>
<type>remote</type>
</container>
<!-- Configuration to use with the Container -->
<configuration>
<type>runtime</type>
<properties>
<cargo.hostname>...</cargo.hostname>
<cargo.servlet.port>8080</cargo.servlet.port>
</properties>
</configuration>
<!-- Deployer configuration -->
<deployer>
<type>remote</type>
<deployables>
<deployable>
<location>...</location>
</deployable>
</deployables>
</deployer>
</configuration>

<executions>
<execution>
<id>deploy</id>
<phase>deploy</phase>
<goals>
<goal>deployer-redeploy</goal>
</goals>
</execution>
</executions>
</plugin>

However, I always got this error message:

[INFO] Failed to deploy to [http://...]
Server returned HTTP response code: 500 for URL: ...

The configuration seems to be correct, so what is the problem?

After asking Google, I realized that Cargo is not able to transfer a file to JBoss! Instead, it requires the deployable to be deployed to be present on the server filesystem (see here). This is obviously caused by the JBoss JMX deployer which is used by Cargo, but actually you don't care who is to blame – you just want it to work. The name "Cargo" implies the parcel is transferred to its destination, right? Also note that this issue is dated from Sep 2006, so there has been some time to fix it in either way.

What Can We Do?

Well, there are probably not many options. Since current version of Cargo is not able to transfer the file to the server, you'd have to do this on your own. The location given in our Cargo configuration above actually is the path on the JBoss server. So, when the file exists locally on the JBoss server, Cargo should be able to deploy it successfully.

For transferring the file to JBoss server, we could use the maven-dependency-plugin, a quite useful plugin for all kind of analyzing, copying or unpacking artifacts. We configure it to run in pre-integration-test phase and to copy the EAR file (produced by this POM) to some temp directory on the JBoss server:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>install</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<type>${project.packaging}</type>
<destFileName>test.ear</destFileName>
</artifactItem>
</artifactItems>
<outputDirectory>${publish.tempdir}</outputDirectory>
<overWrite>true</overWrite>
</configuration>
</execution>
</executions>
</plugin>

The property ${publish.tempdir} can be anything on the JBoss server (which must be available in the network!) and is exactly what has to be used for the value of location element in Cargo configuration.

Another option would be to use the hot-deploy directory of JBoss as outputDirectory for the dependency plugin, and hence rely on hot deployment of JBoss instead of Cargo and JBoss JMX deployer. This way, we could get rid of Cargo configuration and cleanup the POM a bit, but in the end it seemed a bit less clean to me... your mileage may vary.

So, as always, in the end we got it to work, but not without unforeseen pain. When will Cargo be fixed to get the EAR file to JBoss server? Who knows.

Updates

2010/01/22: Note that the dependency plugin must be bound after the install phase so that the artifact has been copied at least to your local Maven repository. As a consequence, the Cargo plugin must be run in deploy phase, which is actually a good choice anyways. I have changed this in my code above.

December 10, 2009

Maven Plugins: Upgrade with Care!

Upgrading Maven Plugins: Tips and Issues

After having shown the list of current Maven plugin versions in my previous post, now I'm going to share my experiences with upgrading. Just like expected, some of the new plugins did not work out of the box or required some changes in configuration:

maven-checkstyle-plugin

We have used version 2.3 previously which is based on Checkstyle 4.4. In contrast, plugin version 2.4 is eventually built on top of Checkstyle 5 which better supports Java 5 language features (I wrote a post on that issue). The configuration is not fully compatible, so you would have to upgrade it.

maven-javadoc-plugin

Starting with version 2.6, JavaDoc plugin can detect the Java API link for the current build. This did not work for us (probably due to missing proxy configuration), so we had to switch it off by setting detectJavaApiLink property to false. See plugin site.

taglist-maven-plugin

The configuration of tags has changed and is now a bit more extensive. Old format (using tags element) is still supported, but deprecated. See plugin documentation.

findbugs-maven-plugin

I have used version 2.2 which was the most recent one when changing the POM. However, this yielded some strange errors during site generation. This is the strack trace:

Generating "FindBugs Report" report.
Plugin Artifacts to be added ->...
AuxClasspath is ->D:\Profiles\Default User\.m2\repository\org\apache\maven\reporting\maven-reporting-impl\2.0\maven-reporting-impl-2.0.jar;...
[java] Exception in thread "main" java.io.FileNotFoundException: D:\builds\...\User\.m2\repository\...\maven-reporting-impl-2.0.jar;D:\Profiles\Default (The filename, directory name, or volume label syntax is incorrect)
[java] at java.util.zip.ZipFile.open(Native Method)
[java] at java.util.zip.ZipFile.<init>(ZipFile.java:114)
[java] at java.util.zip.ZipFile.<init>(ZipFile.java:131)
[java] at edu.umd.cs.findbugs.classfile.impl.ZipFileCodeBase.<init>(ZipFileCodeBase.java:53)
[java] at edu.umd.cs.findbugs.classfile.impl.ZipCodeBaseFactory.countUsingZipFile(ZipCodeBaseFactory.java:92)
[java] at edu.umd.cs.findbugs.classfile.impl.ZipCodeBaseFactory.makeZipCodeBase(ZipCodeBaseFactory.java:46)
[java] at edu.umd.cs.findbugs.classfile.impl.ClassFactory.createFilesystemCodeBase(ClassFactory.java:97)
[java] at edu.umd.cs.findbugs.classfile.impl.FilesystemCodeBaseLocator.openCodeBase(FilesystemCodeBaseLocator.java:75)
[java] at edu.umd.cs.findbugs.classfile.impl.ClassPathBuilder.processWorkList(ClassPathBuilder.java:564)
[java] at edu.umd.cs.findbugs.classfile.impl.ClassPathBuilder.build(ClassPathBuilder.java:195)
[java] at edu.umd.cs.findbugs.FindBugs2.buildClassPath(FindBugs2.java:584)
[java] at edu.umd.cs.findbugs.FindBugs2.execute(FindBugs2.java:181)
[java] at edu.umd.cs.findbugs.FindBugs.runMain(FindBugs.java:348)
[java] at edu.umd.cs.findbugs.FindBugs2.main(FindBugs2.java:1057)
[java] Java Result: 1

As you can see, we are using Windows default for local Maven repository (D:\Profiles\Default User\.m2) which obviously is causing a problem with the path later on. How sick is that?!?

Then, after having tried this and that, I discovered there is a brand new version 2.3 available so I tested that one and guess what – everything works fine again! Hence, don't use version 2.2, it seems to be broken...

docbkx-maven-plugin

The latest version of that plugin is 2.0.9, but that did not work correctly. It failed the build with this error:

ValidationException: null:30:723: Error(30/723): fo:table-body is missing child elements.
Required Content Model: marker* (table-row+|table-cell+)

The given line/position information did not match to anything suspicious, and I could not see anything wrong in our XSL files. So, no idea what this issue is telling me or what could I do about it. That's why I rolled back to 2.0.8 which just works fine.

The Bottom Line

With the given plugin versions, we are up to date again and managed to get rid of some issues hitting us since quite some time. Additionally, we are well prepared for upgrading to Maven 3. I hope I will be able to do so soon, to check if this is really a "drop-in replacement"... I will let you know!

Maven Plugins: Current Versions

Upgrading Maven Plugins

In preparation for a later switch to Maven 3 (which is already knocking on the door) as well as to get rid of some plugin related issues we are suffering from, I decided to update the Maven plugins we use for build and site generation.

Of course, we are following best practice and are locking down the version of all plugins in project.build.pluginManagment.plugins section. This is done in the company's topmost POM, so that all company projects would use the same versions once they reference the latest parent POM.

As you might know, upgrading to new plugin versions is always an adventure and you have to test your builds seriously, which is of course hard when you are going to change the company settings...

Build Plugins

Well, here is the list of build plugins we now use with their current version, as well as (in brackets) the version that Maven 3.0-alpha5 defines in its internal POM. I have highlighted where both versions differ:

  • maven-archetype-plugin: 2.0-alpha-5 (see comment below)
  • maven-assembly-plugin: 2.2-beta-4 (2.2-beta-4)
  • maven-clean-plugin: 2.3 (2.3)
  • maven-compiler-plugin: 2.0.2 (2.0.2)
  • maven-dependency-plugin: 2.1 (2.0)
  • maven-deploy-plugin: 2.4 (2.4)
  • maven-ear-plugin: 2.4 (2.3.1)
  • maven-ejb-plugin: 2.2 (2.1)
  • maven-enforcer-plugin: 1.0-beta-1
  • maven-help-plugin: 2.1 (2.1)
  • maven-install-plugin: 2.3 (2.3)
  • maven-javadoc-plugin: 2.6.1 (2.5)
  • maven-jar-plugin: 2.3 (2.2)
  • maven-release-plugin: 2.0-beta-9 (2.0-beta-9)
  • maven-resources-plugin: 2.4.1 (2.4.1)
  • maven-site-plugin: 2.0.1 (2.0.1)
  • maven-source-plugin: 2.1.1 (2.0.4)
  • maven-surefire-plugin: 2.4.3 (2.4.3)
  • maven-war-plugin: 2.1-beta-1 (2.1-alpha-1)
  • build-helper-maven-plugin: 1.4
  • failsafe-maven-plugin: 2.4.3-alpha-1
  • cargo-maven2-plugin: 1.0
  • docbkx-maven-plugin: 2.0.8

It's a bit strange that Maven 3.0-alpha5 (which came out end of November) does not use the latest version of all those plugins, most of them having been released before that date. I don't know if this was intentional or not... Let's hope it's not because of unsure quality of latest plugin versions ;-) Anyways, I decided to upgrade to the latest available version for all plugins.

Reporting Plugins

Here's the list for plugins related to site reports:

  • cobertura-maven-plugin: 2.3
  • findbugs-maven-plugin: 2.3
  • jdepend-maven-plugin: 2.0-beta-2
  • maven-checkstyle-plugin: 2.4
  • maven-jxr-plugin: 2.1
  • maven-pmd-plugin: 2.4
  • maven-project-info-reports-plugin: 2.1.2
  • maven-surefire-report-plugin: 2.4.3
  • taglist-maven-plugin: 2.4

I'm going to show some tips and issues when upgrading to these versions in an upcoming post...

December 2, 2009

Unit and Integration Testing with Maven, Part 2

Welome Back...

... to the second installment of this little series. After having seen the requirements and hassles when using Maven for testing in the last post, we are now going to list the possible solutions.

Remember, Maven does support different phases for unit and integration tests, but there is only one source directory (usually src/test/java), making it a bit difficult to setup and organize test environments. Hence, we have to use some way to separate both types of test.

Option 1: Separate Module for Integration Tests

In case you are using modules for your project anyways and want to have integration tests which are testing these modules in integration, this is the natural solution: just create another module that depends on the other ones and only contains the integration test sources and resources.

However, if you instead want to integration-test each module individually, this approach just doubles the number of modules, which can be difficult to manage. You could put all integration tests into one big module – but that has other disadvantages, of course.

Since we do not have any sources (only test sources) in that integration-test project, the usual build lifecycle would execute a lot of unneeded steps like copying resources, compiling, testing, creating JAR file etc. We could use the POM packaging type to suppress all of this, but then we have to configure the maven-compiler-plugin to force compilation of test sources.

Independantly of packaging type, we have to do some more configurations:

  • Execute Surefire plugin in integration-test phase.
  • Prepare integration tests (for instance, start the container and deploy the application) in pre-integration-test phase. This is quite easy with Cargo Maven plugin.
  • Shutdown integration tests (stop the container) in post-integration-test phase.

Here is the relevant part of such a POM file:

<build>
<plugins>

<!-- *** Compiler plugin: we must force test compile because we're using a
pom packaging that doesn't have this lifecycle mapping. -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- *** Surefire plugin: run integration tests *** -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- *** Cargo plugin: start/stop application server and deploy the ear
file before/after integration tests *** -->
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.0</version>
<configuration>
...
</configuration>

<executions>
<!-- before integration tests are run: start server -->
<execution>
<id>start-container</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<!-- after integration tests are run: stop server -->
<execution>
<id>stop-container</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>
</build>

Option 2: Different Source Directories

In this scenario, unit and integration test sources are placed in separate source directories, like src/test/java and src/integrationtest/java. I think this would definitely be the best solution, and it actually should be what Maven supports out of the box. Sadly, Maven does not, and as far as I know Maven 3 won't either :-(

Well, we should be able to configure things this way. For compiling and executing integration tests, you would have to configure Compiler plugin to compile the integration test sources in pre-integration-test phase (using the includes parameter), and then configure a second Surefire execution using testClassesDirectory parameter to point it to the integration test folder.

However, this option seems a bit fragile to me. Even if it works (I haven't checked), the integration test folder would probably not show up in Eclipse when using m2eclipse plugin and other plugins may have issues with this additional test source path as well. That's why I do not recommend this option.

Option 3: Different File Name Patterns

In this scenario, the package or file name is used to distinguish between unit and integration test source files. Let's say, for instance, that all integration test classes start with IT* and hence the filename pattern is **/IT*.java. We'd have to configure the Surefire plugin to execute twice: once in test phase for executing the unit tests only, and another time in integration-test phase to execute, well, the integration tests (and only those)

A common way to do so is this:

  • In configuration of Surefire plugin, set skip parameter to true to bypass all tests.
  • Add an execution element for unit tests, where skip is set to false and exclude parameter is used to exclude the integration tests.
  • Add another execution element for integration tests, where skip is set to false and include parameter is used to just include the integration tests and nothing else.
  • Prepare and shutdown of integration tests are like before.

Here is the POM section:

<build>
<plugins>

<!-- *** Surefire plugin: run unit and integration tests in
separate lifecycle phases, using file name pattern *** -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>unit-test</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<excludes>
<exclude>**/IT*.java</exclude>
</excludes>
</configuration>
</execution>

<execution>
<id>integration-test</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<includes>
<include>**/IT*.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>

<!-- *** Cargo plugin: start/stop application server and deploy the ear
file before/after integration tests *** -->
...

</plugins>
</build>

There is another approach to achieve the same result:

  • In configuration of Surefire plugin, use the exclude parameter to exclude the integration tests when executing unit tests in test phase.
  • Add an execution element for integration tests, where exclude is set to any dummy value (to override the default configuration) and include parameter is used to just include the integration tests and nothing else.

It's a bit shorter than the former configuration, but in the end it's a matter of taste. Again, here is the POM snippet:

<build>
<plugins>

<!-- *** Surefire plugin: run unit and integration tests in
separate lifecycle phases, using file name pattern *** -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/IT*.java</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/IT*.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>

<!-- *** Cargo plugin: start/stop application server and deploy the ear
file before/after integration tests *** -->
...

</plugins>
</build>

Failsafe Plugin

Instead of using the Surefire plugin with custom filename pattern to distinguish between unit and integration test classes, you should rather use the Failsafe plugin for executing integration tests.

This plugin is a fork of the Surefire plugin designed to run integration tests.
It is used during the integration-test and verify phases of the build lifecycle to execute the integration tests of an application. Other than Surefire plugin, the Failsafe plugin will not fail the build when executing tests thus enabling the post-integration-test phase to execute.

Failsafe plugin has its own naming convention. By default, the Surefire plugin executes **/Test*.java, **/*Test.java, and **/*TestCase.java test classes. In contrast, the Failsafe plugin will look for **/IT*.java, **/*IT.java, and **/*ITCase.java. Did you note that this matches what we used before for our integration tests? ;-)

When using Failsafe, the last POM (of option 3) looks like this:

<build>
<plugins>

<!-- *** Surefire plugin: run unit and exclude integration tests *** -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/IT*.java</exclude>
</excludes>
</configuration>
</plugin>

<!-- *** Failsafe plugin: run integration tests *** -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>failsafe-maven-plugin</artifactId>
<version>2.4.3-alpha-1</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- *** Cargo plugin: start/stop application server and deploy the ear
file before/after integration tests *** -->
...

</plugins>
</build>

Of course, Failsafe would also work with first option (separate integration test module).

Conclusion

Well, after having shown all the ways that came into my mind, here are my personal "best practices":

  • Use the Failsafe plugin to execute integration tests.
  • If keeping integration tests in a separate module feels alright, do so (see option 1).
  • If you want to have unit and integration tests in the same module, choose a file or package name pattern to distinguish between both, and configure Surefire and Failsafe plugins accordingly.

Update (2010/02/18)

Note that there is a new version 2.5 of failsafe plugin available with changed group id: org.apache.maven.plugins:maven-failsafe-plugin:2.5. See the plugin site for details. Thanks to stug23 for pointing that out!

November 28, 2009

Unit and Integration Testing with Maven, Part 1

Test Types

In my last post, I talked about integration testing with Maven's Cargo plugin and JBoss Application Server. Now let's see how integration testing fits into an overall testing strategy with Maven.

When thinking about tests and testing strategies, there is one important thing to keep in mind: integration tests are not unit tests (even though JUnit may be used to write integration tests). For the sake of completeness, let's pin down the main characteristics of both:

Unit Tests:

  • are testing a small piece of code in isolation
  • are independant from other tests
  • are usually written by software developers
  • have to be very fast because they are run quite often

In contrast, Integration Tests:

  • individual software modules are combined and tested as a group
  • are usually much slower than unit tests because a context has to be established (Spring, database, web server etc.)
  • normally are run after unit testing
  • may be created by QA team using tools, but also by developers using JUnit test cases (which still does not turn them into unit tests)

Testing with Maven

As you know, Maven Build Lifecycle provides phases both for testing and integration testing. Related phases are:

...
generate-test-sources
generate-test-resources
process-test-resources
test-compile
process-test-classes
test
...
pre-integration-test
integration-test
post-integration-test
...

Unfortunately, Maven does not support separate source directories for both test types. This is really complicating things (as we'll see in a minute). There have been some discussions on how to fix that, but I don't think it made it into Maven 3 (not quite sure, though).

Hence, what we need to have is this:

  • Both unit and integration tests have to be compiled by javac.
  • Nevertheless, they should be clearly separated from each other by a path or file name pattern.
  • Unit tests have to be run in test lifecycle phase, and the build should stop if they do not succeed.
  • Integration tests should be run in integration-test phase, and at least the post-integration-test phase has to be run independantly of test result (to be able to shutdown a running container, close database connections etc.).

The next part of this small series will show how these requirements can be met with Maven and what has to be configured, so please stand by...

November 27, 2009

Integration test with Maven, Cargo and JBoss

It's Friday...

... and I thought it would be a good idea to setup an integration test of some EJBs we are creating in a new project. Actually, it was not, since it nearly ruined my evening. But eventually I got it to work, and here is how.

To test the EJBs, I need to create an EAR file, deploy that to the application server (JBoss 5.1.0 in our case) and run JUnit test cases against this server. For Maven, I have setup a separate integration-test module for executing the integration test for the following reasons:

  • to better separate unit tests (with JUnit) from integration tests (also with JUnit), which simplifies Maven configuration a bit.
  • to be able to run this module outside of the normal CI (continuous integration) build due to its lack of performance.

Cargo Maven Plugin

For automatically starting the container, deploying the EAR file and stopping the container when the tests are finished, I use the Cargo Maven plugin. I did this several times before (with Tomcat, though) – so I thought that'd be easy...

Well, when using JBoss, there are some tricks you have to know. Before going into the details, some more information on Cargo.

A Container is the base concept in Cargo. It represents an existing application server runtime, and Cargo hides all the details of the actual server implementation for you. There are two types of containers:

  • Local Container: this is executing on the machine where Cargo runs. This can either be an Installed Container which is, well, installed on the local machine and is run in a separate VM, or an Embedded Container that is executing in the same JVM where Cargo is running (currently only supported for Jetty).
  • Remote Container: a container that is already running anywhere (local or remote). It's not under Cargo's control and can't be started or stopped by Cargo.

You use a Configuration to specify how the container is configured (logging, data sources, location where to put the deployables, etc). The available configuration depends on the container type:

  • Local Configuration: for local containers. There are two local configuration types: Standalone Local Configuration which configures the container from scratch in a directory of your choice, and Existing Local Configuration that re-uses an existing container installation already residing on your hard drive.
  • Runtime Configuration: You use a runtime configuration when you want to access your container as a black box through a remote protocol (JMX, etc). This is perfect for remote containers.

In my case, I wanted to use a Local Installed Container with a Standalone Local Configuration, to eliminate dependencies from other deployments.

JBoss with Cargo

Well, and here are the pitfalls when using JBoss in this setting:

  1. Experimental: JBoss 5.x is still an experimental container for Cargo. This is a bit strange given the fact that this version is now out for a while, but fortunately not really an issue.
  2. Extensive Logging: When Cargo builds the JBoss configuration – remember, I use Standalone Local Configuration so Cargo creates one from scratch – it uses a logging setup (independantly from what is used with your JBoss installation!) that is way too chatty. The console scrolls forever, and things are slowing down in a way that you think everything is stuck in an infinite loop.
    Thus, you have to tell Cargo to use another logging configuration file, which is a bit tricky and not documented very well (see this issue).
  3. Shutdown Port: Now the container starts up, the tests are run, but after that JBoss AS is not shutting down. It's telling me javax.naming.CommunicationException: Could not obtain connection to any of these urls: localhost:1299, which means the wrong port is used for shutdown. Standard shutdown port is 1099, so we have to tell Cargo to use that port number.

All in all, the configuration now looks like this. Mentioned settings are highlighted. Perhaps this is useful for someone else...


<!-- *** Cargo plugin: start/stop JBoss application server and deploy the ear
file before/after integration tests *** -->
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.0</version>
<configuration>
<wait>false</wait>
<!-- Container configuration -->
<container>
<containerId>jboss5x</containerId>
<type>installed</type>
<home>${it.jboss5x.home}</home>
<timeout>300000</timeout>
</container>
<!-- Configuration to use with the Container -->
<configuration>
<type>standalone</type>
<home>${project.build.directory}/jboss5x</home>
<properties>
<cargo.jboss.configuration>default</cargo.jboss.configuration>
<cargo.servlet.port>${it.jboss5x.port}</cargo.servlet.port>
<cargo.rmi.port>1099</cargo.rmi.port>
<cargo.jvmargs>-Xmx512m</cargo.jvmargs>
</properties>
<deployables>
<deployable>
<groupId>com.fja.ipl</groupId>
<artifactId>ipl-lc-ear</artifactId>
<type>ear</type>
</deployable>
</deployables>
<!-- Override logging created by Cargo (which is way to chatty) with default
file from JBoss (see http://jira.codehaus.org/browse/CARGO-585) -->
<configfiles>
<configfile>
<file>src/test/resources/jboss-log4j.xml</file>
<todir>conf</todir>
</configfile>
</configfiles>
</configuration>
</configuration>

<executions>
<!-- before integration tests are run: start server -->
<execution>
<id>start-container</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<!-- after integration tests are run: stop server -->
<execution>
<id>stop-container</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
<properties>
<it.jboss5x.home>${basedir}/../../../tools/bin/jboss-5.1.0.GA</it.jboss5x.home>
<it.jboss5x.port>8080</it.jboss5x.port>
</properties>

November 19, 2009

JBoss and Maven

The Challenge

During the last days I was busy trying to build and deploy a JEE application on JBoss – using Maven, of course. One interesting task was to run integration tests, i.e. JUnit tests that are testing a service bean deployed on JBoss application server. Integration tests with Maven are an interesting issue, worth its own blog entry probably. But this post is all about JBoss.

Well, this task doesn't sound hard for a Maven expert, but unexpectedly it was not that easy. Our test code (calling the EJB) was depending on JBoss artifacts, which might be questionable in itself but that's how it's currently done. Hence, during runtime the code needs the jbossall-client.jar from JBoss' client directory in the classpath.

The Eclipse Way

We are using JBoss 5.x, and in this version the jbossall-client.jar is rather small since it only references all required jar files in the Manifest's Class-Path element:

Manifest-Version: 1.0
Specification-Title: JBossAS
Specification-Version: 5.0.0.GA
...
Implementation-Vendor: JBoss.org
Implementation-Vendor-Id: http://www.jboss.org/
Class-Path: commons-logging.jar concurrent.jar ejb3-persistence.jar hi
bernate-annotations.jar jboss-aop-client.jar jboss-appclient.jar jbos
s-aspect-jdk50-client.jar jboss-client.jar jboss-common-core.jar jbos
s-deployers-client-spi.jar jboss-deployers-client.jar jboss-deployers
-core-spi.jar jboss-deployers-core.jar jboss-deployment.jar jboss-ejb
3-common-client.jar jboss-ejb3-core-client.jar jboss-ejb3-ext-api.jar
jboss-ejb3-proxy-clustered-client.jar jboss-ejb3-proxy-impl-client.j
ar jboss-ejb3-proxy-spi-client.jar jboss-ejb3-security-client.jar jbo
ss-ha-client.jar jboss-ha-legacy-client.jar jboss-iiop-client.jar jbo
ss-integration.jar jboss-j2se.jar jboss-javaee.jar jboss-jsr77-client
.jar jboss-logging-jdk.jar jboss-logging-log4j.jar jboss-logging-spi.
jar jboss-main-client.jar jboss-mdr.jar jboss-messaging-client.jar jb
oss-remoting.jar jboss-security-spi.jar jboss-serialization.jar jboss
-srp-client.jar jboss-system-client.jar jboss-system-jmx-client.jar j
bosscx-client.jar jbossjts-integration.jar jbossjts.jar jbosssx-as-cl
ient.jar jbosssx-client.jar jmx-client.jar jmx-invoker-adaptor-client
.jar jnp-client.jar slf4j-api.jar slf4j-jboss-logging.jar xmlsec.jar

This list is impressive... and the approach is working with current Eclipse. The equivalent for the Maven world would be a POM that references all other required libraries as normal dependencies (see this discussion).

The Maven Way

And yes, such a POM org.jboss.jbossas:jboss-as-client is available on JBoss Maven repository (note: it's not org.jboss.client:jbossall-client which is the reference-by-manifest's-class-path version!).


However, this approach involves two issues:

  • By following the dependencies defined in this org.jboss.client:jbossall-client transitively, Maven will download a vast number of JBoss and JEE libraries which you actually don't want to be used and packaged in your client. This includes things like org.jboss.jbossas:jboss-as-server:jar:5.1.0.GA and jacorb:jacorb:jar:2.3.0jboss.patch6-brew. Does not sound confidence-building, does it? Seems like JBoss should exclude transitive dependencies at the right places.
  • Moreover, some of the dependencies can't be found on any of the Maven repositories we configured to proxy in our Nexus. This includes Maven Central, JBoss of course, and a couple of others, so I do not know what else to add to provide the missing jars. Maybe it's just that the reference itself is wrong (version number?).

Instead of excluding everything we do not use currently, next idea is to just include those dependencies to the JBoss jar files that the client actually requires. But... it's quite hard to find out the corresponding Maven coordinates. This includes guessing the groupd and artifact id, but also the version – and unfortunately the version information given in the jar's manifest file is not useful since it notes the JBoss AS version instead of the version of the library which is required for Maven.

Lost in Space?

So, I ended up re-packaging a jar that holds the content of the required client jars and putting this on our Nexus...

I honestly wonder how everybody else is using JBoss 5.x with Maven on the client side???

October 27, 2009

Java Mystery: Directory Exists but Does Not?

Well, yesterday for me was another one of those days where you think your computer must be fooling you... There is an issue with a DirectoryCleaner that works for a subproject but does not when called from the parent. But first things first.

The openArchitectureWare Part

We are using oAW (yep, the new version which is part of Eclipse Modeling Project of Eclipse Galileo). Before generating the artifacts from the model, we are executing a directory cleaner that just rubs out the folders, for instance src/generated/java. The workflow file generate.mwe looks like this:

<workflow>
<property file="generateAll.properties"/>
<component class='org.eclipse.emf.mwe.utils.DirectoryCleaner' directory='${srcGenDir}'/>
<component file='.../generateAll.mwe' inheritAll="true">
<modelFile value='...' />
</component>
</workflow>

srcGenDir is a property that is defined in the included properties file, but that's irrelevant. The highlighted line configures the mentioned directory cleaner. The relevant code snippet is invokeInternal() method of org.eclipse.emf.mwe.utils.DirectoryCleaner which is part of Eclipse EMF frameworks:

protected void invokeInternal(final WorkflowContext model, 
final ProgressMonitor monitor, final Issues issues) {
if (directory != null) {
final StringTokenizer st = new StringTokenizer(directory, ",");
while (st.hasMoreElements()) {
final String dir = st.nextToken().trim();
final File f = new File(dir);
if (f.exists() && f.isDirectory()) {
... do the cleanup ...
}
}
}
}

Pretty simple, right? The directory attribute contains one or more directories (comma separated), hence a tokenizer is used to get each of them and to create a file. If this file exists and it's a directory, that will be erased.

The Maven Part

Of course we are using Maven to build the stuff. The Fornax Maven plugin is configured to call the workflow during Maven build. Moreover, the mentioned workflow is part of a subproject B which belongs to an outer multi-module project A.

Now, this is where the mystery begins... When I build B (the submodule), everything is fine and works like expected. However, when I build A (the parent project), B will be built in turn and its workflow is executed, but the directory is not cleaned up! You wouldn't expect this, right?

The Strange Part

In an attempt to find out what's going on, we put some debugging code into DirectoryCleaner. It turns out that the file f returns the same value for getAbsolutePath() in both cases, but f.exists() reveals false when the build is started from the parent project – hence the condition is not met and nothing will be cleaned up.

Unfortunately, you can't look any deeper into the native code that is called when determining if a file exists. So, in a kind of trial and error approach, we found out that using the following code fixes the issue:

protected void invokeInternal(final WorkflowContext model, 
final ProgressMonitor monitor, final Issues issues) {
if (directory != null) {
final StringTokenizer st = new StringTokenizer(directory, ",");
while (st.hasMoreElements()) {
final String dir = st.nextToken().trim();
final File f1 = new File(dir);
final File f = new File(f1.getAbsolutePath());
if (f.exists() && f.isDirectory()) {
... do the cleanup ...
}
}
}
}

That is: by creating another file that is using the absolute path of the first one and using this further on, everything is fine in both scenarios – whether called from parent project or submodule.

To be honest: I have no explanation for these findings. Why is File behaving differently? When calculating the exist flag, the code should take into account the file's absolute path, right? So, why is creation of another file based on the absolute path is fixing the issue? Any insights are deeply appreciated...!

June 23, 2009

Maven: How Relocated Artifacts Can Ruin Your Day

Wow, this is one of those days I struggle with issues all the time without actually going forward one little bit on my original task... I guess you all know this feeling :o(
Now, one of the things that I wasted quite some time on is excluding a dependency. One of our modules is defining a dependency to Apache DBCP framework like this:
<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.2.1</version>
</dependency>
This dependency is transitively depending on some XML artifacts, which you can see by using the maven-dependency-plugin's tree goal:
...
[INFO] +- commons-dbcp:commons-dbcp:jar:1.2.1:compile
[INFO]   +- commons-pool:commons-pool:jar:1.2:compile
[INFO]   +- xml-apis:xml-apis:jar:1.0.b2:compile
[INFO]   \- xerces:xercesImpl:jar:2.0.2:compile
...
Well, the xml-apis and xerces artifacts are a bit outdated and actually we didn't want them at all in our war files, so I decided to exclude them using the dependency exclusion feature:
<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.2.1</version>
  <exclusions>
    <exclusion>
      <groupId>xml-apis</groupId>
      <artifactId>xml-apis</artifactId>
    </exclusion>
    <exclusion>
      <groupId>xerces</groupId>
      <artifactId>xercesImpl</artifactId>
    </exclusion>
  </exclusions>
</dependency>
What do you think will be the result? Surprisingly, xml-apis:xml-apis vanished, but xerces:xercesImpl didn't. The new dependency tree is the evidence:
...
[INFO] +- commons-dbcp:commons-dbcp:jar:1.2.1:compile
[INFO]   +- commons-pool:commons-pool:jar:1.2:compile
[INFO]   \- xerces:xercesImpl:jar:2.0.2:compile
...
That's strange, isn't it? It took me an hour or so playing around to find the reason for this behaviour: Xerces has been relocated with respect to its Maven coordinates... The only hint Maven is giving you is this message when executing in debug mode:
[DEBUG] While downloading xerces:xerces:2.0.2
  This artifact has been relocated to xerces:xercesImpl:2.0.2.
What does that mean? Well, you may relocate artifacts in the Maven repository when the group or artifact id should change (for more details, see the Guide to Relocation). Originally, the DBCP artifact specified a dependency to xerces:xerces in its dependencies – which you can see in the artifact's POM file (residing in your local Maven cache, for instance):
<dependency>
  <groupId>xerces</groupId>
  <artifactId>xerces</artifactId>
  <version>2.0.2</version>
</dependency>
However, xerces:xerces has been relocated to xerces:xercesImpl and these new coordinates are what is shown in the dependency tree. But, when excluding a dependency, you must specify the original transitive dependency (as defined by the artifact), not the new coordinates:
<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.2.1</version>
  <exclusions>
    <exclusion>
      <groupId>xml-apis</groupId>
      <artifactId>xml-apis</artifactId>
    </exclusion>
    <exclusion>
      <groupId>xerces</groupId>
      <artifactId>xerces</artifactId>
    </exclusion>
  </exclusions>
</dependency>
How intuitive is that???
Maven should really do better, either by indicating the original coordinates when showing the dependency tree/list, or better yet by issuing a warning when trying to exclude a relocated dependency. I guess the latter one is not easy to implement, but current solution is just annoying!

June 17, 2009

Checkstyle: One and Only Configuration File?

The Checkstyle Challenge

When you are using both Eclipse and Maven, you are probably facing the same challenge like we do: you would like to have a single configuration file for Checkstyle tool and reference that in Eclipse as well as in Maven.

Checkstyle ships with some default conventions (according to the Sun coding standards). But you usually want to customize this and create your own configuration set, based on your corporate coding styles.

Now, you want to setup Checkstyle within Eclipse to provide direct feedback to developers. You want to setup Checkstyle with Maven to be able to have a coprehensive report built as part of your project site. And you want both to reference the same configuration file, to avoid having to maintain two of them. Naturally, right?

Well, unfortunately, it seems impossible to achieve... if you really found a way, let me know!

However, there nonetheless are some tips to help you having a centralized configuration of Checkstyle with both tools. This post is about how to do that.

Checkstyle with Maven

The goal is to centralize the Checkstyle configuration to make its usage as simple as possible for all the projects, avoiding manual configuration as far as possible. Fortunately, this is quite easy using Maven's dependency mechanism.

Essentially, we build a custom artifact that contains nothing but our Checkstyle configuration file. This JAR is deployed to the company's Maven repository from where it can be downloaded as a normal dependency when needed. After that, the Checkstyle plugin is configured (in the reporting section) to reference the packaged configuration file. That's it!

Now, we'll look at the details...

Let's assume our checkstyle configuration is located in config/my-checkstyle.xml. Then the POM of the checkstyle artifact, which actually is a minimal Maven project, looks like this:

  ...
<groupId>com.fja.ipl</groupId>
<artifactId>checkstyle-config</artifactId>
<version>1.0</version>
<build>
<resources>
<resource>
<directory>config</directory>
</resource>
</resources>
</build>
...

(Of course, you additionally have to add the distributionManagement section to be able to deploy the artifact, but this is usually derived by the company's base POM.)

Deploy the artifact. You'll notice that the created checkstyle-config-1.0.jar indeed contains your my-checkstyle.xml file in its root.

Now let's switch to the project that should use the Checkstyle report. The configuration file is referenced in the Checkstyle plug-in configuration in the reporting section like this:

  <reporting>
<plugins>
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.2</version>
<configuration>
<configLocation>my-checkstyle.xml</configLocation>
</configuration>
</plugin>
...
</plugins>
</reporting>

That is not yet working, Maven can't find this file – our checkstyle-config artifact has not been referenced. We do this now by adding it as an extension dependency to the POM as follows:

  <build>
<extensions>
<extension>
<groupId>com.fja.ipl</groupId>
<artifactId>checkstyle-config</artifactId>
<version>1.0</version>
</extension>
</extensions>
...
</build>

The artifacts configured as extensions will be included in the running build's classpath. That means, checkstyle-config-1.0.jar now is in the classpath of the Checkstyle plugin, and the file referenced by the configLocation setting is located in the JAR and hence can be found.

Using this approach, the Checkstyle configuration can be maintained centrally, and projects can get new versions simply by updating the artifact's version in the extension section.

Checkstyle with Eclipse

Unfortunately, I don't really see a way to use the same Checkstyle configuration, packaged into the Maven artifact, with Eclipse as well...

So you'll have to duplicate the configuration file. However, to keep the number of duplicates small, I recommend to put it on a central network drive within your company and use the Remote Configuration option of the Checkstyle Eclispe plugin to reference the remote file. This does not prevent you from working offline because Eclipse downloads and caches this file.

In this scenario, changing the Checkstyle configuration is done centrally by just updating the remote file. This is a great advantage over using a configuration that is part of the project, because in the latter case you would have to upgrade every single project.

May 13, 2009

Maven Compromised by Plugins

Every piece of software has its flaws... The important part is how the project is dealing with bugs.

Maven is fine

With Maven, the situation actually is good enough: there have been releases of 2.0.x branch 1-2 times per year, to fix bugs and provide new features or improvements. At the same time, the 2.1.0 branch appeared with a milestone and a final release six month later.

However, you would usually not want to switch your Maven build system to a new version too often. This is always risky since a new version of the "core" may cause unexpected results in any area of the build. A continuous integration engine helps to detect bugs, but you never know.

Fortunately, Maven is heavily based on plugins – which means if you encounter any issue with a specific Maven task, it's usually caused by the related plugin, not the Maven core. That's great, right? You just upgrade your POMs to use a new version of the particular plugin. Better isolation, fewer risks!

(Some) Plugins are not!

But... this assumes that there is a new version of the plugin available. Now imagine this: a plugin has a bug reported by the community, and there is a Jira issue for that. Some brave soul even attached a patch to fix the issue and others confirmed that it works for them. Thus, the new bugfix release could easily be built. But what happens? No new release. Nothing. Not even an alpha/beta version. Not even a comment explaining why. For months!

Here are just two examples we stumbled upon the last couple of weeks:

  • Javadoc plugin: The report is not generated at all for multi-module project if run from parent level and using the recommended aggregate goal. Jira issue reported 2008/08/18, patch added 2008/09/22. No new release ever since.

  • Checkstyle plugin: Current version does not work with Checkstyle 5.0 that started to appear on the stage last summer. Jira issue created 2008/10/28, along with a patch; another patch added 2009/03/27. No new release ever since.

Wow. How annoying is that? There is a major/blocking issue, and there is a patch available, but the plugin owners don't take the time or effort to create a new release?!? Why so? What else could the community do to help fixing the plugin?

This way, the plugins are compromising usability of Maven. IMHO, that's a serious issue...

So, what can you do?

If you don't want to continue waiting for a new plugin release, you have to build one on your own, for your "private use". That's quite easy, fortunately, and I'll probably post about that later.

However, this is actually not what a typical Maven user wants to do. The plugins should just do it right: release more often to fix their known bugs! You could increase the pressure a little bit by voting for the Jira issue or adding some comments, but my experiences are not too good ...

May 11, 2009

Update to Checkstyle 5.0

Checkstyle 5 is available!

This is good news: a new version of Checkstyle is available that better supports Java 5 language features (like generics, annotations or package-info files). Additionally, some checks are cleaned up a bit, for instance concerning their parent in which they are contained. See the release notes for details.

Due to these changes, Checkstyle 5.0 is not fully compatible to previous versions 4.x – which is already indicated by the version number leap. Hence, don't expect the upgrade to be smoothly!

Nevertheless, I thought it would be time to upgrade, so here is what I did...

Step 1: Upgrade checkfile configuration

Due to the incompatabilities, I had to apply some changes to our checkstyle configuration to make it work with Checkstyle 5. To be sure I did it right, I have downloaded and used the Checkstyle binaries and checked my configuration for just a simple project. Hence, before upgrading Eclipse and Maven Checkstyle integration, I know my configuration is correct.

BTW, we are using a common checkstyle configuration file for all projects, and reference this "global" file with Maven and Eclipse in different ways:

  • With Maven, we use a custom artifact that contains nothing but our checkstyle.xml file. This in turn is included as build extension in our main base POM. See this post for more details.

  • For Eclipse, we use the "Eclipse Checkstyle Plugin" that provides a Remote Configuration option to reference the configuration file on an internal file server.

Step 2: Upgrade Eclipse Plugin

There is a new Beta-Release of Eclipse Checkstyle plugin (eclipse-cs) available on its update site http://eclipse-cs.sf.net/update. At the time of writing, there are three features available:

  • Eclipse Checkstyle Plug-in – version 5.0.0-beta4
  • Eclipse Checkstyle Plug-in 4.4.x -> 5.0.0 Migration (Optional) – version 5.0.0-beta4
  • m2eclipse Maven Synchronization Plugin (Optional/Experimental) – version 0.0.3

Oh, seems there would have been some help in migrating from Checkstyle 4.4 to 5.0... Doesn't matter, I always like to see what the changes are so it's good to do it manually.

The m2eclipse Synchronization plugin is a new feature providing mechanism to synchronize Checkstyle rules and configuration between the maven plugin and eclipse-cs. Sounds really interesting... but let's do one step after the other and test this later.

So. I just installed the "Eclipse Checkstyle Plug-in" feature. Eclipse didn't recognize that this actually is an update, so you have to uninstall the previous eclipse-cs installation manually.

Why that? Well, the "package" has changed from com.atlassw.tools.* to net.sf.eclipsecs.*, and this applies to the feature's ID, too. Moreover, this renaming also affects the buildCommand and nature in .project files, they have to be net.sf.eclipsecs.core.CheckstyleBuilder and
net.sf.eclipsecs.core.CheckstyleNature
now.

Additionally, the notation for file sets has been changed: a file set previously configured as src\\main\\java\\com\\mycompany\\.* does no more match to any file; instead, it has to be the slash now like in src/main/java/com/mycompany/.*.

Okay, maybe I should have tried the Migration plugin... Anyways, after these changes everything works fine for me in Eclipse.

Step 3: Upgrade Maven Plugin

Good. Last piece is Maven, which provides the Maven 2 Checkstyle Plugin. However, the current version is 2.2 which is based on Checkstyle 4.4 by defining these dependencies:

<dependency>
<groupId>checkstyle</groupId>
<artifactId>checkstyle</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>checkstyle</groupId>
<artifactId>checkstyle-optional</artifactId>
<version>4.4</version>
</dependency>
There is an interesting way to override the plugin's dependencies pointed out by Brian Fox, but that's not going to work for us because Checkstyle versions 4.4 and 5.0 are not API compatible.

What can we do? Not much... we'll have to wait for a new version of the Checkstyle plugin that updates to Checkstyle 5. There is this Jira issue, and patches have already been provided some time ago. It's only that there seems to be no progress whatsoever... Checkstyle 5.0 is officially out since April 18th, so there is no reason to wait any longer! Create a new release (for my part, alpha/beta is fine as well) – please!!!

If you really need the plugin to be fixed now, you could checkout the plugin's sources and built your own version, applying the patch provided in the Jira issue. That works, but is nothing we want to do regularly!