Thursday 19 December 2013

Conditional SBT Keys for Jenkins Builds

A little SBT snippet that's proven useful (place this in a file like jenkins-only.sbt alongside your build.sbt):
    // Settings which should only be applied if this SBT is being run by Jenkins

    // Jenkins nodes always have $JENKINS_URL set - that is our telltale:
    lazy val underJenkins:Option[String] = scala.util.Properties.envOrNone("JENKINS_URL")
    def ifUnderJenkins[T](setting: => T):Seq[T] = underJenkins.map ( s=> setting ).toSeq


    // Write a JUnit-format output especially for Jenkins:

    testOptions in Test ++= ifUnderJenkins ( Tests.Argument("junitxml") )

Why: Well in this case, it gives Jenkins the output he/it needs to do pretty "Test Result Trend" graphs and have clickable error messages when tests fail, while simultaneously giving developers the well-structured, detailed test output that SBT gives by default.
How: As mentioned in the code comments, and in the Jenkins documentation, a Jenkins node will have the JENKINS_URL environment variable set, and a dev (probably) won't.
Nice Features:
  • Exploits the fact that an Option[T] can be converted to a zero-or-one-length Seq[T] with toSeq()
  • Parameterized ifUnderJenkins so that any SBT key can be handled conditionally
  • By-name parameter (that's the setting: => T ) means the setting won't even be evaluated unless needed

Monday 18 November 2013

Argument Capture with Scala / Specs2 / Mockito, part 2: The neater way

So in my last post, I indicated my dissatisfaction with the ugliness of creating a Mockito ArgumentCaptor in Scala - turns out someone else had the same thought, and it's already fixed in Specs2:
  "Application Main Controller" should {

    "Pass a non-empty list to the template" in {
      val result = controller.newsAction(FakeRequest())
      status(result) must beEqualTo(200)

      val captor = capture[List[String]]

      there was one(mockedNewsRenderer).apply(captor)
      val theArgument = captor.value
      theArgument.isEmpty must beFalse
    }
  }

Nice.

Sunday 17 November 2013

Using Mockito's ArgumentCaptor in Scala

I've been doing what an old colleague of mine used to call "Stack-Whoring" a bit recently - trying to get a few more badges and also up my general reputation on Stack Overflow. It's been fun and educational, and I wanted to post (mainly for my own reference) how to use Mockito's powerful ArgumentCaptor facility from inside a Specs2 example:

I'm just going to nab my own answer from the relevant SO question:
class ApplicationSpec extends Specification with Mockito {

  val mockedNewsRenderer = mock[List[String] => Html]

  val controller = new ApplicationController {
    override def renderNews(s:List[String]) = mockedNewsRenderer(s)
  }

  "Application News Controller" should {

    "Pass a non-empty list of news items to the template" in {
      val result = controller.newsAction(FakeRequest())

      status(result) must beEqualTo(200)

      val captor = ArgumentCaptor.forClass(classOf[List[String]])

      there was one(mockedNewsRenderer).apply(captor.capture())
      val theArgument = captor.getValue
      theArgument.isEmpty must beFalse
    }
  }
}
The trickiest bit (if you're already familiar with ArgumentCaptor usage in Java) is the use of classOf[T] to get the Scala equivalent of Java's .class so you can then call ArgumentCaptor.forClass().

There's probably scope there for a Pimp My Library tweak to tidy up that particular line - I'm thinking I'd like to just be able to say:
  val captor = ArgumentCaptor.for[List[String]]


Stay tuned ...

Tuesday 29 October 2013

Characteristic Impedance Mismatches

I'm going old-skool on this one - I've got tabular data, I'm gonna use a table, dammit!
You can have this... But not at the same time as this
Yearly IT Budgets Agile Project Development
"We're In The Cloud" Actually Being In The Cloud
Continuous Delivery Change Review Boards
Continuous Improvement Change Review Boards
Clean, Elegant, Responsive web pages Internet Explorer Support

Got any more?

Monday 30 September 2013

Reflecting on Impedance Mismatches

I've been playing around a bit with MongoDB, and in particular its ReactiveMongo non-blocking asynchronous client for Scala. It's fitting in beautifully with what I'm trying to do with it (which is basically a standard CRUD webapp where the primary object in the domain model is some anonymous, arbitrary JSON block), even more so when I brought in the Play ReactiveMongo plugin.

Now I feel I'm really "living the dream"; I can concentrate entirely on my Scala code, sequencing in database fetches and stores when I want them, and not having to worry about the efficiency of table joins (never mind the tedium of getting them even working in the first place!) or mapping column types. The oft-mentioned "SQL-to-OO impedance mismatch" is gone, but even better, the ORM-to-useful code mismatch (which probably gives more pain in the long haul) is gone too.

Which got me to thinking - where are some other impedance mismatches in modern software development, and can they be eliminated as nicely?

Tuesday 6 August 2013

The elusive single FakeApplication specs2 test

As has been noted in numerous places, the Play 2 documentation on testing kinda suggests that spinning up a FakeApplication is something you can/should do in every single one of your test examples, e.g.:

  // Don't do this
class MyControllerSpec extends Specification {

  "My controller" should {

    "return a 404 on top-level GET to non-existent resource" in {
       running(FakeApplication()) {
         val home = route(FakeRequest(GET, "/mycontroller/blurg")).get
         status(home) must equalTo(NOT_FOUND)
       }
     }

     "serve up JSON on list request" in {
       running(FakeApplication()) {
         val home = route(FakeRequest(GET, "/mycontroller/list")).get
         status(home) must equalTo(OK)
         contentType(home) must beSome.which(_ == "application/json")
       }
     }
  }
}
Trust me when I say that it is not a good idea. At the very least slow tests, inconsistent test results and general Weird Things™ will happen.

What you most-likely want is something like this:
import play.api._
import play.api.test._
import org.specs2.specification._
import org.specs2.mutable._

/**
 * Mix this in to your Specification to spin up exactly one Play FakeApplication
 * that will be shut down after the last example has been run.
 * Override 'theApp' to use a customised FakeApplication
 */
trait FakePlayApplication {
  this: Specification =>

  def theApp = FakeApplication()

  def startApp = {
    System.err.println(s"Starting $theApp")
    Play.start(theApp)
  }

  def stopApp = {
    System.err.println(s"Stopping $theApp")
    Play.stop()
  }

  override def map(fs: => Fragments) = Step(startApp) ^ fs ^ Step(stopApp)
}

Which you could use in my previous example as follows:

class MyControllerSpec extends Specification with FakePlayApplication {

  "My controller" should {

    "return a 404 on top-level GET to non-existent resource" in {
         val home = route(FakeRequest(GET, "/mycontroller/blurg")).get
         status(home) must equalTo(NOT_FOUND)
     }

     "serve up JSON on list request" in {
         val home = route(FakeRequest(GET, "/mycontroller/list")).get
         status(home) must equalTo(OK)
         contentType(home) must beSome.which(_ == "application/json")
     }
  }
}
Less repetition, faster execution, and most importantly, RELIABLE TESTS!

Monday 22 July 2013

Fun with Scala/Play, Part 2 (In Asynchronous Which Get We)

As promised, we need to convert our painfully old-fashioned single-threaded pinger into something asynchronous.

Let's look at some output (classic timing code kludged into Part 1's solution - you don't need to see it):
Pinging 'http://www.bar.net'
Pinged 'http://www.bar.net' - got result: 'HTTP/1.1 200 OK' in 440ms
Pinging 'http://www.baz.com'
Pinged 'http://www.baz.com' - got result: 'HTTP/1.1 200 OK' in 240ms
Pinging 'http://www.zomg.com'
Pinged 'http://www.zomg.com' - got result: 'HTTP/1.1 200 OK' in 230ms
Pinging 'http://fe.zomg.com'
Pinged 'http://fe.zomg.com' - got result: 'HTTP/1.1 200 OK' in 242ms
Entire operation took 1155ms
Realistically, we should be constrained only by the slowest element (just like school, right?) and so our "Entire operation" time should be something like 450ms, give or take. Let's fix this up.

I'll be using Play's WS features to achieve this, which means that as a bonus, I get to drop my dependency on Apache HTTP Client. Nothing against it, but less code (even somebody else's) is always better code. Smaller code search-space, smaller deployment artifact, win!

With a lot of help from the Play Async doco and the Akka Futures explanation, I came up with the following changes to the previous single-threaded solution:
  trait Pingable extends Addressable {
    def ping : Future[(String, String, Long)] = {
      println("Pinging '" + address + "'")
      val startTime = Platform.currentTime
      WS.url( address ).get().map { response =>
        val endTime = Platform.currentTime
        val time = endTime - startTime
        println("Pinged '" + address + "' - got result: '" + response.status + "' in " + time + "ms")
        (address, response.statusText, time)
      }
    }
  }

  def sendPing = Action {
    val pingTargets = configuration.getStrings("targets")

    val startTime = Platform.currentTime

    val futurePingResults : List[Future[(String, String, Long)]]  = pingTargets.map( _ ping)
    Async {
      val results = Future.sequence(futurePingResults)
      results.map { tuples =>
        val endTime = Platform.currentTime
        val time = endTime - startTime
        println("Entire operation took " + time + "ms")
        Ok(html.ping(tuples))
      }
    }
  }
Things to note:
  • ping() now returns a Future Tuple3, which will eventually hold the address, status and ping response time
  • This is the result of calling map on the WS's get() which is already returning a Future - we're essentially just massaging the actual return type to the one we want
  • The pingTargets.map() call is unchanged, only its return type (stated explicitly for clarity) has altered
  • The Async block tells Play that we'll be dealing with Futures from here on
  • And, perhaps least obviously, but most importantly of all, the Future.sequence has the very important task of translating a List of Future triples into a Future List of triples, giving us just one thing to wait for instead of many
All this gives:
Pinging 'http://www.foo.net'
Pinging 'http://www.bar.net'
Pinging 'http://www.baz.com'
Pinging 'http://www.zomg.com'
Pinging 'http://fe.zomg.com'
Pinged 'http://www.foo.net' - got result: '200' in 226ms
Pinged 'http://www.bar.net' - got result: '200' in 427ms
Pinged 'http://www.zomg.com' - got result: '200' in 435ms
Pinged 'http://www.baz.com' - got result: '200' in 441ms
Pinged 'http://fe.zomg.com' - got result: '200' in 458ms
Entire operation took 463ms


Aaaand, strut :-)

Tuesday 25 June 2013

Publishing from SBT to CloudBees

As I'm a massive CloudBees fan, I'm starting to use more and more of their offerings. I've got a number of Play! Framework 2.1 apps (Scala flavour, natch) hosted up there and they Just Work, but I wanted to write a Scala library on Github and push it up to a CloudBees-hosted Maven repo for general consumption.

SBT is a nice tool for developing Scala, and will happily publish to a Nexus or other such Maven/Ivy repository, but CloudBees is a little trickier than that, because it's best suited to hosting CloudBees-built stuff (i.e. the output of their Jenkins-In-The-Cloud builds).

Their support for uploading "external" dependencies is limited to the WebDAV protocol only - a protocol that SBT doesn't natively speak. Luckily, some excellent person has minimised the yak-shaving required by writing a WebDAV SBT plugin - here's how I got it working for my build:

In project/plugins.sbt, add the WebDAV plugin:
resolvers += "DiversIT repo" at "http://repository-diversit.forge.cloudbees.com/release"

addSbtPlugin("eu.diversit.sbt.plugin" % "webdav4sbt" % "1.3")


To avoid plastering your CloudBees credentials all over Github, create a file in ~/.ivy2/.credentials:
realm={account} repository
host=repository-{account}.forge.cloudbees.com
user={account}
password=
Where {account} is the value you see in the drop-down at the top right when you login to CloudBees Grand Central. Don't forget the Realm, this is just as important as the username/password!

Next, add this to the top of your build.sbt:
import eu.diversit.sbt.plugin.WebDavPlugin._
and this later on:
credentials += Credentials(Path.userHome / ".ivy2" / ".credentials")

seq(WebDav.globalSettings : _*)

publishTo := Some("Cloudbees releases" at "https://repository-{account}.forge.cloudbees.com/"+ "release")
What this does: The first line tells SBT where to find that credentials file we just added.
The second line makes the webdav:publish task replace the default publish task, which is most-likely what you want. If it's not, use WebDav.scopedSettings and invoke the task with webdav:publish.
The third line specifies where to publish to, replacing all other targets. I found if I used the notation in the WebDAV plugin documentation:
publishTo <<= version { v: String =>
  val cloudbees = "https://repository-diversit.forge.cloudbees.com/"
  if (v.trim.endsWith("SNAPSHOT"))
    Some("snapshots" at cloudbees + "snapshot")
  else
    Some("releases" at cloudbees + "release")
}
...SBT would attempt to upload my artifact to not-just CloudBees, but any other extra repository I had configured with the resolvers expression higher up in build.sbt, and hence try and upload to oss.sonatype.org, which I'm not ready for just yet! "Releases" is sufficient for me, I don't need the "snapshots" option.

And with that, it should just work like a charm:
> publish
[info] WebDav: Check whether (new) collection need to be created.
[info] WebDav: Found credentials for host: repository-{account}.forge.cloudbees.com
[info] WebDav: Creating collection 'https://repository-{account}.forge.cloudbees.com/release/net'
[info] WebDav: Creating collection 'https://repository-{account}.forge.cloudbees.com/release/net/foo'
[info] WebDav: Creating collection 'https://repository-{account}.forge.cloudbees.com/release/net/foo/bar_2.9.2'
[info] WebDav: Creating collection 'https://repository-{account}.forge.cloudbees.com/release/net/foo/bar_2.9.2/0.1'
[info] WebDav: Done.
...
[info]  published bar_2.9.2 to https://repository-{account}.forge.cloudbees.com/release/net/foo/bar_2.9.2/0.1/bar_2.9.2-0.1.pom
[info]  published bar_2.9.2 to https://repository-{account}.forge.cloudbees.com/release/net/foo/bar_2.9.2/0.1/bar_2.9.2-0.1.jar
[info]  published bar_2.9.2 to https://repository-{account}.forge.cloudbees.com/release/net/foo/bar_2.9.2/0.1/bar_2.9.2-0.1-sources.jar
[info]  published bar_2.9.2 to https://repository-{account}.forge.cloudbees.com/release/net/foo/bar_2.9.2/0.1/bar_2.9.2-0.1-javadoc.jar

Thursday 20 June 2013

Missing Jenkins Feature?

I really like Jenkins. If you are disciplined and treat him well, he gives you a tremendous amount of building power for the princely sum of $0.00. I've used Bamboo a little bit and it was fine, but I really can't see the value proposition when Jenkins does the same job and offers over 600 plugins to do just about everything you could possibly think of with a software build.

One thing that does seem to be missing (as far as I can tell) from Jenkins is "smart" detection of the current build killer. A colleague of mine spotted this today. Of course, this is a situation that shouldn't happen if people did the right thing and didn't check in on a broken build, but hey, we're all human. So here's the normal break-fix situation:

User adolf commits bad code
Build runs and breaks (3 test fails)
adolf correctly identified as breaker
User bob commits good code into broken build
Build runs and breaks the same way (3 test fails)
adolf remains correctly identified as breaker
User caz commits fixed code/tests
Build runs and passes
Build is green, huzzah for caz


Note that of course the items in the 3 columns don't necessarily occur perfectly sequentially as shown above. In fact, a far more common mode of failure would be an "innocent mistake" from bob who didn't realise a build was in progress when he pushed his changes, viz:

User adolf commits bad code
Build runs and breaks (3 test fails)User bob commits good code
adolf correctly identified as breakerBuild runs and breaks the same way (3 test fails)
adolf remains correctly identified as breaker
User caz commits fixed code/tests
Build runs and passes
Build is green, huzzah for caz


OK. Now for the not-so-great, but definitely not-too-uncommon "firestorm of crap checkins" - this often actually happens when builds are unstable and/or slow and so people have almost no choice but to Russian-Roulette their pushes:

User adolf commits bad code
Build runs and breaks (3 test fails)User boris commits MORE bad code
adolf correctly identified as original breakerBuild runs and breaks even more (4 new test fails, 7 test fails total)
adolf remains correctly identified as original breaker
User caz commits fixed code/tests for adolf's breakage
Build runs but 4 tests still fail (boris' breakage)
Build is still red, but still identifies adolf as the culprit (incorrectly)


Because the build never fully recovered, Jenkins just leaves the original culprit as the bad guy. Of course, a little bit of further investigation will show that boris is now to blame, but it would be much better (especially if your name is up on a Big Screen of Shame that Important People might see!) if Jenkins could calculate the true culprit correctly.

New Jenkins plugin, anyone?

Friday 7 June 2013

Fun With Scala/Play, Part 1

I currently have a rather useful little app running which I call PingCaster. Basically when it receives an HTTP GET it triggers a whole lot of other HTTP GET requests to a list of configured URLs. I'll leave it to you to consider possible applications of this...
It's actually one of the first toy apps I wrote using Play! Here is version 1 of Application.scala:
...

import org.apache.http.client.fluent.Request
import org.apache.http.StatusLine

object Application extends Controller {

  abstract trait Addressable { def getAddress():String }

  trait Pingable extends Addressable {
    def ping() : StatusLine= {
      println("Pinging '" + getAddress() + "'")
      val result = Request.Get( getAddress() ).execute().returnResponse().getStatusLine()
      println("Pinged '" + getAddress() + "' - got result: '" + result + "'")
      result
    }
  }

  case class PingTarget(url:String)  extends Addressable with Pingable {
    override def getAddress():String = {
      url
    }
  }

  def sendPing = Action {
    val pingTargets =
      PingTarget("http://www.fake1.net") ::
      PingTarget("http://www.fake2.com") ::
      PingTarget("http://www.fake3.com") ::
      PingTarget("http://subdomain.fake4.com") ::
      Nil

    val results = pingTargets.map( pt => (pt.url, pt ping))
    Ok(html.ping(results))
  }
}
I was pretty happy with that; I was using a couple of Scala features, namely the use of a trait, a case class, cons-style list construction, the map operation and returning a tuple (containing the URL being pinged and the result of the ping) from it. It was also fun using the new (for me) fluent interface to the Apache HttpClient. And most importantly, it worked!
But this is Scala. I'm surely not done yet, right? Let's try and make this a bit more idiomatic ...
Firstly, the construction of all those PingTarget objects is very repetitive, let's do it just-in-time:
def sendPing = Action {
    val pingTargets =
      "http://www.fake1.net" ::
      "http://www.fake2.com" ::   
      "http://www.fake3.com" ::   
      "http://subdomain.fake4.com" ::   
      Nil

    val results = pingTargets.map( pt => (pt, PingTarget(pt) ping))
    Ok(html.ping(results))
  }
Actually, let's use an implicit conversion to hide PingTarget from the client entirely:
implicit def str2PingTarget(value: String) = PingTarget(value)

...

    val results = pingTargets.map( pt => (pt, pt ping))
And actually, we can drop the whole PingTarget thing and get the converter to create us an anonymous type:
  implicit def str2Pingable(value: String) = {
    new Addressable with Pingable {
        override val getAddress =  value 
    } 
  }

OK. But we're still executing those pings sequentially! How quaint! In the next installment, let's get parallel...

Thursday 23 May 2013

nginx as a CORS-enabled HTTPS proxy

So you need a CORS frontend to your HTTPS target server that is completely unaware of CORS.

I tried doing this with Apache but it couldn't support the creation of a response to the "preflight" HTTP OPTIONS request that is made by CORS-compliant frameworks like jQuery.

Nginx turned out to be just what I needed, and furthermore it felt better too - none of that module-enabling stuff required, plus the configuration file feels more programmer-friendly with indenting, curly-braces for scope and if statements allowing a certain feeling of control flow.

So without any further ado (on your Debian/Ubuntu box, natch) :

Get Nginx:
sudo apt-get install nginx

Get the Nginx HttpHeadersMore module which allows the CORS headers to be applied whether the request was successful or not (important!)
sudo apt-get install nginx-extras

Now for the all-important config (in /etc/nginx/sites-available/default ) - We'll go through the details after this:

#
# Act as a CORS proxy for the given HTTPS server(s)
#
server {
  listen 443 default_server ssl;
  server_name localhost;

  # Fake certs - fine for development purposes :-)
  ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
  ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;

  ssl_session_timeout 5m;

  # Make sure you specify all the methods and Headers 
  # you send with any request!
  more_set_headers 'Access-Control-Allow-Origin: *';
  more_set_headers 'Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE';
  more_set_headers 'Access-Control-Allow-Credentials: true';
  more_set_headers 'Access-Control-Allow-Headers: Origin,Content-Type,Accept';

  location /server1/  {
    include sites-available/cors-options.conf;
    proxy_pass https://<actual server1 url>/;
  }

  location /server2/  {
    include sites-available/cors-options.conf;
    proxy_pass https://<actual server2 url>/;
  }
}

And alongside it, in /etc/nginx/sites-available/cors-options.conf:
    # Handle a CORS preflight OPTIONS request 
    # without passing it through to the proxied server 
    if ($request_method = OPTIONS ) {
      add_header Content-Length 0;
      add_header Content-Type text/plain;
      return 204;
    }
What I like about the Nginx config file format is how it almost feels like a (primitive, low-level, but powerful) controller definition in a typical web MVC framework. We start with some "globals" to indicate we are using SSL. Note we are only listening on port 443 so you can have some other server running on port 80. Then we specify the standard CORS headers, which will be applied to EVERY request, whether handled locally or proxied through to the target server, and even if the proxied request results in a 404:
  more_set_headers 'Access-Control-Allow-Origin: *';
  more_set_headers 'Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE';
  more_set_headers 'Access-Control-Allow-Credentials: true';
  more_set_headers 'Access-Control-Allow-Headers: Origin,Content-Type,Accept';

This last point can be important - your JavaScript client might need to inspect the body of an error response to work out what to do next - but if it doesn't have the CORS headers applied, the client is not actually permitted to see it!
The little if statement that is included for each location provides functionality that I simply couldn't find in Apache. This is the explicit response to a preflighted OPTIONS:
  
    if ($request_method = OPTIONS ) {
      add_header Content-Length 0;
      add_header Content-Type text/plain;
      return 204;
    }
The target server remains blissfully unaware of the OPTIONS request, so you can safely firewall them out from this point onwards if you want. A 204 No Content is the technically-correct response code for a 200 OK with no body ("content") if you were wondering.
The last part(s) can be repeated for as many target servers as you want, and you can use whatever naming scheme you like:
 location /server1/  {
    include sites-available/cors-options.conf;
    proxy_pass https://server1.example.com;
  }

This translates requests for:
    https://my.devbox.example.com/server1/some/api/url
to:
    https://server1.example.com/some/api/url

This config has proven useful for running some Jasmine-based Javascript integration tests - hopefully it'll be useful for someone else out there too.

Tuesday 21 May 2013

2013 Nose-Wrinklers

You know what I'm talking about. A colleague starts at a new gig and you meet up after a few weeks; "So how is it?"

And they wrinkle up their nose and say "awwww, it's alright. The people are good..."

You press a bit more, and it's the technology that is making them sad. And it turns out that while it was there in the job description, your colleague thought that it wasn't as important as the other stuff. Or it was glossed over in the interview. Yeah.

So I hereby present my 2013 Nose-Wrinklers -  If you see these technologies in a position description, consider carefully whether you want to be working with them in 2013:


  • Struts - At the time, it was okay. That time has passed. The amount of boilerplate code and design compromises required to simply serve up some pages is unacceptable today
  • CVS/SVN - I'm not saying that Git will solve everything. But it will get in the way a whole lot less than these antique attempts at version control
  • Ant - Managing your own dependencies should take exactly one line of configuration. Specifying how to build your project's deliverable should take zero.
  • IBM WebSphere - an application server for companies that feel like they need to pay money to someone for something you can get for free. Except the free offering is faster, more secure, more standards-compliant and more stable. Riiiight.
  • "J2EE" - It's not J2EE, it's Java EE and has been for seven years. Whatever it's called, it's a very long way from the lightweight components most developers actually enjoy building.

Thursday 9 May 2013

Really Grails, Really?

I'm a bit grumpy at Grails today. We're trying to get a Grails app into something like a production-ready state and Grails is repeatedly showing me that it really doesn't want to be there.

 A couple of weeks ago, our Ops guys requested (completely reasonably) that the app be configurable "externally" - i.e. tweak a properties file somewhere on the filesystem, restart the app, and bingo.

Rather than the godawful files-in-classpath mess that results in excessive trawling through unpacked WAR files in Tomcat's webapps directory, followed by nervous vi-ing and praying something doesn't come along and blow away your delicate changes. Nod sagely if you've done that crap about a bazillion times.

So the change was duly made, there are a million "externalise your Grails config" blogs out there, Google away if you care. But they almost all are totally on the Groovy Kool-Aid: the externalised config files are .groovy files - curly brackets and all that. Our guys not only don't like that (don't blame them), but they won't allow it for security reasons - you can put executable "stuff" in such a file and do who-knows-what to a system.

So the Grails Doco on externalised config says .properties files are A-OK. Great! Let's try something then:

In our externalised property file:
config.feature.enabled = false
In a Grails controller, some time later:
if (config.feature.enabled) {
  println "Combobulating the Doodads"
} else {
  println "Enabled I Am Not"
}

Execute.

And get very, very VERY annoyed.


Friday 3 May 2013

SOTSOG 2013H1

Some cool stuff that makes life way easier to do webapps in 2013:
  • Play! Framework 2.1 - Scala, Hit-refresh recompilation, built-in LESS, proper request routing (no annotations!) and no XML. Nuff said.
  • LESS CSS - Does a great job of de-repetitioning CSS.
  • Angular.JS - the most unobtrusive client-side framework I've ever used - just feels like "dynamic HTML"
Also (not yet used, but fully intend to):
I note that in 2013, Standing on the Shoulders Of Giants is more like Standing on the Shoulders of Giants Standing on the Shoulders of Giants - all of the above technologies build on something a bit older, a bit cruftier, a bit trickier.
Have we finally hit the Industrial Revolution in software development?

Monday 22 April 2013

When Grails Attacks

I've been pretty harsh on Grails before but I'm starting to appreciate some of its charms now. Of course I still think Play! 2.1 is even better but must admit there are fewer OMFGWTFBBQ?!?! moments with Groovy compared to Scala. That might just be me and my functional-programming newb-ness, or it might just be that while Groovy is a new front door on Java, Scala is a new top floor, roof and swimming pool out the back...

 Anyway.

Innocently trying to run some unit tests on an existing set of Grails controllers. Everybody else seems to be running them fine, but I get:

| Running 9 unit tests... 1 of 9
| Failure:  testThatAAABBDDD(BrokenAssTests)
|  java.lang.NullPointerException: Cannot set property 'method' on null object
 at BrokenAssTests.setup(BrokenAssTests.groovy:155)
| Running 9 unit tests... 2 of 9
| Failure:  testThatEEEFFGGG(BrokenAssTests)
|  java.lang.NullPointerException: Cannot set property 'method' on null object
 at BrokenAssTests.setup(BrokenAssTests.groovy:155)

The setup() method in question:
  @Before
  public void setup() {
    request.method = 'GET'
  }

 Same Groovy version, same Grails version, very similar Java version (1.7.0_11 vs 1.7.0_21, hardly a biggie), so what's going on?

I'm running Ubuntu 12.04, everyone else is on Macs (a story for another day).

A great deal of digging and Googling later, and here's what's going on:


  • Grails 2.0 introduced the TestFor annotation which automagically mixes in the ControllerUnitTestMixin  
  • As one of its many magic tricks, the ControllerUnitTestMixin adds mocked request and response objects into the test scope
  • This magic is performed in method bindGrailsWebRequest() which if you persue the documentation, has the JUnit 4 @Before annotation applied
  • The only ordering guarantee about @Before evaluation is "superclass comes before subclass"
  • But a mixin is NOT a superclass!
  • And so the order of @Before evaluation when using Grails' test mixins is essentially OS/JVM dependent
In my case, I was the unlucky "canary" on the "different" platform who encountered the bug. The "fix", such as it was, was manually calling the mixin method just-in-time in "our" setup() method:
  @Before
  public void setup() {
    if (!request) {
      bindGrailsWebRequest()  // Make sure request is visible - the ordering of @Befores is not guaranteed :-(
    }
    request.method = 'GET'
  }
I don't like it much, and hopefully the next version of Grails rectifies this, but for me it's just another one of those "too many layers of magic" problems that in my experience seems to afflict the Groovy world more than others.

Thursday 18 April 2013

Backfillin'

One of my motivations for getting back into blogging after a considerable hiatus is the rise of the "cloud-based infrastructure" solutions which make it so much easier not just to host a software solution, but to develop one as well.

"The cloud" is a nebulous (see what I did there?) concept at best but strip away the management-pitched advertising and there are some amazing offerings out there for low- and no-cost.

As such I will be backfilling content from my old self-hosted Pebble blog onto this blog. I won't censor anything except for my ill-fated "setting up your own development server" series which, after a ludicrous number of instalments, is basically the best advertisement Cloudbees' Jenkins-As-A-Service could ever wish for!

Monday 15 April 2013

Ubuntu Wubi: Here Be Dragons

After being handed a rather low-spec ThinkPad at my new gig, and attempting to get work done in a locked-down corporate Windows 7 installation, I gave up after two days. The main reason being Cygwin. It's cool and all, but it's not Linux. All the cool kids write install/run scripts that almost work in Cygwin, but not quite. Usually something to do with slashes and backslashes. The third time I had to go diving into someone else's shell script which would *just work* on Linux/Mac, I'd had enough.

Off to the Ubuntu site I trundled, and my eye was caught by the Wubi option - Install Ubuntu from "inside" Windows, thus minimising my unproductive "watch the progress meter" time.

So here is my advice for anyone who does more with their computer than light web-browsing or a spot of word-processing, strictly one app at a time:

Do NOT, under any circumstances, use Wubi to get Ubuntu onto your machine.

What you get after a Wubi Ubuntu installation is a disk image stored in your Windows directory, which you can choose from a boot loader, and it feels "just like Ubuntu". Because it is Ubuntu. Being run in a virtual disk, mounted from an NTFS partition. There are a lot of moving parts in that last sentence, and therein lies the problem.

Undoubtedly is a remarkable and clever achievement to be able to do all of the boot-record-jiggery and virtual-file-system-pokery from within a "hostile" operating system. But just because you can, doesn't mean you should.

My experience of the Ubuntu 12.10 OS running from a Wubi install was universally positive, until I started actually multitasking. Like this (each bullet being an "async task" as it were):

  • Open new Terminal -> Start pulling down new and large git updates
  • Open new Terminal tab ->Start pulling new and large SVN updates
  • Open new Terminal tab -> Kick off a grails application build and test
  • Open Chrome -> GRIND. BANG. LOCKUP
It seems the filesystem-intensive work being done in the terminal (particularly the Grails job that does a lot of temporary file twiddling) makes the virtual disk driver work hard, which in turn makes the NTFS filesystem driver do a lot of work. And between the two of them, they lock up THE ENTIRE MACHINE. Lights-on, nobody-home style. Press-and-hold-the-power-button style.

Then, after deciding you've had enough of your Wubi-based install, you have a very considerable number of fiery hoops to jump through to get your stuff out of its clutches and into a real disk mounted on a real filesystem.

So when (not if) you decide to liberate a corporate machine from Windows, take the hit and do the job properly - boot into the installer the way God/Shuttleworth intended, partition the disk correctly and enjoy.

Tuesday 9 April 2013

Scala *is* Clean Code

Reading, learning, marvelling at Martin Odersky's creation thanks to his great book.

Everything I try (usually within the Play! 2.1 framework as that's just the cherry on top) just works, and as I learn more, I find 5 lines condenses to 3, condenses to 2, with no reduction in readability (as long as I name my variables properly).

My conclusion is that Scala is the closest thing I've seen to the physical embodiment of the Clean Code ideal. Huzzah!