Monday, March 12, 2012

Getting Started with Scala Web Development - Lift 2.4 and SBT 0.11.2

Anyone following this blog knows I do most of my development in C#, but I recently had an opportunity for a Java project, so I'm taking the Scala plunge with Lift. It's been a bit of a frustrating experience so far since all of the documentation on any Java web framework assumes prior experience with Java servlets or other Java web frameworks.

Just a note, I'm not intending to bash anything, but I will point out some serious flaws in the tools or documentation that I encountered which seriously soured me on these tools. This is not a reason to get defensive, but it's an opportunity to see how accessible these tools are to someone with little bias or background in this environment.

In this whole endeavour, it was most frustrating trying to find a coherent explanation of the directory structures used by various build tools, JSPs and WARs. I finally managed to find a good intro for Lift that didn't assume I already knew how files are organized, and that started me on the right path. I soon after found a concise review of servlet directory structures written for the minimalist web4j framework which filled in the rest.

Of the IDEs I tried, IntelliJ IDEA and Eclipse both support Maven Lift project templates out of the box. However, Maven is not itself a dependency that they resolve and install for you. Combined with the fact that Maven is no longer the recommended method of building Lift programs, I saw little choice but to give that up as a bad job [1]. There is no working SBT support within these IDEs that I could see. IDEA had an SBT plugin, but it never worked for me.

So I set about playing with the recommended Simple Build Tool (SBT), but of course, the version shipped with Lift 2.4 is way out of date (0.7.7 IIRC, while the latest is 0.11.2). As a result, the terminology has changed somewhat, as the article I linked above mentions. I saw no reason to learn something already outdated, so my first project was to update the lift_blank build to SBT 0.11.2. This started the 13 page journey through SBT's getting started guide, much of which you have to understand to grok lift_blank's trivial build.

Fortunately, SBT is pretty cool. It can automatically download and register dependencies using Apache Ivy, and launch your web programs in Jetty for testing purposes. However, the intro and the default setup could definitely use a little work (at least for Lift documentation). For instance, to even make SBT usable in Lift scenarios, you need the xsbt-web-plugin, which itself requires you to define a plugin.sbt file in the appropriate location. What plugins are and where exactly plugins.sbt should go is on page 11 of the SBT getting started guide, and the need for that web plugin was buried in another page somewhere I accidentally found via Google after much searching on "not found" errors for "seq(webSettings :_*)".

The Final Solution

I present here the final toolset and configuration I settled on, combined with the updated and simplified build script for lift_blank which enabled me to finally work with code in a semi-usable environment:

  1. Download Eclipse Helios, and install it as you prefer. Helios is one version behind, but it's needed for Scala-IDE.
  2. Install the Scala-IDE Eclipse plugin.
  3. Install Scala 2.9.1. I'm not convinced this step is necessary, but it's what I did.
  4. Download Lift 2.4.
  5. Unpack and go into lift-lift_24_sbt-f911f30\scala_29\lift_blank.
  6. Delete project\build directory, and sbt-launcher.jar, sbt, sbt.bat. The only remaining sbt-specific file is project\build.properties.
  7. Open project\build.properties in a text editor, and replace "sbt.version=0.7.7" with "sbt.version=0.11.2", "def.scala.version=2.7.7" with "def.scala.version=2.9.1".
  8. Download sbt-launch.jar from this page and place it in the lift_blank root directory.
  9. Create an sbt.bat in the lift_blank root directory file with the following contents:
    set SCRIPT_DIR=%~dp0
    java -Xmx512M -jar "%SCRIPT_DIR%sbt-launch.jar" %*
    This simply runs the sbt-launch.jar you just downloaded.
  10. Run sbt.bat. You will get an error, but this will create the ~/.sbt directory where global SBT settings are stored.
  11. Go to your root user directory, ie. in Windows 7, this is the start menu > [Your User Name]. You should see a .sbt folder there. Enter it and create a folder called "plugins".
  12. Under the above plugins folder, create a file called "plugins.sbt" with the following contents:
    addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0")
    
    libraryDependencies <+= sbtVersion(v => "com.github.siasia" %% "xsbt-web-plugin" % (v+"-0.2.11"))
    This simply adds Eclipse and web app support to SBT. You could also put this plugins folder under lift_blank\project, but I just made it a global setting so I never have to fiddle with it again.
  13. Return to folder lift_blank, and create a file called "build.sbt" with the following contents:
    name := "lift_blank"
    
    version := "1.0"
    
    scalaVersion := "2.9.1"
    
    seq(webSettings :_*)
    
    libraryDependencies ++= Seq(
        "net.liftweb" %% "lift-webkit" % "2.4" % "compile->default",
        "org.mortbay.jetty" % "jetty" % "6.1.26" % "container,test",
        "junit" % "junit" % "4.7" % "test",
        "ch.qos.logback" % "logback-classic" % "0.9.26",
        "org.scala-tools.testing" %% "specs" % "1.6.9" % "test")
    This is the simplified and updated build file for lift.
  14. Now execute sbt.bat and you should get an SBT command prompt. Type "compile" and watch SBT download all the dependencies listed above in build.sbt, and compile the source code.
  15. Now type "eclipse" at SBT's command prompt, and this will generate Eclipse project files.
  16. Launch Eclipse, and go to File > Import, select General > Existing Projects into Workspace, then click Next. Navigate to and select your lift_blank folder and click Finish.
  17. Go to menu Project > Properties, and select the Builders option on the left hand side.
  18. Click New, select Program, click OK. This will bring up an "Edit Configuration" dialog.
  19. In the "Name" field, type "SBT Builder".
  20. Under the "Location" section, click Browse Workspace, and select sbt.bat.
  21. Under the "Working Directory" section, click Browse Workspace, and select lift_blank, ie. your project name.
  22. Under the "Arguments" section, type in compile, and click OK.

You now have a working, update to date SBT build and a valid Eclipse project. Happy hacking!

You can start Jetty by executing sbt ~container:start.

Suggestions

There is no way it should be this convoluted to get a working build and a working IDE. This solution isn't even complete, as I'm still working on getting the tests to run and Jetty to launch, and I hold out little hope for debugging. It also seems like any change to the build, like adding a new dependency, requires regenerating the Eclipse files and restarting Eclipse. Any suggestions here would be much appreciated! An SBT plugin for Eclipse would be most welcome. :-)

Some suggestions for Lift documentation: a complete intro to directory structures would only cost a few paragraphs, and would make your project accessible even to beginners. A short review of the build tool would also be helpful.

Suggestions for SBT:

  1. Remove 90% of the documentation in the getting started guide. In particular, getting started generally doesn't involve recursive builds, contexts, key dependencies, scopes and how it all works at the Scala level, ie. immutable sequences, etc. I think the getting started guide should be focused on directory layout, settings, simple library dependencies, common commands, and batch/interactive mode, all using the simplified .sbt build file. This shouldn't take more than 2 pages, and anything else should be in an advanced guide.
  2. I'm still learning Scala, but I still stumble over libraryDependencies. I often get confused over exactly which column version numbers go in. It would help tremendously if there were some named parameter syntax that would make this obvious, ie. something like:
    dependency(
      package: "net.liftweb",
      jar: "lift-webkit",
      version: "2.4",
      into: "compile->default")

[1] As an aside, I'd just like to mention that IntelliJ was particularly annoying because I couldn't seem to open any file that wasn't already part of a project. Why can't I just open any old text file? Drag and drop certainly didn't work, and adding a folder to a project that already exists in the project directory also seemed impossible. I haven't used Eclipse as much yet, but I will no doubt find similar irritations. One instant irritation is the lack of installer for Windows, though I can appreciate how that might be a low priority for Eclipse devs.

No comments: