April 29, 2009

Maven Documentation: The Missing List

A rather weak talent of Maven is probably its documentation. This is my personal opinion, but it seem to match what other people think. Yeah, it has been gotten better, but there is still some room...

It's not that there is not enough documentation available, it's just that the existing documentation is poorly organized and you often can't find what you need. Moreover, many documents (especially for plugins) are only scratching the surface, not even diving into the important shallows. Or, even worse, they give you wrong or inconsistent information...

Here is just one example: The surefire plugin page for surefire:test goal gives this description for the excludes parameter: "List of patterns (separated by commas) used to specify the tests that should be excluded in testing. (...)". However, the example page for "Inclusions and Exclusions of Tests" shows an example that uses nested elements – wait, what about the commas? So, what do you do? Go ahead and try yourself? I bet this type of annoyance is hitting every Maven newbie sooner or later.

Well, maybe the Maven team should start a coordinated, collaborative effort to improve documentation just like Wikipedia is doing once in a while. Or, better yet, why not move all documentation into some open Wiki and let the community work on what it thinks is missing, broken, inconsistent, badly organized, ...

Well, until then, we have to use what is available. This post tries to list most important pieces of online documentation, indispensable when you are dealing with Maven in a professional way. Let me know if you think something is missing on this list...

Introductory & General

Books

  • Maven: The Definitive Guide – a free book by Sonatype (using a Creative Commons license). High quality, up-to-date, written by some of the famous Maven gurus: Tim O'Brien, John Casey, Brian Fox, Bruce Snyder and Jason Van Zyl.
  • Better Builds with Maven – another free book, originally maintained by Mergere, now managed by MaestroDev. This, too, is written by core members of the Apache Maven Project: Vincent Massol, Jason van Zyl, Brett Porter, et al.

Technical Details

Community

Other Useful Links


Updates

  • 2009/05/11: added link to Sonatype's "Summary of Maven How-Tos" blog post – definitely worth to be listed here...
  • 2009/05/28: added link to Maven Properties Guide wiki page

April 27, 2009

Spring Web Flow: watch out!

Starting in November 2008, we found a concurrency issue in our web applications that are based on the following framework stack:

  • Spring 2.5.4
  • Spring Web Flow 2.0.5
  • JSF 1.2_09
  • Facelets 1.1.14a
  • Trinidad 1.2.9

The issue

For a single user, everything run fine. However, when multiple users hit the application, we got some strange exceptions occasionally – not always and not always at the same place. However, we managed to get this one regularly with our JMeter tests:

SCHWERWIEGEND: Error Rendering View[/plan/FindPlan.xhtml]
javax.el.PropertyNotFoundException: /file:/.../facelets/ipl-comboBox.xhtml @64,30 value="#{localField.jsfValue}": Target Unreachable, identifier 'localField' resolved to null
at com.sun.facelets.el.TagValueExpression.getType(Tag ValueExpression.java:62)
at org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.ValueRenderer.getDefaultConverter(ValueRend erer.java:78)
at org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.SimpleSelectOneRenderer.renderNonElementCon tent(SimpleSelectOneRenderer.java:323)
at org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.FormElementRenderer.encodeAllAsNonElement(F ormElementRenderer.java:172)
at org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.FormElementRenderer.encodeAll(FormElementRe nderer.java:105)
at org.apache.myfaces.trinidad.render.CoreRenderer.delegateRenderer(CoreRenderer.java:335)
....

This exception got rendered on the page instead of the actual component. At other times, we got java.lang.IllegalStateException and other funny behavior.

The strange thing is, the referenced variable is put into the flow scope, of course. Now that we took the time to debug into this issue, we can see that the scopes seem to be correctly filled and the property is actually there, it's just that the ELResolvers could not retrieve it...

Actually, we thought it would be caused by our Java or Spring configuration code, presumably using wrong bean scope or the like.

...is fixed!

Guess what... now I have been pointed to this bug of Spring Web Flow version 2.0.5: Class SimpleELContext is not thread-safe, but is stored in some application singletons :-( See this or this thread for more details.

The good news is, the bug is fixed in version 2.0.6; we have upgraded our projects and load tested them, and everything is just fine.

But...

IMO this is leaving a stale aftertaste. I'll tell you why.

That issue is a severe one: web applications using SWF 2.0.5 are just not working for concurrent users. Everybody using SWF in this version (maybe earlier versions as well) is affected. Given that, it seems strange that

  • the issue hasn't been reported and fixed earlier: Version 2.0.5 has been published on 14th of November, and it took more than 2 weeks for the Jira issue to show up.
  • this issue is hard to find in the web: Of course, we did some web research to find out if someone else might have a similar problem, but we did not find very much useful. The mentioned threads have started in February/March 2009 – did nobody suffer from this bug earlier than that? Why did nobody post this issue anywhere before?
  • the fact that this version is buggy hasn't been reported on Spring Source web pages: Since it's rather threatening, why not put an eye-catcher message on the download page (or somewhere else) telling that this version is buggy and should not be used?

I can see two reasons for this situation, and actually I don't like both: on one hand, there seems to be no load testing by Spring Web Flow team before doing a release; on the other hand, this version 2.0.5 is probably not used very much for real-world web applications by the community out there, and I honestly hope that this is not true to SWF as a whole...

April 24, 2009

Eclipse: User Operation is Waiting, and Waiting, ...

I am using Eclipse since quite a long time, sometimes around 2002. That was version 2.0, if I remember correctly. Since then, I have always upgraded to the latest version, if not milestone build. Most of the time I was quite happy with the stability and really appreciated all the evolving features and UI improvements.

One major improvement (it was in 3.0, right?) was when they moved builds to a background process, enabling you to continue working when the class or project needed to be compiled.

But... what is this? In all the latest release versions (3.4.0 ... 3.4.2) I sometimes get this dialog when trying to save a file:

Eclipse can't save my file while it is building "in the background"? Come on! Nota bene, I am not talking about .project files or other stuff the whole build could depend upon, it's also true for simple resources or even documentation files!

That's really annoying. This is a modal dialog, which means I have to wait for minutes until build is complete or at least in a state where Eclipse thinks it can save the file. Of course, the user interface is blocked during that time... unless you cancel the save task.

I use the Eclipse Java EE package and additionally have installed a couple of regular plugins, like m2eclipse and oAW. So I can't tell for sure who is actually causing this mess, but on the other hand my installation is probably not so unusual and I'm sure others get this issue as well. There are some entries in various issue databases telling me that I'm right.

Eclipse is great and I (still) love it – but our relationship really gets poisoned by this kind of things... Maybe I try IntelliJ IDEA or NetBeans one day... I have warned you!

April 22, 2009

Maven Setting for Using a Single Repo Manager

In a previous post I have tried to explain why it's a good idea to define your Maven repository in your settings.xml file instead of the POM.

Of course, there is some information available on how to do this, for instance in the Maven Settings Reference, the Mirrors Guide, or the great Nexus book from Sonatype.

Nevertheless, this setup of repositories, proxies and mirrors can be a bit tricky (and is quite confusing when just starting with Maven), so here is how we did that.

How should it look like?

We use the Nexus repository manager and especially do like the repository groups which combine repositories into single logical repo. (This feature is also implemented by other products like Archiva or Artifactory, see this Feature Matrix).

Now, here is the approach we want to implement:

  • Access the repository group "internal" for release and snapshot versions of all internal artifacts that have been produced by our company
  • Use the "public" repository group to provide release versions of all external artifacts (we don't want external snapshots!)
  • For downloading plugins, use both the "public" as well as the "internal" repository group to get release versions (we don't want to use snaphot version of plugins!)
  • Don't use any other repository than "public" or "internal", no matter what is configured in project's POM, inherited POM, transitive dependencies, dependencies of plugins, etc. -- never ever!


Defining the Repositories

This is the required repository definition:

  • We use a profile (that is activated by default) to group the repositories. This is actually not required, but gives a bit more flexibility.
  • For artifact resolution, there are two repositories with id "central" and "internal" to provide public (external) respectively internal artifacts.
  • The definition for "central" repository is overriding (by using the same id) the "central" repository already defined in the implicit super POM, which is using repo1.maven.org for downloads.
  • The given URLs are only bogus and are overridden by mirror settings – see below.
  • Additionally, we define the same two repositories "central" and "internal" as plugin repositories (required by Maven to find plugins). However, in contrast to artifacts, snapshots are not allowed at all.

And here is how the code looks like:

<profiles>
<profile>
<id>repo-config</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>

<repositories>
<!-- repo "central": override Maven default,
mirrored to Nexus group "public" -->
<repository>
<id>central</id>
<url>http://central</url>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<!-- repo "internal": provides internal release and snapshot artifacts,
mirrored to Nexus group "internal" -->
<repository>
<id>internal</id>
<url>http://internal</url>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
</repository>
</repositories>

<pluginRepositories>
<!-- plugin-repo "central": override Maven default,
mirrored to Nexus group "public" -->
<pluginRepository>
<id>central</id>
<url>http://central</url>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<!-- plugin-repo "internal": provides internal plugin releases,
mirrored to Nexus group "internal" -->
<pluginRepository>
<id>internal</id>
<url>http://internal</url>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
<checksumPolicy>ignore</checksumPolicy>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>

</profile>
</profiles>

Defining the Mirrors

After defining the repositories, two issues are left: we have to specify the correct URLs, and to make sure nobody uses any other repository. Both of this is done with the <mirrors> definition.

  • Repository with id "internal" is mapped to the URL of the "internal" Nexus group; that is, Maven uses that URL instead of what is given with the repository definition (the bogus URL http://internal).
  • Every other repository (specified by using <mirrorOf>*</mirrorOf>) is mapped to the URL of the "public" Nexus group.
  • This last definition not only maps the "central" repository to the correct URL, but also any other repository and hence ensures every request is locked down to the internal repository manager.

Again, here is the section from settings.xml file:

<mirrors>
<mirror>
<id>nexus-internal</id>
<name>Nexus internal repository group</name>
<url>http://our-nexus-server:8081/nexus/content/groups/internal</url>
<mirrorOf>internal</mirrorOf>
</mirror>
<mirror>
<id>nexus-public</id>
<name>Nexus public repository group</name>
<url>http://our-nexus-server:8081/nexus/content/groups/public</url>
<mirrorOf>*</mirrorOf>
</mirror>
</mirrors>

What about the Proxy?

The <proxy> section in the settings.xml file can be used to define a network proxy that is used for some or all of your HTTP requests. In our configuration, since the Nexus repository manager is running on an internal server and Maven is configured to not connect to any other server, we just don't need this setting.

See Configuring a proxy for more details on Maven Proxies. BTW, it should now be very clear that mirrors in the Maven world are something completely different than proxies...