Categories
Uncategorized

Maven Irritation

I’m looking at Maven related stuff for the first time in … perhaps 4 years. This is long enough that I’ve forgotten how irritating it is. My task is simple: redirect the writes from the project’s directory to another location on local disk. This is because the project is located on a filesystem where writes are expensive (something like an NFS filer, but worse). I have to do this for all projects, not just my own, so saying “edit the POM to do X” is not an option. Given how disparate the projects are, there isn’t even a company-wide POM I can alter.

Of course the maven documentation tells you everything you don’t want to know. So, off to stack overflow.

First, is the helpful description of ${project.build.directory}. This is exactly what I want: a way to affect all settings related to the output directory (target by default). See pom-4.0.0.xml for how this works.

Except… it doesn’t work. You can set -Dproject.build.directory=/tmp on the command line, and maven will happily ignore you. I think this is because AbstractCompilerMojo marks it as read-only.

It turns out there’s another way to do this, via profiles.

<profile>
  <id>move-output-dir</id>
  <activation>
    <activeByDefault>true</activeByDefault>
  </activation>
  <build>
    <directory>${java.io.tmpdir}/maven-builds/${project.groupId}/${project.artifactId}/target</directory>
  </build>
</profile>

This works perfectly! But it has to be done on a pom-by-pom basis. What I want is to use maven’s settings.xml to do this. So I pasted it into the profile section there and got:

[WARNING] Some problems were encountered while building the effective settings
[WARNING] Unrecognised tag: ‘build’ (position: START_TAG seen …n … @263:14) @ /…/conf/settings.xml, line 263, column 14

It turns out that profiles in settings.xml are different to profiles in pom.xml (“The profile element in the settings.xml is a truncated version of the pom.xml profile element.”)

At this point, I’m kind of stuck. Maven has provided the illusion of configurability, but all attempts to do so have failed.

Categories
Uncategorized

The importance of Central

One of the selling points of maven is it’s dependency mechanism. You say what code you need, and maven makes sure it’s there for you. The magic behind this is called central. It’s a phenomenal collection of software (much akin to Perl’s CPAN).

This is useful enough that other projects have sprung up that also use central, like apache ivy.

Central also has source code jars (most of the time). This means it’s possible to jump from your piece of code seamlessly into 3rd party code that’s available on central. I can’t imagine getting up to speed with other people’s code anywhere near as quickly without this sort of facility available. Sure, the source is available, but is it a single click away from your code? Not likely. m2eclipse and NetBeans 6.7 make this trivial.

So, having your open source project on central is a really good idea. It lets your users get at it easily and automatically. You really want to do this.

However, getting your projects on to central isn’t simple (see Guide to uploading artifacts to the Central Repository). It boils down to two choices:

  • Package up your code using mvn repository:bundle-create and file a Jira issue for it. This can take four weeks or more.
  • Set up and host an rsyncable repository somewhere. This can be automatically pulled into central.

Both of these options are fairly painful. That’s why for the latest release of jslint4java, I was pleased to see that Sonatype are offering an alternative: oss.sonatype.org. This is much simpler because you can just use the usual maven deployment mechanism (or presumably ivy equivalent). A short while later, oss.sonatype.org will sync up with central.

However, the sonatype guys are (rightly) concerned with the quality of artifacts on central. You have to jump extra hoops to go down this road with your open source projects. The main one that tripped me up was requiring all artifacts be GPG signed, which entailed learning GPG and the maven-gpg-plugin. This took me some time. However, subsequent releasing to central is much easier now I’ve been through all the hoops.

  • If you want to get your open source project on central, I’d definitely recommend checking out oss.sonatype.org.
  • If you want to contribute to an open source project, offering to help get it on to central could be very useful. You’ll not just be helping that project, you’ll also be helping all users of central. The bigger it gets the more useful it is.
Categories
Uncategorized

Documenting with maven

Recently, I’ve been thinking about documentation more and more. I’ve just finished up some documentation for an internal product, which was all done using the maven-site-plugin and APT. This worked reasonably well: I get a generated website, with javadocs and a few howto pages I wrote.

But it really feels like it starts to fall apart when you get to a multimodule build. Maven does quite a few nice things for you, but you start to see some annoying glitches here. One of the first ones I came across was the url element. If you set it correctly in the root pom (e.g. http://mycompany.com/project/) then maven automatically appends the artifactId when constructing the site for submodules. So you end up with http://mycompany.com/project/mysubmodule/, which isn’t necessarily correct.

After thinking about this for a while, and how best to document jslint4java, I’ve come to the same realisation as Mark Pilgrim: plain old HTML works best. I’ve tried a few like Textile (yuck), Markdown (better), POD (too simple) and docbook (too complex). HTML strikes the right balance. Especially with the new HTML5 tags. And it doesn’t require any building to view it. Just hit refresh.

So how to make it part of the maven build? Easy. Stuff it into it’s own submodule. Then, it can be referenced by the assembly plugin later and put in the correct place in your distribution. Check out jslint4java-docs for details. It would be nice if maven natively supported zip packaging, but that’s easily overcome.

This does mean it doesn’t get automatically deployed to a server. But that doesn’t really matter. It happens infrequently enough that I’m happy to do that by hand where needed. Or at least find a better solution when I get bored of that. 🙂

Categories
Uncategorized

jslint4maven

A real maven plugin for jslint4java would be nice. I will write one, but until then, you can always get away with using the antrun plugin. This is fairly simple to do now that jslint4java is available in the central maven repository.

Here’s a plugin definition of how to do it.


  
    
      
        org.apache.maven.plugins
        maven-antrun-plugin
        1.3
        
          
            com.googlecode.jslint4java
            jslint4java-ant
            1.3
          
        
        
          
            jslint
            test
            
              run
            
            
              
                
                  
                  
                
              
            
          
        
      
    
  

Notice how you can add the jslint4java-ant dependency to the antrun plugin without affecting the dependencies of the project.

At the test phase, maven will now run jslint for you. This relies on a small external ant build file to actually perform the jslint task.



  
    
      
      
    
  

We can use the nice antlibs namespace style of declaration because the jslint4java jar is in the classpath already.

Here, I’m assuming that we’re in a war project and want to validate all the files under src/main/webapp. If this goes wrong, you’ll get an error and the build will stop. For example:

…
Tests run: 26, Failures: 0, Errors: 0, Skipped: 0

[INFO] [antrun:run {execution: jslint}]
[INFO] Executing tasks

jslint:
[jsl:jslint] contact.js:1:8:Bad line breaking before '+'.
[jsl:jslint]         + " Nulla in felis Aliquam luctus Proin tincidunt nisi Donec suscipit"
[jsl:jslint]         ^
…
[jsl:jslint] load.js:17:6:Missing semicolon.
[jsl:jslint]     })
[jsl:jslint]       ^
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] An Ant BuildException has occured: The following error occurred while executing this line:
/Users/dom/work/someproj/jslint.xml:4: 3 files did not pass JSLint

One side note: originally, I tried to fold the jslint.xml file into the POM as well. But this failed to load the antlib. I suspect that xmlns attributes were not being passed in to the tasks definition. It’s not a big deal..

Categories
Uncategorized

Conditional maven modules

This is a little something that’s now caught me out a few times. Quite often, I’ll have a project with a parent POM, and a module that I only care about in certain circumstances. So I naturally do this.

  
  
    
      foo-core
      foo-webapp
    
    
      
        dist
        
          foo-dist
        
      
    
  

Unfortunately, this can interact badly with the release plugin. Unless that profile is activated during release, the module won’t have it’s version updated. So the next time you use that profile, it’ll break.

The correct solution is to “push down” the profile into the foo-dist module. i.e.

  
  
    foo
    foo-parent
    1.0-SNAPSHOT
    
      foo-core
      foo-webapp
      foo-dist
    
  

  
  
    foo-dist
    
      foo
      foo-parent
      1.0-SNAPSHOT
    
    
      
        dist
        
      
    
  

That way, foo-dist is always available, but is a noop unless the profile is activated. But it does ensure that the release plugin (and also the versions plugin) know that it’s there.

Categories
Uncategorized

Using a Java 6 based Eclipse with Cocoa

This is somewhat niche, but I’m going to post it anyway in case it helps somebody else

I recently saw a problem with Eclipse and m2eclipse. When I tried to import a Java 6 based project, I got an error in the maven console.

Failure executing javac, but could not parse the error:
javac: invalid target release: 1.6
Usage: javac  
where possible options include:
 -g                         Generate all debugging info
 -g:none                    Generate no debugging info
 -g:{lines,vars,source}     Generate only some debugging info
 -nowarn                    Generate no warnings
 -verbose                   Output messages about what the compiler is doing

This is happening because m2eclipse is trying to run the compiler from within the same JVM that Eclipse is running. And by the eclipse.org downloads only offer Carbon and Cocoa options. Both of these are 32 bit. Which means they’ll only ever run using Java 5, even if you’ve got Java 6 installed (unlike my iMac G5, grumble, grumble).

Thankfully, the 64 cocoa version is available, though it’s only the old “Eclipse SDK” download. But ekke wrote up [galileo] EPP for Cocoa 64-bit, which shows how to go about getting (effectively) the same setup as the eclipse.org downloads.

If you follow along that procedure, you get an eclipse that works well with m2eclipse and Java 6 project. As a bonus, it feels quicker to me.

Hopefully future versions of Eclipse will offer a 64-bit cocoa download.

Categories
Uncategorized

gnupg very basically

I’m trying to get jslint4java into central, via oss.sonatype.org. Part of this requires that you use the maven-gpg-plugin to sign your artifacts. All well & good, but I’ve never used GPG before (though I’ve been playing with SSL certificates for years).

So, following along the howto, I did:

$ gpg --gen-key
gpg (GnuPG) 1.4.9; Copyright (C) 2008 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) DSA and Elgamal (default)
   (2) DSA (sign only)
   (5) RSA (sign only)
Your selection? 1
DSA keypair will have 1024 bits.
ELG-E keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048)
Requested keysize is 2048 bits
Please specify how long the key should be valid.
         0 = key does not expire
        = key expires in n days
      w = key expires in n weeks
      m = key expires in n months
      y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) "

Real name: Dominic Mitchell
Email address: dom@happygiraffe.net
Comment:
You selected this USER-ID:
    "Dominic Mitchell "

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
+++++.++++++++++.++++++++++.+++++++++++++++.+++++++++++++++.+++++...++++++++++.+++++.+++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++>+++++......+++++
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
..++++++++++++++++++++.++++++++++++++++++++++++++++++...++++++++++++++++++++++++++++++.++++++++++++++++++++++++++++++.+++++++++++++++.+++++...++++++++++.+++++>.++++++++++>..+++++>+++++.......+++++^^^
gpg: /Users/dom/.gnupg/trustdb.gpg: trustdb created
gpg: key A24D5076 marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   1024D/A24D5076 2009-06-24
      Key fingerprint = 2F2E 85D8 A945 41C2 B7D1  667A 8616 2CE5 A24D 5076
uid                  Dominic Mitchell 
sub   2048g/4C2D8074 2009-06-24

As an aside, I am using gnupg 1, as I had some issues with the maven-plugin and gnupg 2. And it was simpler to just install gnupg 1 than fix the issues. 🙂

This creates a bunch of files in ~/.gnupg:

$ ls -l ~/.gnupg
total 64
-rw-------  1 dom  dom  9154 21 Jun 20:39 gpg.conf
-rw-------  1 dom  dom  1171 24 Jun 20:44 pubring.gpg
-rw-------  1 dom  dom  1171 24 Jun 20:44 pubring.gpg~
-rw-------  1 dom  dom   600 24 Jun 20:44 random_seed
-rw-------  1 dom  dom  1320 24 Jun 20:44 secring.gpg
-rw-------  1 dom  dom  1280 24 Jun 20:44 trustdb.gpg

Next, it needs to be published on to one of the key servers. The default configuration comes set up with a keyserver keys.gnupg.net. You can send your key up there easily:

$ gpg --send-keys A24D5076
gpg: sending key A24D5076 to hkp server keys.gnupg.net

And now it’s published.

Integrating this with your maven build is fairly simple. The example configuration works exactly as expected. I did one thing slightly differently: I created a gpg profile, and then referenced that from the release plugin. That means I’ll only sign releases, not all builds. Which seems reasonable enough to me.


  
    
      
        
          org.apache.maven.plugins
          maven-release-plugin
          2.0-beta-9
          
            gpg
          
        
      
    
  
  
    
      gpg
      
        
          
            org.apache.maven.plugins
            maven-gpg-plugin
            …
          
        
      
    
  

Categories
Uncategorized

The Maven Ecosystem

Last night I went to see Jason van Zyl of sonatype talking about various bits of the maven ecosystem, and where they’re going. The main bit for me was what’s coming up in maven 3.0. There was a great deal of talk about OSGI related issues, but it reinforced my belief that whilst there’s some good technology in there, it’s still quite complicated to use and manage. Steps are being taken to address this (better tooling support), but they’re not there yet. Also, for the kind of things I do (simple, content-driven, somewhat static webapps), it doesn’t seem to be necessary anyway.

So what’s coming up in maven 3.0? Fundamentally, there won’t be that many new user-visible features (wait for 3.1!). Internally, there have been huge refactorings by the sound of things (along with integration tests to ensure no user-visible regressions). They’re switching away from plexus and towards guice + peaberry. But that’s internal detail. And in theory, it shouldn’t matter even if you’re a plugin author.

What sounded really nice was the focus on making life much easier for users of the embedded maven. Primarily, this means IDE authors. Things like plugin & lifecycle extension points, and incremental build support should allow m2eclipse to be much, much more intelligent about the work they do. Jason mentioned that a version of m2eclipse which builds on the trunk of maven 3.0 can now build the trunk of maven in seconds rather than minutes. Why? Because it’s not duplicating work that’s already been done by Eclipse.

The main change is to the artifact resolution system. It’s been one of the main source of bugs in maven 2.0. It’s been completely junked in 3.0 and replaced with mercury, which handles both transport and resolving artifacts. It should be better tested, and things like version ranges much closer to how OSGI does things.

One (minor) change is that the error messages should be much better. That’s a welcome relief.

There are other tidbits that I think are scheduled for 3.1 that should be really nice:

  • everybody’s favourite: versionless parent elements
  • attributes in the POM — hooray, that should make POMs vastly smaller.
  • mixin POMs — should allow much more flexibility in constructing dependencies on both groups of artifacts and groups of plugins.

There were further talks about hudson & nexus, but I’m fairly familiar with these, so I didn’t see much of news to me.

My thanks go to Peter Pilgrim for organising and EMC/Comchango for hosting.

Categories
Uncategorized

dependency complexity

I love the google-collections library. It’s got some really nice features. But, it’s not stable yet. They’ve explicitly stated that until they hit 1.0 it’s not going to be a stable API. So there are changes each release. Nothing major, but changes.

As an example, in the jump from 0.9 to 1.0rc1, the static methods on the Join class became the fluent API on the Joiner class.

(as an aside, could we have some tags, please?)

Following this change is simple.

@@ -310,7 +310,7 @@
         } catch (KeyStoreException e) {
             throw new RuntimeException(e);
         }
-        return Join.join(" | ", principals);
+        return Joiner.on(" | ").join(principals);
     }

     /**

But the knock-on effect comes when you start getting lots of things which have google-collections dependencies. At $WORK, I’ve got a project whose dependencies look like this.

dc2-deps-before.png

I wanted to extract a part of DC2 into its own library, commslib. This was pretty easy as the code was self contained. Naturally, I wanted it to use the latest version of everything, so I upgraded google-collections to 1.0rc1. Again, fairly simple.

This is what I ended up with.

dc2-deps-after.png

Except that now there’s a problem.

  • commslib uses Joiner, so it’ll blow up unless it upgrade DC2‘s google-collections to 1.0rc1.
  • GSK uses Join, so it’ll blow up if I upgrade DC2‘s google-collections to 1.0rc1.

And thus have I painted myself into a corner. 🙂

As it happens, DC2 had a dependencyManagement section forcing everything to use google-collections 0.8. → Instant BOOM.

The solution is to upgrade all my dependencies to use google-collections 1.0rc1. But this turns out to be a much larger change than I had originally envisaged, as now I have to create releases for two dependent projects. This isn’t too much of a hassle in this case (yay for the maven-release-plugin), but it could be a large undertaking if either of those projects is not presently in a releasable state.

I’m not trying to pick on google-collections (I still love it). I’m just marvelling at how quickly complexity can blossom from something so simple.

Categories
Uncategorized

Github Pages with Maven

Github recently introduced their pages feature, for serving static content. This sounds like an ideal match for a maven , so I thought I’d give it a go. I managed to get happygiraffe.github.com/tclogview/ up and running in fairly short order, but I’m not totally happy with the end result. And that’s not just the fault of the default maven site.

First things first. We need to set up the gh-pages branch as devoid of content. I nicked most of this from drnic’s sake-tasks, even though the instructions are also on pages.github.com.

git symbolic-ref HEAD refs/heads/gh-pages
rm .git/index
git clean -fdx
echo "

Coming soon!

" >index.html git add index.html git commit -a -m 'Initial page' git push origin gh-pages

Soon, you should be able to see the “Coming soon!” message on you.github.com/yourproj/.

Now that’s there, we need to set it up so that maven can deploy the site to it. First, let’s add the branch as a submodule.

repo=`git config remote.origin.url`
git submodule add -b gh-pages $repo site

That should create a directory “site” which contains the index.html you committed a moment ago. Now you have to ask Maven to deploy the site there. Add this to your POM.

  
    
      gh-pages
      
      file:${project.basedir}/site
    
  

Now you can say mvn site-deploy and it will fill up the site directory with lots of lovely HTML. Then you have to commit it (separately, since it’s a submodule) and push it back to github.

  mvn site-deploy
  (
    cd site
    git add .
    git commit -a -m 'Generate site.'
    git push origin gh-pages
  )

And shortly thereafter it shows up on the web site. Magic.

I like this a lot in principal. But I have a few issues with it as well. Mostly, this is down to git submodules being quite a blunt instrument:

  • They don’t auto-update when there’s a new commit in the submodule — they point at a fixed commit. You have to ask for it to be updated.
  • On top of that, you can’t point the submodule at a branch. It’s always pointing at a specific commit.
  • The submodule location is recorded in .gitmodules. The main issue I have with this is I can’t find a way to say “my current repository”. Why is this a problem? Well, if somebody forks the codebase (and they will, this is git) then they want their site to point at their gh-pages branch, not mine.
  • The submodule location is my private push URL, not the public one. Which is all well and good, but when somebody checks out the project, they’re going to have an error when they try to check it out. As I found out when setting up the build in hudson.
  • When somebody else checks out the repository they have an extra step to go through to fetch the submodule contents (git submodule init git submodule update).

I think that next time I try this, I’ll skip submodules and just checkout a second copy of the repo in ../project-site.

I still think it’s the right way to go. Not just for the site, but you can also use github pages to serve up a maven repository in a very similar fashion. Github and maven do seem to go together rather well.