Tag: maven

 

JSPs in Maven

Every now & then I need to whip out a quick webapp. Normally, I would prefer to use something like freemarker or velocity. But JSP is standard, and it’s everywhere. Despite it’s inability to be secure, it is convenient. And that’s gotta count for something right?

But, I never quite managed to get everything working. So whilst looking at tclogview, I figured it out. First of all, you have to get the dependencies correct.

  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.1</version>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <scope>runtime</scope>
    <version>1.2</version>
  </dependency>

So you need to pull in both the servlet API and the JSP API. They’re both “provided” scope, which means that they have to be available when compiling, but they don’t need to be packaged as they’ll be there already. I’m not sure why you need the JSP API. When I was playing with NetBeans, it appeared to be necessary to get bits of the editor working correctly.

You also need to pull in the JSP Standard Tag Library. It’s not mandatory, but you won’t get far without it. The only trouble is that there are a gazillion different versions on the central repository with no clear clue as to which one should be used. This version appears to work OK. Importantly, it contains the taglib descriptors. Also, we set the scope to “runtime” as it’s not needed at compile time.

This gets you started. The other thing that you need is a correct web.xml. Different versions of the deployment descriptor get you different features in your JSP page. Go figure. Looking in the JSP spec right now, I can see §JSP.3.3.2:

The default mode for JSP pages in a Web Application delivered using a web.xml using the Servlet 2.3 or earlier format is to ignore EL expressions; this provides for backward compatibility.

The default mode for JSP pages in a Web Application delivered using a web.xml using the Servlet 2.4 format is to evaluate EL expressions with the ${} syntax. Expressions using the #{} are evaluated starting with JSP 2.1. See Section, “Backwards Compatibility with JSP 2.0” for more details on the evaluation of #{} expressions.

This caught me out for a while as I was trying to use the new syntax and it wasn’t getting interpolated. I want to use JSP 2.1, so I reckon it’s easiest to use servlet 2.5. That means I need a declaration that looks like this in my web.xml.

  <?xml version="1.0" encoding="UTF-8"?>
  <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"></web-app>

Why was this a problem? Because the maven-archetype-webapp archetype generates a web.xml that looks like this.

  <!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd" >
  <web-app>
    <display-name>Archetype Created Web Application</display-name>
  </web-app>

Ooops. This issue was filed as ARCHETYPE-106 over a year ago. Fixing that would have made my life a lot easier.

maven in anger

I’ve ranted about Maven before. As it happens, I still stand by much of what I said. But, I’ve come to realise how superficial these criticisms are.

Earlier this year, one of the libraries that I have a hand in maintaining needed to be used by a maven project. Whilst I could have just written enough of a POM to specify my dependencies, I figured that it would probably be easier to just switch the build system to maven.

Plus, this particular library is meant to be used as a webapp. The existing ant build builds a war file. But really, the webapp is just for exercising the library, it’s not the main point. Switching to maven makes it ideal for splitting into two different projects.

So I went ahead, and made the change. I did it in a slightly invasive manner (moving my source directories around to fit the expectations of maven). But the end result is that I have found the project easier to work with. Previously, the dependencies of the project were very unclear. Switching to maven and running mvn dependency:analyze quickly spotted quite a few jar files we “depended” on weren’t actually used. And one of the nice things is that maven embeds the version number1 in the resulting jars (previously it had been a chore to figure out what version was in use).

This turned out reasonably well, although the main benefits were yet to be had. These are down to the huge growth in the maven tooling ecosystem, particularly this year.

  • Eclipse support has been improving by leaps and bounds. Initially I went with q4e (now IAM), but I later switched to m2eclipse as it seems to be a more fully-featured plugin right now.
  • NetBeans has pretty good maven support, even though it’s lacking a couple of useful features in m2eclipse. But it’s also very nice as NetBeans is then entirely driven by the POM — it doesn’t create a bunch of files based on the POM. The POM is the project.
  • Hudson makes it an absolute snap to get a maven project into continuous integration. Hudson is the only java project I’ve ever seen that comes close to the simplicity of a wordpress install. Way to go Kohsuke!
  • The nexus repository manager makes using maven much more practical (even for a single developer) as it vastly speeds up downloads and reduces your dependency on central. Plus, you can use it as a place to deploy your own goodies once you’ve built them.
  • Maven: The definitive guide — the maven documentation, whilst there’s a good deal of it, isn’t exactly integrated well. This book has been an enormous eye opener about how to do things “the maven way.” Plus the in-depth coverage of things like the assembly plugin was tremendously helpful.

With all these tools in place you find the integration really starts to come to the fore. For example, I noticed that hudson had a findbugs plugin. I installed the plugin in hudson, activated the findbugs plugin in my POM (about 4 lines of XML) and got:

  • Pretty graphs of the number of bugs found in each build.
    findbugs-trend.png
  • Detailed breakdown of each findbugs report in each build, including source code views of where the problems lie.

    hudson-findbugs-fixed.png

  • Indicators showing how many bugs I had introduced into each build (or hopefully removed).
    hudson-findbugs-warn.png

In short, I got a lot of value out of a (relatively) small change. And the same technique can be applied almost identically on the next maven project. And the one after that. And so on. Consistency has been one of the great virtues of maven for us.

The end result is that now most of our projects are maven projects at work (and I’m also using maven at home).

Of course, it’s not all plain sailing. I’m still seeing something weird to do with m2eclipse and WTP. But I’m confident that I’ll figure it out and it’ll be fixed.

Overall, I reckon that maven is something you should be taking a look at if you’re building Java projects. I know some people had taken a look at Maven 1 and come away with a sour taste. Now’s the time to check back and see what’s changed.

1 Even though I later had to change how this was done.

NetBeans 6.5

Today NetBeans 6.5 got released. Congratulations, guys! I’m primarily an Eclipse user1, but I keep hearing about NetBeans through the Java Posse and heck, I even subscribe to the NetBeans podcast to try and keep an eye on what’s up. Every time a new release comes out, I give it a whirl.

So this time, now I’m looking at 6.5. And it’s really nice on a lot of fronts. Most of the improvements in 6.5 aren’t directly relevant to me — they’re related to scripting languages I don’t use. What I’m particularly interested in is the core Java SE support. And NetBeans is really very good. It’s just not quite as good as Eclipse. Or (more likely) I can’t figure out a way to make it do what I want.

There are a couple of things I do all the time in Eclipse. First, ⌘-T, which shows type hierarchies. But that’s putting it simply. If you pop it over a class, it shows the subtype hierarchy. Press it again and it shows the supertype hierarchy. What’s neat is that if you do it over a method, it greys out those classes that don’t have an implementation of that method.

⌘-T in Eclipse

But what makes it particularly useful is that it doesn’t pop up a proper window, just a little floater (I have no idea what it’s really meant to be called). As soon as you click somewhere, it disappears. This makes it incredibly fast to navigate code.

As far as I can see, the closest that NetBeans has is “File Hierarchy” on ^⇧F12. But this pops up a big old clunky dialog box.

That’s one more thing about NetBeans — the key bindings are weird. And I speak as a long time emacs user. Now I’ll learn them (as it’s annoying to carry my customisations everywhere). But they conflict badly with a lot of Mac keyboards, because they’re extremely function-key heavy, and the mac likes to take over the function keys for itself.

The other Eclipse feature I use the whole damn time is “assign to local variable”. Yes, you can curse me for being damned lazy. But, I want to be able to type new PhoneImpl("01273", "123456"); then hit a key and get it assigned to a variable. In Eclipse, that’s ⌘-T L. I couldn’t find a way to do it in NetBeans. The compiler knows what type it’s going to be. Why should I have to remember? Otherwise I’d be back in Emacs.

There are a couple of missing refactorings I use a lot: “Extract to local variable” and “inline local variable”. Both are easily replaced with cut’n’paste 90% of the time.

One particular feature that Eclipse and NetBeans both share is terrible defaults for exceptions. Here’s Eclipse’s default:

  try {
      new URI("http://example.com/");
  } catch (URISyntaxException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
  }

And here’s NetBeans.

  try {
      new URI("http://example.com/");
  } catch (URISyntaxException ex) {
      Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
  }

Both of these are fundamentally broken, and going to catch out dozens of unwitting programmers who don’t know any better. Yes, you can change the defaults, but most people don’t.

Anyway I don’t want you to think that I’m totally down on NetBeans. The new release maintains what I first saw in 6.1: a very fast, full featured IDE. It’s dead easy to get going, and there is loads of documentation. I find the project view to be much more useful and better organised than the eclipse view of the same.

NetBeans Project View

I suspect that most of the problems I’ve been having are down to the fact I’ve been using Eclipse for 3 years, and NetBeans for a few hours at best.

One thing I did like very much is that there’s builtin Maven support. I downloaded the Java SE installation2, went to the plugins window and selected “maven”. About a minute later I had a working installation. And I can create new projects with archetypes really simply. And it’s dead simple to add new libraries as needed (I’m guessing it uses the nexus indexer to look stuff up). The only missing thing is the dependency viewer, but I can always run dependency:tree and dependency:analyze manually.

Funnily enough, where I think NetBeans really wins is the dynamic languages support. I’ve been following Tor’s blog for a little while and I’m incredibly impressed with what has been achieved in the Ruby support. For developing Ruby code, I head straight for NetBeans. Most of the issues I’ve outlined above are really specific to statically typed languages. For dynamic languages like Ruby (and now PHP and Python), what NetBeans gives you is a big win over the standard text editor.

Heck, the JavaScript support is pretty decent too. The first thing I did today was open up jslint4java and jump into fulljslint.java. That’s some pretty scary code. But NetBeans handled it with no issues at all. And it even pointed out a warning that’s easy to overlook (a duplicate hash key).

So, the big question is — am I going to use NetBeans? For any Ruby stuff, there’s no question. For Java stuff, I’m going to give it a try. After spending enough time with it to write this, I’ve already figured out enough stuff to use it a bit more successfully.

1 Apart from Emacs, Vim and TextMate. ☺

2 I didn’t like the look of the Java EE version — I don’t really need 3 application servers bundled, do I?

Embedding the maven version number

If you build a jar file with maven, it helpfully embeds a properties file so that you can pull out information about the build environment. You just need some code like this.

  String path = "/META-INF/maven/groupId/artifactId/pom.properties"
  InputStream stream = getClass().getResourceAsStream(path);
  Properties props = new Properties();
  props.load(stream);
  System.out.println("my version is" + props.get("version"));

Now, this is all well and good, but it only works when you’ve built the artifact1. In development, it simply doesn’t exist, and you have to work around that in the code.

It would be much simpler if you could just compile in the version as a constant.

Thankfully, when I asked the question on the maven-users list, Lee Meador showed me an example. In a nutshell, you use ant to generate an extra source file containing the version number.

First, you need a little ant script to emit some Java code.

  <project basedir=".." default="generate-version">
    <target name="generate-version">
    	<mkdir dir="${src.dir}/com/mycompany/myproject"/>
        <echo file="${src.dir}/com/mycompany/myproject/Version.java">
            package com.mycompany.myproject;
            /** Automatically generated by ant. */
            public class Version {
              public static final String VERSION = "${version}";
            }
    	</echo>
    </target>
  </project>

Then, you need to add the antrun plugin to your pom.xml.

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
      <execution>
        <id>generate-version-class</id>
        <phase>generate-sources</phase>
        <goals>
          <goal>run</goal>
        </goals>
        <configuration>
          <tasks>
            <ant antfile="etc/build-version.xml" inheritAll="false" inheritRefs="false">
              <property name="version" value="${project.version}" />
              <property name="src.dir" value="${project.build.directory}/generated-sources" />
            </ant>
          </tasks>
          <sourceRoot>
            ${project.build.directory}/generated-sources
          </sourceRoot>
        </configuration>
      </execution>
    </executions>
  </plugin>

This binds the antrun:run goal to the “generate-sources” phase (which happens right at the start of the build). It runs the little ant script above, passing in the correct properties. Then, it adds a new source directory, so maven knows to compile that one file that we’re generating.

When I initially tried this in Eclipse, it couldn’t find the Version class. However, running “Maven → Update Project Configuration” added target/generated-sources as a new source folder. And everything worked just fine after that. If you don’t have that option, you need to update to a new version of m2eclipse. Deleting and reimporting the project should sort it out.

This might seem like a fairly complicated way of solving the problem of getting the maven version number. But it’s more reliable than any of the other methods I have seen, so I’m pretty happy with it.

1 Yeah, I know. American spelling. I’ve been polluted by years of exposure.

AntUnit inside Maven

I’m in the middle of converting jslint4java to use maven as its build system (yes, really). Part of this is ensuring that the antunit tests I wrote continue to work. Maven has the antrun plugin, but it’s not 100% obvious how to use an antlib inside it.

Normally, to run an antlib extension as part of your build, you first dump the jar in ~/.ant/lib (or on the command line using a -lib flag) and then reference it in a namespace. e.g.

  <project name="foo"
           xmlns:au="antlib:org.apache.ant.antunit"></project>

This relies on ant being able to pick out org/apache/ant/antunit/antlib.xml from the classpath.

When doing this in maven through the antrun plugin, you can’t just dump stuff into ~/.ant/lib however. You need to tell ant where everything is. Originally, I was following the example of the rat ant task. This attempts to invoke a second copy of ant with the correct -lib argument. It’s ugly though. Why should we fork a second copy of the JVM? And ant may not even be installed (in maven, it’s just jar files in the local repository).

Eventually, I looked closer at the antlib documentation and found Load antlib from inside of the buildfile. This shows how a typedef is the key: You can associate a given antlib URI with a particular classpath entry. Thankfully, maven-antrun-plugin provides ${maven.test.classpath} (amongst others) which contains every entry that we need. So the solution now looks something like this.

Firstly, pom.xml.

  <project>
    <dependencies>
      <dependency>
        <groupId>org.apache.ant</groupId>
        <artifactId>ant-antunit</artifactId>
        <version>1.0</version>
        <scope>test</scope>
      </dependency>
    </dependencies>
    <build>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <executions>
          <execution>
            <phase>test</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <tasks>
                <ant antfile="${build.testOutputDirectory}/antunit/tests.xml"
                  inheritAll="false" inheritRefs="false">
                  <property name="test.classpath" refid="maven.test.classpath" />
                </ant>
              </tasks>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <!-- Force an upgrade to a newer ant version.  Required by antunit. -->
          <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.7.0</version>
          </dependency>
        </dependencies>
      </plugin>
    </build>
  </project>

So, when we get to the test phase, we automatically run antunit/tests.xml, passing in the correct classpath. This is what the ant file looks like:

  <project name="jslint-antunit"
           xmlns:au="antlib:org.apache.ant.antunit">
 
      <taskdef uri="antlib:org.apache.ant.antunit"
               resource="org/apache/ant/antunit/antlib.xml"
               classpath="${test.classpath}" /></project>

This appears to work quite well, and runs my tests a sight quicker than before. If anybody from the apache rat project is listening, you might want to update your POMs. 🙂

m2eclipse tip

I was wondering why one of my computers seemed to have an older version of m2eclipse installed. It turns out that they moved the update site from m2eclipse.codehaus.org/update/ to m2eclipse.sonatype.org/update/. Doh. It’d be nice if they mentioned that one the front page.

q4e

I hate maven. The UI sucks so badly, it’s incredibly painful to use. Anything that can take the edge off this has to be a good thing. In the past, I’ve experimented with m2eclipse, but to be quite frank, it’s not much improvement over the command line. And the tools are why we use Java instead of Perl/Python/Ruby, right?

So now I’m trying out q4e, which is being promoted as the “official” maven integration for Eclipse. At some point. Hopefully.

After installing (0.4.0), first impressions are good. There’s a “new maven project” wizard, which knows about archetypes. Creating archetypes manually is incredibly annoying (I can never remember how). Now, the wizard just lists them for you:

The q4e archetypes wizard

Afterwards, it nicely prompts you for details like groupId and artifactId. Plus a description, which I’d never have thought of otherwise. The more metadata the better! (Actually, the description appears to vanish once the project has been created).

Once created, this is what you end up with.

A new maven project created by q4e

Nice and simple so far. Most of the maven commands are on the right-click menu:

q4e's main menu

When you run something, you get a log viewer, which is a little nicer than the m2eclipse console. At least it’s timestamped.

The q4e log view

Unfortunately, the dependency management appears to be broken. I can’t search for dependencies in the way I could in m2eclipse. That’s not good, as there are lots of them available and the computer should be telling me what the heck they are.

Annoyingly, it seemed to “lose” my src/main/java folder. I had to recreate it in order to get it to work. Very odd.

It will graph dependencies, which is probably more useful on a larger project than my test app.

The q4e dependency viewer

There’s no help yet, which is annoying, though not critical (I don’t know anyone who uses Eclipse help much anyway. Developers—who’d believe it, eh?)

The one thing that’s been really bugging me with maven is the complete inability to get at the source code of dependencies. Oh sure, it’s available for download, and there are features to ask for the source to be downloaded. But I have no idea how on earth to make it work. Not having source code is a real problem when it comes to understanding software.

It didn’t offer much help in the way of creating tests (A default src/test/java would have been nice).

Overall, it’s pretty clear that this is a very early stage of development. But it still looks more promising than m2eclipse, and anything that can make maven easier to use is most welcome.

That was all written last night. This morning, I’ve updated to the development build (0.5.0). It’s managed to grow a “Fetch Source JARs” command, which is great. Unfortunately, I’ve just bumped into issue 153—the compiler settings aren’t synced between maven and eclipse. Oh well. I stand by the previous paragraph. This has potential, but it’s not there yet.

Trusting your tools

After Grzegorz’s piping up, I’m giving cocoon 2.2 another try. Here are some selected errors.

  javax.servlet.ServletException: No block for /favicon.ico
          at org.apache.cocoon.servletservice.DispatcherServlet.service(DispatcherServlet.java:84)
          at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
          at org.apache.cocoon.tools.rcl.wrapper.servlet.ReloadingServlet.service(ReloadingServlet.java:89)
          at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
          at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1093)
          at org.apache.cocoon.servlet.multipart.MultipartFilter.doFilter(MultipartFilter.java:119)
          at org.apache.cocoon.tools.rcl.wrapper.servlet.ReloadingServletFilter.doFilter(ReloadingServletFilter.java:50)
          at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
          at org.apache.cocoon.servlet.DebugFilter.doFilter(DebugFilter.java:169)
          at org.apache.cocoon.tools.rcl.wrapper.servlet.ReloadingServletFilter.doFilter(ReloadingServletFilter.java:50)
          at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
          at org.apache.cocoon.tools.rcl.wrapper.servlet.ReloadingSpringFilter.doFilter(ReloadingSpringFilter.java:69)
          at org.apache.cocoon.tools.rcl.wrapper.servlet.ReloadingServletFilter.doFilter(ReloadingServletFilter.java:50)
          at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
          at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
          at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
          at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
          at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712)
          at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
          at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:211)
          at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
          at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
          at org.mortbay.jetty.Server.handle(Server.java:313)
          at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:506)
          at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:830)
          at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:514)
          at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
          at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:381)
          at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:396)
          at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:442)

How fabulous! 30 lines to tell me about a 404 I couldn’t care less about! (this is from mvn jetty:run). And in the process, obliterating any messages I did care about.

  [ERROR] VM #displayTree: error : too few arguments to macro. Wanted 2 got 0
  [ERROR] VM #menuItem: error : too few arguments to macro. Wanted 1 got 0
  [INFO] ------------------------------------------------------------------------
  [INFO] BUILD SUCCESSFUL
  [INFO] ------------------------------------------------------------------------

There’s an error, but the build was successful. That makes sense. Not.(from mvn site).

  Caused by: org.codehaus.plexus.util.xml.pull.XmlPullParserException: TEXT must be immediately followed by END_TAG and not START_TAG (position: START_TAG seen ...<reports>\n            <report>... @118:21)
          at org.codehaus.plexus.util.xml.pull.MXParser.nextText(MXParser.java:1063)
          at org.apache.maven.model.io.xpp3.MavenXpp3Reader.parseReportPlugin(MavenXpp3Reader.java:3572)
          at org.apache.maven.model.io.xpp3.MavenXpp3Reader.parseReporting(MavenXpp3Reader.java:3709)
          at org.apache.maven.model.io.xpp3.MavenXpp3Reader.parseModel(MavenXpp3Reader.java:2347)
          at org.apache.maven.model.io.xpp3.MavenXpp3Reader.read(MavenXpp3Reader.java:4422)
          at org.apache.maven.project.DefaultMavenProjectBuilder.readModel(DefaultMavenProjectBuilder.java:1412)
          ... 17 more
  [INFO] ------------------------------------------------------------------------
  [INFO] Total time: 2 seconds
  [INFO] Finished at: Sun Jan 27 22:25:35 GMT 2008
  [INFO] Final Memory: 1M/2M
  [INFO] ------------------------------------------------------------------------

XML parser exception (and in only 40+ lines!). Complaining about unbalanced tags? Must be non-well-formed XML, right? Wrong. This is down to copying an example from “Better builds with maven”. But the example’s wrong—I’m missing a tag. Can you guess what’s missing from this XML?

  <reportSets>
    <reports>
      <report>dependencies</report>
    </reports>
  </reportSets>

You mean you didn’t spot the missing reportSet element? I’m shocked, I tell you, shocked. Plus the lack of indication that an error actually occurred. The stacktrace is a good indication, but an actual “ERROR” or “BUILD FAILED” message would be nice (there is an error line, but it zoomed past three screens ago. I blinked and missed it).

So that’s two strikes to maven and one to cocoon. My trust in them is basically non-existent at this point. But at least the RCL worked as documented.

A Year in Cocoon

The other large part of the project at $WORK I’ve just finished was Cocoon. Cocoon is a Java web framework. It’s got some really neat ideas in it, and it’s main purpose in life is transforming XML. It is (or should be) a perfect match for XML databases.

I described Cocoon about a year ago, towards the start of this project. But how do I feel about it now? Looking back, was it the right choice?

To start with, I’m still very impressed with the core Cocoon technologies. The pipelines are perfect for dealing with XML. FlowScript still impresses the heck out of me.

But there’s a lot that leaves a sour taste. My first original complaint was about the size. Well, 50Mb isn’t so big. But the fact of the matter is that there’s an awful lot of stuff in there, quite a bit of which you don’t want to touch with a bargepole. We wasted a lot of time looking at things like XSP, Actions and implementing our own custom Generators. I wish I’d been made more aware of what FlowScript was up-front, and what it could do for more. I wish I’d realised that it’s basically the “C” in MVC.

Which dovetails straight into another complaint: documentation. There is quite a bit of documentation for Cocoon. But it’s still inadequate given the gargantuan size of the project. And the coverage is extremely spotty. Normally, I’d jump straight to the published literature, but the most recent Cocoon book I could find was hideously out of date. In fact, that’s what caused me to go down several of the rabbit-holes mentioned above.

When I’ve really needed to figure out what’s going on, I’ve invariable had to turn to the cocoon source code. Which due to it’s dependence on the weird-yet-not-wonderful avalon framework made it less than simple to understand.

My complaint about debugging still holds, although less severely. You get used to the seemingly-intractible error messages. You spot the patterns that are causing trouble. Like most things, logging goes a long way.

And then there’s Cocoon 2.2. My site was developed entirely on Cocoon 2.1. This certainly had it’s flaws—figuring out how to deploy it as a war file sensibly was a pain. But Cocoon 2.2 has Maven.

I’ve pointed out my dislike for Maven before. As have other people. Recently, other people in my office have been using it and I’ve witnessed the project overruns thanks to trying to figure out what Maven is doing. Nice idea, bad implementation.

Cocoon 2.2 uses Maven because it’s “modularized”. What this means is you can’t have a single project with everything in it any more. You have a “webapp” project and a “block” project. And when something changes you have to build the block, install it, build the webapp, mount the installed block in the webapp and fire up jetty. It doesn’t make for a good development environment.

Now I could be completely wrong and missing the obvious way to do seamless Cocoon 2.2 web development from with Eclipse. I’d love to be corrected. But for now, Cocoon 2.2 has shot itself in the foot as far as I’m concerned.

So I’m not happy with the future direction of Cocoon. I need to look again at why I chose it in the first place. Initially, it was because all the other web frameworks for Java (Struts, Tapestry, Wicket) all seemed totally focussed on form-based CRUD-style web apps. Cocoon focussed on documents and URLs instead. So it’s time to start working my way through the tutorials of the many java web frameworks in order to find a more suitable one. I may not find a good replacement for Cocoon. But I certainly need to try.

Update: In the comments, Grzegorz Kossakowski points out a screencast about the RCL which slightly lessens the pain of interactive development with Cocoon 2.2.

Maven

After being a little bit ranty about cocoon yesterday, I thought I’d better take a closer look at Cocoon 2.2. But that means getting to know Maven first…

The premise of Maven is simple. Instead of having a scriptable build system (as with ant), the build system knows how to do things. And it does them in a standard fashion. You might call this convention over configuration. I like this idea, it simplifies things a lot.

To use Maven, you create a pom.xml (Project Object Model) file and then run mvn package. You should end up with a jar file for your project. This is a sample POM file:

  <project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                        http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.happygiraffe.app</groupId>
    <artifactId>my-app</artifactId>
    <name>my-app</name>
    <version>1.0-SNAPSHOT</version>
    <description>My first Maven project???</description>
    <url>http://maven.apache.org</url>
    <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.2</version>
      </dependency>
    </dependencies>
  </project>

Ah, but how did I get this far? As it turns out, Maven has the equivalent of the rails command to generate new projects for you. Maven calls it an archetype. Here’s a simple example of generating a new project from an archetype from the tutorial:

  mvn archetype:create \
    -DarchetypeGroupId=org.apache.maven.archetypes \
    -DgroupId=com.mycompany.app \
    -DartifactId=my-app

Piece of cake, huh? And guess what? mvn --help gives no clue about any of that.

The second annoyance is quite how verbose Maven is. I know I’m an old-school Unix guy (silence is golden), but this is taking the piss:

  % mvn package
  [INFO] Scanning for projects...
  [INFO] ----------------------------------------------------------------------------
  [INFO] Building my-app
  [INFO]    task-segment: [package]
  [INFO] ----------------------------------------------------------------------------
  [INFO] [resources:resources]
  [INFO] Using default encoding to copy filtered resources.
  [INFO] [compiler:compile]
  [INFO] Compiling 1 source file to /Users/dom/work/my-app/target/classes
  [INFO] [resources:testResources]
  [INFO] Using default encoding to copy filtered resources.
  [INFO] [compiler:testCompile]
  [INFO] Compiling 1 source file to /Users/dom/work/my-app/target/test-classes
  [INFO] [surefire:test]
  [INFO] Surefire report directory: /Users/dom/work/my-app/target/surefire-reports

  -------------------------------------------------------
   T E S T S
  -------------------------------------------------------
  Running net.happygiraffe.app.AppTest
  Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.164 sec

  Results :

  Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

  [INFO] [jar:jar]
  [INFO] Building jar: /Users/dom/work/my-app/target/my-app-1.0-SNAPSHOT.jar
  [INFO] ------------------------------------------------------------------------
  [INFO] BUILD SUCCESSFUL
  [INFO] ------------------------------------------------------------------------
  [INFO] Total time: 9 seconds
  [INFO] Finished at: Tue Apr 17 08:51:40 BST 2007
  [INFO] Final Memory: 5M/14M
  [INFO] ------------------------------------------------------------------------

What’s worse are the error messages. Check this out:

  % mvn pakage
  [INFO] Scanning for projects...
  [INFO] ------------------------------------------------------------------------
  [ERROR] BUILD FAILURE
  [INFO] ------------------------------------------------------------------------
  [INFO] Invalid task 'pakage': you must specify a valid lifecycle phase, or a goal in the format plugin:goal or pluginGroupId:pluginArtifactId:pluginVersion:goal
  [INFO] ------------------------------------------------------------------------
  [INFO] For more information, run Maven with the -e switch
  [INFO] ------------------------------------------------------------------------
  [INFO] Total time: < 1 second
  [INFO] Finished at: Tue Apr 17 08:53:08 BST 2007
  [INFO] Final Memory: 1M/2M
  [INFO] ------------------------------------------------------------------------

So buried somewhere in there is the problem (“pakage” instead of “package”), but it doesn’t even warrant an error? Pretty unfriendly behaviour.

The third problem is shown up the first time you run a Maven command. I didn’t show it above because I’ve been playing with it already. But most of Maven’s behaviour is implemented as plugins. And these plugins are downloaded on first use. The Maven download itself is only about 1Mb.

Now, I don’t have a problem with plugins, or them being downloaded. But, for a fresh install, I would really rather be able to unpack and go. I don’t always have Internet access. On top of this, I have no idea about the provenance of the code that’s being downloaded. And the source code isn’t automatically brought along with it (unlike CPAN or rubygems). So you really have no idea what’s going on and you just have to trust that it’s doing the right thing. Trust is earned, though. I don’t trust Maven to do the right thing yet.

With all those complaints, I still think Maven is useful. The dependency management (ability to pull in dependent jars) is fantastic. The standardization of project layout is a boon. The ability to simply produce code quality reports is kind of handy. The Maven eclipse plugin is pretty reasonable. Indeed, the eclipse support for Maven itself (used to create the .project and .classpath files for Eclipse) is great.

But if Maven wants to see wider adoption, it really, really needs to spend some time working on the UI issues. They’re not easily dismissable just because this is a developer tool.

Anyway, after working my way through the tutorial I’m comfortable enough to start playing with cocoon 2.2 now.