Wednesday 18 June 2014

Scala By Stealth, Part 1: SBTifying your Mavenized Build

I was faced with updating and extending some old Java code of mine recently, and it seemed like much more of a chore than it used to. The code in question does a lot of collection manipulation, and I was looking at the Java code (which was, if I say so myself, not too bad - clean, thoroughly-tested and using nice libraries like Google Guava where at all possible) thinking "ugh - that would be a couple of lines in Scala and way more readable at the same time".

At this point I realised it would be a perfect candidate for a step-by-step guide for converting a simple Maveni[sz]ed Java library project (e.g. resulting in a JAR file artifact) to an SBT-based, Scala library.

Shortly after that I realised this could be a terrific way for a traditional "Java shop" where everything up until now has been delivered as JARs (and/or WARs) into a private Nexus to get its feet wet with Scala without having to go with a risky "big-bang" approach. An iterative migration, if you will. So let's get started!

A tiny bit of background first though - I'm not going to bother anonymising the library I'll be migrating, because I will almost certainly forget to do so somewhere in the example snippets I'll be including. So I'll say it here: the library is called brickhunter, and it's the "engine" behind the web-scraping LEGO search engine you can use at brickhunter.net. The site itself is a Java/Spring MVC/JQuery webapp that I launched in late 2012, and was the last significant bit of Java I ever wrote. It includes brickhunter.jar as a standard Maven dependency, pulling it from my private Maven repo hosted by CloudBees.

Step 0 (A Precondition): A Cared-For Maven Java Project

You need to be doing this migration for a library that has redeeming qualities, and not one that suffers from neglect, lack of test coverage, or a non-standard building process. Generally, using Maven will have made the latter difficult, but if, somehow, weird stuff is still going on, fix that. And make sure your tests are in order - comprehensive, relevant and not disabled!

Step 1: An SBTified Java Project

  • Create a new directory alongside the "legacy" project directory with a suitable name. For me, the obvious one was brickhunter-scala.
  • Now recursively copy everything under src from legacy to new. Hopefully that gets everything of importance; if not, see Step 0 and decide what should be done.
  • While a number of people have written helpers to automate the creation of a build.sbt from a pom.xml, unless you have a truly enormous number of dependencies, you're probably better-off just writing it yourself. For one thing, it's the obvious entry point to the enormous world of SBT, and there's plenty to learn;
  • In a typical Maven shop you may have quite a stack of parent POMs bringing in various dependencies - I found the quickest way to get all of them into SBT style was by invoking mvn dependency:tree which for my project, gave me:
    [INFO] +- org.jsoup:jsoup:jar:1.6.1:compile
    [INFO] +- commons-lang:commons-lang:jar:2.6:compile
    [INFO] +- com.google.guava:guava:jar:11.0.1:compile
    [INFO] |  \- com.google.code.findbugs:jsr305:jar:1.3.9:compile
    [INFO] +- log4j:log4j:jar:1.2.16:compile
    [INFO] +- org.slf4j:slf4j-api:jar:1.6.4:compile
    [INFO] +- org.slf4j:slf4j-log4j12:jar:1.6.4:compile
    [INFO] +- com.themillhousegroup:argon:jar:1.1-SNAPSHOT:compile
    [INFO] +- org.testng:testng:jar:6.3.1:test
    [INFO] |  +- junit:junit:jar:3.8.1:test
    [INFO] |  +- org.beanshell:bsh:jar:2.0b4:test
    [INFO] |  +- com.beust:jcommander:jar:1.12:test
    [INFO] |  \- org.yaml:snakeyaml:jar:1.6:test
    [INFO] +- org.mockito:mockito-all:jar:1.9.0:test
    [INFO] \- org.hamcrest:hamcrest-all:jar:1.1:test
    
  • Anything transitive (i.e. indented once or more) can be omitted as SBT will work that out for us just as Maven did.
  • The eagle-eyed might notice an in-house dependency (argon) which clearly isn't going to be found in the usual public repos - it will need its own resolver entry in build.sbt.
  • Here's how mine looked at this point:
  • name := "brickhunter-scala"
    
    organization := "com.themillhousegroup"
    
    version := "0.1"
    
    scalaVersion := "2.10.3"
    
    credentials += Credentials(Path.userHome / ".ivy2" / ".credentials")
    
    resolvers += "tmg-private-repo" at "https://repository-themillhousegroup.forge.cloudbees.com/private/"
    
    libraryDependencies ++= Seq(
      "org.jsoup"             % "jsoup"           % "1.6.1",
      "commons-lang"          % "commons-lang"    % "2.6",
      "com.google.guava"      % "guava"           % "11.0.1",
      "log4j"                 % "log4j"           % "1.2.16",
      "org.testng"            % "testng"          % "6.3.1"         % "test",
      "org.mockito"           % "mockito-all"     % "1.9.0"         % "test",
      "com.themillhousegroup" % "argon"           % "1.1-SNAPSHOT"  % "test"
    )
    
  • At this point, firing up SBT and giving it a compile command should be successful. If so, pat yourself on the back, and commit all pertinent files in source control. This is a good milestone!


Step 2: A Tested SBTified Java Project

  • Compiling is all very well but you can't really be sure your SBT-ification has been a success until all the tests are passing, just like they did in Maven. They did all pass in Maven, didn't they?
  • Here's where I hit my first snag, as my Java tests were written using the TestNG framework, which SBT has no idea how to invoke. And thus, the brickhunter-scala project gets its first plugin, the sbt-testng-interface.
  • But now when running sbt test, instead of "0 Tests Found", I get a big stack trace - the plugin is expecting to find a src/test/resources/testng.yaml and I don't have one, because Maven "just knows" how to run a load of TestNG-annotated tests it finds in src/test/java, and I've never needed to define what's in the default test suite.
  • The fix is to create the simplest possible testng.yaml that will pick up all the tests:
    name: BrickhunterSuite
    threadCount: 4
     
    tests:
      - name: All
        packages:
        - com.themillhousegroup.brickhunter
    
  • And now we should have the same number of tests running as under Maven, and all passing. Commit all the changes!


Next time: Publishing the new artifact to your private repository.

Friday 6 June 2014

Tascam FireOne hardware buttons and GarageBand

Another blatant Google-troll here but hopefully it'll help someone else out there.

As mentioned elsewhere I use a Tascam FireOne Firewire Audio Interface when I make music with GarageBand, and it works pretty well.
Side note for even more karma: There are times when it doesn't work well (particularly on OSX Mavericks) and I humbly present my fixes which seem to work - mostly variations on the classic "turn it back off and back on again" trick:
  • Mac doesn't "see" the FireOne - Check Thunderbolt-to-Firewire adaptor is snug, unplug-replug.
  • Mac sees FireOne, FireOne seems dead - Unplug-replug.
  • Mac sees FireOne, FireOne lights and meters working, no sound - Mash both PHANTOM buttons at the same time. This seems to (probably not by design!) cause a hardware soft-ish reset and audio should ensue.

But I digress. One of the nice things about the FireOne is the hardware control surface it offers. Now ideally you're running Pro Tools or some other very nice, very expensive DAW where the FireOne's buttons Just Work but if, like me, your needs are actually met quite nicely by GarageBand (not to mention its price), then you'll be wanting to get those buttons going in GB. Because they most certainly don't by default.

Sadly, you won't be able to map all the FireOne's buttons to GB functions, but the most important ones can be done. Firstly, download GarageRemote, a very simple, but nicely done System Preferences extension thingy. Install it, and turn on its "Listener" functionality so it can do its thing. Then, you'll need to customise the MIDI message mapping as follows:



I diagnosed the MIDI messages that the FireOne sends by using the free Snoize MIDI Monitor utility. Here's the full list, in case you want to tune your setup:
FireOne Hardware ControlMIDI Message Bytes
<<90 5B 7F
>>90 5C 7F
[]90 5D 7F
>90 5E 7F
O90 5F 7F
 
F190 36 7F
F290 37 7F
F390 38 7F
F490 39 7F
F590 3A 7F
F690 3B 7F
F790 3C 7F
F890 3D 7F
 
Jogwheel CW (Slowest)90 3C 01
Jogwheel CW (Slow)90 3C 02
Jogwheel CW (Medium)90 3C 03
Jogwheel CW (Fast)90 3C 04
Jogwheel CW (Fastest)90 3C 05
 
Jogwheel CCW (Slowest)90 3C 41
Jogwheel CCW (Slow)90 3C 42
Jogwheel CCW (Medium)90 3C 43
Jogwheel CCW (Fast)90 3C 44
Jogwheel CCW (Fastest)90 3C 45
 
SHIFT (on its own)90 46 7F
Weirdly, using SHIFT + other keys doesn't actually change the MIDI message that is sent, making it pretty useless for our purposes. I'd sure love to get my hands on that GarageRemote source code and support more buttons!