Monday, April 30, 2012

Critical failures of Apache Maven Central

by Richard Vowles

Today's failure of the meta-data of logback really hit us hard and resulted in significant downtime. What was the issue? Its an issue that appears to happen relatively frequently - someone releases a new version of their library and the release process blows away the Maven metadata - making it appear to anything checking the meta data that there is only one version available - the current one.

As at time of writing, the Maven metadata for logback-classic looks like this:










We are using the version range [0.9.17] for Grails 1.3.7 projects and [1.0.1] for Grails 2.0.3 projects. This is good Maven hygiene - we want the build to fail if these specific versions are not available for a good reason (such as no repository available) - not for a bad reason like someone broke the repository. We don't want Maven to choose a different version, we want that specific version.

When you use version ranges, you need the meta data - that tells Maven what versions are available. If you don't specify a range, it shouldn't use the meta data. However... With Maven 3.0.4 today, that turned out not to happen for some reason.

Whats worse, is that when we wanted to work around the problem by relaxing the version ranges, it didn't work. It just got worse and weirder. Eventually, on the brainwave of Michael McCallum - installing the artifact in our 3rd party repository and moving it ahead of Central in the Nexus allowed us to get back to work.

Now if this was the first time this happened, it wouldn't ring such alarm bells, but it isn't. Its at least the third (SOLR being the first time we hit it). I'm now in the unenviable position to have to discuss with my team whether direct Apache Maven Central access will be banned. If the artifact isn't taken from Central and put into our 3rd party repo, it can't be used. We just cannot afford the downtime.

What I don't understand is why there is no automatic process for repair for Central. Its a critical resource, which we enormously appreciate but its value in accessing directly has become much lessened after today's extreme waste of productive, valuable time.

Update: Another one -

Friday, April 27, 2012

Code Lounge, Post Mortem + Hangout Lounge

by Richard Vowles

We held our first Code Lounge on Wednesday - Anzac Day in Auckland. We went to the morning service at Glendowie College and then proceeded a rapid clean up to ensure we were ready for the three people arriving. I've posted elsewhere about the results (ZeroMQ vs RabbitMQ vs Pusher), the day went pretty well in my opinion - we stopped every hour and made sure we were aware of what the other team was doing - we set ourselves goals which we overstepped periodically and when we got to the end of the day (3pm) and had only an hour left, we decided whether we continued learning (David and Irina decided to continue getting the Clustering working for Rabbit and trying to work out its fail over strategy) and Mark and I decided to push on with Pusher (which turned out to be an exercise in re-writing the library instead).

All in all a good day. But I was lying on the bed after an exhausting week while listening to my wife practice Cello (which is a surprisingly meditate experience) and thinking about my backlog of books to read. They consist of programming language books (Dart 4 Hipsters for example), Tool books (a few around VIM are bubbling around, Pragprog have just released a new early access), Electronics books (MSP430 and various others), General informational/idealogical books (Information Diet, Biohacking) and fiction. And I'm not really reading them, particularly the programming language ones and I really really want to.

So I'm going to get inspired by Chris Strom (Dart 4 Hipsters, The Spdy Book, Backbone.js recipes) and try starting chains ( and see if I can just knuckle down and self educate.

I spoke with Mark about it tonight and wondered aloud whether or not having a Hangout open when I was working away at a particularly topic might be a worthwhile idea. I think I will do it, I will try just having an open hangout around  a particular topic and ensure I circle Programming related people in my G+ stream. If they want to come in they can otherwise I at least will continue to learn. I'll set a goal for 1 hour a day and see how I get on.

(PS Manning have a great special on at the moment which includes a lot of interesting books, I'm trying desperately to not go and buy them and have them sitting there as well!)

Monday, March 26, 2012

Code Lounge

by Richard Vowles

Last year I started a conference - I was looking for a barcamp style conference that I could really enjoy going to - that would be full of topics I wanted to cover. It eventually got named The Exceptional Conference (given Illegal Argument as the theme) - and it got a good turnout. However, the topics really didn't interest me all that much. They interested a lot of other people however and the feedback was good. We got some Pizza sponsorship from Fronde, the rest was paid for by my company Blue Train Software Ltd.

We had a few people come up from Wellington - which was excellent, I enjoy John and Nigel's company and it was great to meet the other guys who seem to form a really informed and friendly group. John and Nigel decided to run one in Wellington and so it was simply polite that I went - Mark and I went down (I took my son Xavier with me). Unfortunately it was pretty much the same topics, so I didn't enjoy it a whole lot. I felt pretty guilty about that - John, Nigel, and the VUW crew had gone to a lot of trouble to organize it - I just couldn't get into it. Later I just realized that my original goal - something discussing things that I am interested in is hard to find at conferences - even ones of such a small size. I get it from the IllegalArgument podcast, but only in general chat. I also found there wasn't enough core-geek depth.

So when Nigel asked when the next conference was going to take place I said I'd bow out of this one, but more prodding from Nigel and Mark got me thinking, and I really appreciate that they did prod.

What I realized I wanted in reflection was what John, Nigel, Mark and myself did at the start of all this - a geek weekend or even just a single day where we could take a topic or idea and really explore it - a small number of people (4 in that case - perhaps up to 8?) would probably work. Idea had to be fairly tight, people shouldn't know too much about it otherwise its not a learning experience - or perhaps they should - I don't know yet. Close environment, good for pairing, good food, good drinks (coffee for those that like it, water for those that don't :-), good wifi, projector if necessary, white board, all that good stuff.

And I realized I had most of these in my lounge - and so was born the concept of CodeLounge. The idea is to take a topic, advertise your lounge will be a meeting place on a specific date for a specific set of times, specify what you can provide and just go for it. At the moment, I'm not sure how it will turn out - but I am hoping that we'll learn ways of making the experience interesting.

I registered a domain name for it - CodeLounge.IO (a play on Google.IO - a conference I no-longer wish to attend, the videos are good enough), there is no website yet - perhaps that is the first job (Twitter Bootstrap anyone?), but we have a Meetup group and a logo.

I call them a micro-code-camp.

Bluegrails Maven Plugin

by Richard Vowles

There have been some considerable changes made to the Bluegrails Maven plugin - we are starting support for Grails at 2.0.1 - it is quite difficult to ensure compatibility as I have found a number of quirks to how the Grails Gant scripts behave.

We are now using it internally, and it seems to be going well - we found one dependency that wasn't in central (Spring UAA) but that can be excluded and is only used by the plugin (via the scripts inclusion). I hope to have video tutorials up once we are happy that it is solid and working well.

It can be downloaded on Github now under the repository.

Friday, March 16, 2012

Can you afford to not know Git any longer?

by Richard Vowles

As much as people still debate the Subversion vs Git arguments, particularly in the JVM community I asked myself today whether or not you can get away with not knowing the basics of Git any longer. Why? Because 2/3 of your source base (or more) is likely to be open source and an ever increasing portion of that seems to reside on Github - for the better in my opinion as it has a superior mechanism for supporting patches via pull requests. One of the fundamental tenants of open source is that you can fix the bug and with Git,  not being able to get a patch accepted - like with Guava or many Grails plugins) or in a timely fashion so it is released and usable (for example with Guice), is not fatal. At least you will be able to keep merging the upstream changes into your code base with the patches relatively easily (as I recently did with the Grails Maven plugin).

I think any organisation that chooses an open source project that isn't on Git is actually choosing one that is a higher risk for their organization and when you do, you need to have a basic working knowledge of it. If it was me, I'd get every organisation to have a Github organisation account where any forked artifacts reside so as people come and go in their organisation, they have one central place where their patched versions reside. Its not a perfect solution, ideally projects that need patching with do it quickly and update themselves, but the way most projects have their artifacts organized and how often they release is always a problem, so I think it is inevitable and Git offers such a better mechanism than Subversion.

So if you are a JVM software developer and you don't know Git, now is the time to learn. There are great books out there and some excellent screen casts from Orielly, so get on it now!

Friday, March 9, 2012

Grails 2.0.1 and Maven

by Richard Vowles

One of the projects I have been working on is moving from Grails 1.3.7 to Grails 2.0.1 - so I naturally went looking for the Maven artifacts for the project. We use Maven extremely successfully with composites and I have had a talk to Graeme about using them to reduce clutter in the project pom and make things clean.

My first hurdle is that Grails 2.x is not in Apache Maven Central - asking Graeme on twitter gave the answer that it wasn't going to be there and was in Fair enough I thought until I realized that in fact this would cause problems for me - I couldn't release bluegrails because bluegrails needs to be in central. Bluegrails as a refresher is a set of composite dependencies that allow you to be consistent with Grails dependencies in your non Grails artifacts.

Greame's reasoning when @talios prodded him was that Grails had external dependencies that weren't in central - which I understand - if you have that you can't use Apache Maven Central, and it is a rule that makes sense especially from a corporate perspective. Grails run their own repo, just point to it. Yet, still - it has hosed my ability to deploy these artifacts that add considerable value to anyone who uses Maven and Grails generally in their organization and want to maintain version compatibility.

So tonight I decided to check - if I could chase those projects down that weren't in Apache Maven Central and offer to help get them there, I'd hope the Grails team would push to Central.

I downloaded and build the Grails Maven Archetype and Grails Maven Plugin (which wouldn't build in Maven 3.0.4 - there are a couple of annotation problems with it). Once I had those two working, I created an archetype and found to my delight that Graeme had taken the Composite approach to Maven dependencies - the new pom is a delight!


Thats it. Awesome! Exactly how it should be.

Now to ascertain the naughty dependencies - so a mvn dependency:tree

[INFO] - org.grails:grails-dependencies:pom:2.0.1:compile
[INFO]    +- org.grails:grails-core:jar:2.0.1:compile
[INFO]    |  +- commons-io:commons-io:jar:2.1:compile
[INFO]    |  +- org.slf4j:slf4j-api:jar:1.6.2:compile
[INFO]    |  +- org.slf4j:jcl-over-slf4j:jar:1.6.2:compile
[INFO]    |  +- org.hibernate.javax.persistence:hibernate-jpa-2.0-api:jar:1.0.1.Final:compile
[INFO]    |  - commons-lang:commons-lang:jar:2.6:compile
[INFO]    +- org.grails:grails-bootstrap:jar:2.0.1:compile
[INFO]    +- org.grails:grails-crud:jar:2.0.1:compile
[INFO]    +- org.grails:grails-datastore-core:jar:1.0.2.RELEASE:compile
[INFO]    +- org.grails:grails-hibernate:jar:2.0.1:compile
[INFO]    |  +- javax.validation:validation-api:jar:1.0.0.GA:runtime
[INFO]    |  +- org.grails:grails-datastore-simple:jar:1.0.2.RELEASE:compile
[INFO]    |  +- javax.transaction:jta:jar:1.1:compile
[INFO]    |  +- org.hibernate:hibernate-validator:jar:4.1.0.Final:runtime
[INFO]    |  +- org.hibernate:hibernate-ehcache:jar:3.6.7.Final:runtime
[INFO]    |  +- org.grails:grails-datastore-gorm:jar:1.0.2.RELEASE:compile
[INFO]    |  +- org.hibernate:hibernate-core:jar:3.6.7.Final:compile
[INFO]    |  |  +- antlr:antlr:jar:2.7.7:compile
[INFO]    |  |  +- dom4j:dom4j:jar:1.6.1:compile
[INFO]    |  |  - org.hibernate:hibernate-commons-annotations:jar:3.2.0.Final:compile
[INFO]    |  - javassist:javassist:jar:3.12.0.GA:compile
[INFO]    +- org.grails:grails-plugin-codecs:jar:2.0.1:compile
[INFO]    |  - commons-codec:commons-codec:jar:1.5:compile
[INFO]    +- org.grails:grails-plugin-controllers:jar:2.0.1:compile
[INFO]    +- org.grails:grails-plugin-converters:jar:2.0.1:compile
[INFO]    +- org.grails:grails-plugin-datasource:jar:2.0.1:compile
[INFO]    |  +- commons-dbcp:commons-dbcp:jar:1.4:compile
[INFO]    |  - commons-pool:commons-pool:jar:1.5.6:compile
[INFO]    +- org.grails:grails-plugin-domain-class:jar:2.0.1:compile
[INFO]    +- org.grails:grails-plugin-filters:jar:2.0.1:compile
[INFO]    +- org.grails:grails-plugin-gsp:jar:2.0.1:compile
[INFO]    +- org.grails:grails-plugin-i18n:jar:2.0.1:compile
[INFO]    +- org.grails:grails-logging:jar:2.0.1:compile
[INFO]    +- org.grails:grails-plugin-log4j:jar:2.0.1:compile
[INFO]    |  +- log4j:log4j:jar:1.2.16:compile
[INFO]    |  - org.slf4j:jul-to-slf4j:jar:1.6.2:compile
[INFO]    +- org.grails:grails-plugin-mimetypes:jar:2.0.1:compile
[INFO]    |  - commons-collections:commons-collections:jar:3.2.1:compile
[INFO]    +- org.grails:grails-plugin-scaffolding:jar:2.0.1:compile
[INFO]    +- org.grails:grails-plugin-services:jar:2.0.1:compile
[INFO]    +- org.grails:grails-plugin-servlets:jar:2.0.1:compile
[INFO]    +- org.grails:grails-plugin-url-mappings:jar:2.0.1:compile
[INFO]    +- org.grails:grails-plugin-validation:jar:2.0.1:compile
[INFO]    +- org.grails:grails-resources:jar:2.0.1:compile
[INFO]    +- org.grails:grails-spring:jar:2.0.1:compile
[INFO]    |  - org.springframework:spring-web:jar:3.1.0.RELEASE:compile
[INFO]    |     - aopalliance:aopalliance:jar:1.0:compile
[INFO]    +- org.grails:grails-web:jar:2.0.1:compile
[INFO]    |  +- commons-fileupload:commons-fileupload:jar:1.2.2:compile
[INFO]    |  +- xpp3:xpp3_min:jar:1.1.4c:compile
[INFO]    |  - opensymphony:sitemesh:jar:2.4:compile
[INFO]    +- org.grails:grails-webflow:jar:2.0.1:compile
[INFO]    +- org.codehaus.groovy:groovy-all:jar:1.8.6:compile
[INFO]    +- org.aspectj:aspectjweaver:jar:1.6.10:runtime
[INFO]    +- org.aspectj:aspectjrt:jar:1.6.10:runtime
[INFO]    +- cglib:cglib:jar:2.2:runtime
[INFO]    +- asm:asm:jar:3.1:runtime
[INFO]    +- oro:oro:jar:2.0.8:runtime
[INFO]    +- commons-beanutils:commons-beanutils:jar:1.8.3:runtime
[INFO]    +- commons-el:commons-el:jar:1.0:runtime
[INFO]    +- commons-validator:commons-validator:jar:1.3.1:runtime
[INFO]    +- com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:jar:1.2_jdk5:runtime
[INFO]    +- com.h2database:h2:jar:1.2.147:runtime
[INFO]    +- javax.servlet:jstl:jar:1.1.2:runtime
[INFO]    +- net.sf.ehcache:ehcache-core:jar:2.4.6:runtime
[INFO]    +- org.springframework:spring-aspects:jar:3.1.0.RELEASE:runtime
[INFO]    +- org.springframework:spring-jdbc:jar:3.1.0.RELEASE:runtime
[INFO]    +- org.springframework:spring-jms:jar:3.1.0.RELEASE:runtime
[INFO]    +- org.springframework:spring-orm:jar:3.1.0.RELEASE:runtime
[INFO]    +- org.springframework:spring-tx:jar:3.1.0.RELEASE:runtime
[INFO]    - org.springframework:spring-webmvc:jar:3.1.0.RELEASE:compile
[INFO]       +- org.springframework:spring-asm:jar:3.1.0.RELEASE:compile
[INFO]       +- org.springframework:spring-beans:jar:3.1.0.RELEASE:compile
[INFO]       +- org.springframework:spring-context:jar:3.1.0.RELEASE:compile
[INFO]       |  - org.springframework:spring-aop:jar:3.1.0.RELEASE:compile
[INFO]       +- org.springframework:spring-context-support:jar:3.1.0.RELEASE:compile
[INFO]       +- org.springframework:spring-core:jar:3.1.0.RELEASE:compile
[INFO]       |  - commons-logging:commons-logging:jar:1.1.1:compile
[INFO]       - org.springframework:spring-expression:jar:3.1.0.RELEASE:compile

(I removed the \)

And a bit of code (I put that all in a def called deps, its ugly but it doesn't matter) - ignored the org.grails ones as these were accepted to not be in central:

def failed = []

deps.split("\n").each { line ->

  if (line.startsWith("[INFO]")) {
    line.split(" ").each { dep ->
      if (dep.size() > 0 && Character.isLetter(dep.charAt(0)) ) {
        if (!dep.startsWith("org.grails")) {
          //println dep
          String[] splitDep = dep.split(':')
          String url = "${splitDep[0].replace('.', '/')}/${splitDep[1]}/${splitDep[3]}/${splitDep[1]}-${splitDep[3]}.pom"
          try {
            new URL(url).openStream()
            println "ok ${url}"
          } catch ( Exception ex ) {
            println "failed ${url}"

And the result?


No failures.

Now I'm perplexed - it isn't because of dependencies in the Grails artifacts. 


Thursday, February 16, 2012

I will defeat you Grails config loader!

by Richard Vowles

As part of the way we do thing, we wanted to use properties files rather than the plain config.groovy - and thats pretty normal - Grails documentation even gives you a mechanism to load in extra config.

Ideally for us, this could be configured outside of Grails - through some Spring mechanism rather than having to modify the Grails config for every app, but its ok - it has worked. Until today. Today I hit the bug in Groovy where the Properties -> ConfigObject via ConfigSlurper started giving weird results. I had these lines = localhost
grails.mail.port = 5000

and i was getting = localhost
grails.'mail.port' = 5000
grails.mail.port = [:]

which turns out to be a peculiarity of how Groovy loads the file - and there are quite a few bugs around this on the Groovy bug parade - back since 1.6 (and unclosed).

So how to get Grails to load a properties file in my own fashion? Well thats where the fun started. Turns out that Grails hard codes in its scripts the creation of the DefaultGrailsApplication, which in turn has hard coded the ConfigurationHelper. Neither of these were overrideable or extendable without forking Grails 1.3.7. So one of the things we had been doing was using static methods on our class - and in the end, this turned out to be our saviour - that and Groovy's MOP.

As there were lots of parse methods already, I added a methodMissing to detect for a request for a parse with our class and then passed it onto a method of my choosing that would turn the tree into something more resembling common sense.

That seemed to fix it - so if you have the same problem with broken properties loader when using Grails, that approach worked for me.

Wednesday, February 15, 2012

Bad request errors from Sonatype's Nexus during release

by Richard Vowles

So we changed the way we published our artifacts to the repository - going straight to the Nexus, instead of via Apache and having Nexus index them. This meant it was easier from a permissions perspective - making sure the right people could upload artifacts into the right places and Nexus and Apache kept mixing up on-disk file permissions.

That unfortunately turned up a very annoying bug in Nexus - you can't attach 0 length files. Typically for artifacts that could generate a WSDL from code, we'd attach them as a -wsdl classifier artifact into the repository. If there were none, then we would touch the file, so the build-helper plugin could be configured to attach the file, otherwise it would fail.

But the move to Nexus knocked that on the head and we could no longer release web service artifacts - so if you run into a situation when you are releasing artifacts and you get a bad request for no good reason - it may be because you are trying to upload a zero length artifact.

The fix was to remove the zero length file and upgrade to 1.6 of the build helper plugin which has a new capability to skip the attachment if its empty. Our plugin definition now looks something like this:

       <skipAttach>true</skipAttach> <!-- if the file doesn't exist, skip attaching it, added since 1.6 -->

So I don't forget this

by Richard Vowles

One of the things that that I'm always doing is posting to this blog with the SyntaxHighlighter javascript extension. So to make it easy for myself to remember a bit of code to quickly format the code, I'm posting it here:

code goes <> here
'''.each { c -> print '"><&'.contains(c)?('&#'+(int)c+';'):c }

And it works in the online Groovy console.

Friday, February 10, 2012

Android Tablets

by Richard Vowles

Last year I purchased a Samsung Galaxy Tab 10.1 - I was spending a lot of time browsing the news feeds for stuff for the podcasts, being away from a computer and not being able to make use of valuable time. So I decided to grab a Tablet and see how useful it would be. I was also considering one for use by my wife - given the capabilities of Google Docs Drawing, it was pretty awesome in its ability to do something she was paying for Comic Life on her Mac. I really quite like Android 3.1 and it made moving to my Galaxy Nexus easy and familiar.

It turned out way cheaper than I expected and I have very much enjoyed the experience - unfortunately lately the YouTube has been playing up, but other than that its excellent (seems to be a network issue). The cheap $130 APAD 2 I got was totally useless not because of its resistive screen but because the wifi is utterly lousy, even 1 metre away from the wifi and I can't use it.

The problems I now have with it is that to really use it, I need a keyboard - so ideally I'd replace it with a Transformer Prime with a keyboard - in hindsight I should have waited, but I can always get a keyboard for the Galaxy Tab. The lack of a decent Google Docs client for Android continues to make that application wholly useless unfortunately, so if you are looking for one for that reason, don't bother.