Scala Pages Released

I am glad to announce the first release of the Scala Pages (SCP) lightweight web framework, which I wrote for a personal web application project. Although it’s still at a humble stage of development, I believe it is useful enough to be shared. I am planning to extend it in future and I hope that it will contribute to increased diversity in the Scala web development area. Software and manual can be downloaded from this page:

http://www.thomasknierim.com/scala-pages-web-framework/

The source code is included in the distribution zip file. Comments, suggestions, and constructive criticism are always welcome.

Addendum: I was asked to make a brief comparison to the Lift framework, so here’s my answer:

The approach that SCP takes is different. To put it in a nutshell, Lift is Rails-inspired, XML-oriented, and it abstracts from the Servlet API and the request/response model. SCP is inspired by traditional Java MVC frameworks; it is text-oriented, and it builds directly on the request/response model and the Servlet API.

Lift processes templates using the SAX-parser and presents Scala XML data structures to the programmer. By contrast, SCP reads templates as plain text from top to bottom, performs variable replacement and executes embedded instructions. Template processing with SCP thus consumes fewer resources. Lift uses prefixed XML tags; SCP uses processing instructions.

Lift wraps much of the Servlet API and presents a number of abstracted objects to the programmer, such as the S object, the SHtml object, LiftRules, and so on. SCP doesn’t do that. With SCP you get the familiar JEE objects such as HttpServletRequest, ServletContext, etc. and you use these directly from the Scala code. So, it’s easy to use if you’ve done Java web development.

The consequence is that you deal with the request/response model directly, which I personally prefer, because it keeps the control flow simple and clear. The only disadvantage of the MVC request/response model is that controllers tend to become bulky and difficult to reuse. The SCP solution to this problem is the same that Lift offers: snippets.

In SCP, snippets are processing instructions powered by a custom Scala class that the programmer provides. This means you can encapsulate application logic and/or complicated display logic into a snippet class which -if properly coded- is perfectly reusable. SCP also keeps template expressions very simple, because the more complex a template language becomes (many are even Turing-complete), the more likely it is for application logic to sneak into the view. Currently, iteration is the only supported type of control-flow.

Last but not least, Lift is huge compared to SCP. It has a lot of functionality that SCP does not offer, at least not at this time.

Ant script for Scala with Lift

Today I'd like to present another Ant script for Scala development. I promise it's going to be the last one, but I felt this mini series wouldn't be complete without it. This script is for developing a Lift application with Scala. I became interested in Lift in the first half of this year, but unfortunately, I wasn't able to install it when I wanted to give it a try, because of some besetting problems with Maven. Deplorably, the Lift developers have made Maven a requirement for Lift. If you go to the Lift download page, you wont find a Lift package but a Maven package which you must install in order to retrieve Lift from the central repository. Alternatively, you can fetch the latest Lift sources from GitHub, but then you still need Maven to get the examples to work. Since Maven and I have a difficult relationship, I mentioned this to the Lift community earlier this year. The community seemed quite pleased with Maven, however, and had little interest in supporting alternatives. Hence, I've adapted the Ant script from the last article in order to work with Lift and -while we are at it- replaced the Jetty server that is bundled with Lift with Tomcat. I hope this will be useful for people interested in tinkering with Lift who want to avoid the aggravation of having to deal with Maven. As usual, you can download the project sekeleton and Ant build file right here. I have packaged the Lift 1.0 jars as well as its dependencies (commons-fileupload.jar and log4j.ar), so that you can get the helloworld example to run without additional installation steps. If you plan to use this for your work, you should replace the Lift jars with the most recent versions from GitHub. The Ant script has the same target definitions as the one I presented for general Web development including deploy, undeploy and reload. The Lift dependencies are likewise handled in the same way, as the script uses everything it finds in the WEB-INF/lib directory. Your only responsibility is to put the right jars in there. The main difference consists of a different directory tree structure that reflects the layout of a standard Lift project. This means you won't have to rearrange things if you move a project from Maven to Ant. In fact all of the Lift examples should run simply by dropping in the sources (I've tried this for a few but not all examples). So, here's the Ant script:

<project default="build" name="hellolift">

  <!-- root directory of this project -->
  <property name="project.dir" value=".">

  <!-- root directory of Scala installation -->
  <property name="scala.home" value="C:\\Program Files\\Scala">

  <!-- root directory of Tomcat installation -->
  <property name="server.home"
    value="C:\\path\\to\\Tomcat\\installation">

  <!-- URL for Tomcat's manager application -->
  <property name="server.manager.url" value="http://dev:8080/manager">

  <!-- account name for Tomcat's admin account -->
  <property name="server.manager.username" value="admin">

  <!-- password name for Tomcat's admin account -->
  <property name="server.manager.password" value="admin">

  <!-- location of scalatest.jar for unit testing -->
  <property name="scalatest.jar"
     value="C:\\path\\to\\scalatest-1.0\\scalatest-1.0.jar">

  <target name="init">

    <!-- derived path names -->
    <property name="source.dir" value="${project.dir}/src">
    <property name="web.dir" value="${project.dir}/web">
    <property name="classes.dir" value="${web.dir}/WEB-INF/classes">
    <property name="lib.dir" value="${web.dir}/WEB-INF/lib">
    <property name="webapp.dir"
       value="${server.home}/webapps/${ant.project.name}">
    <property name="test.dir" value="${project.dir}/test">

    <!-- scala libraries for classpath definitions -->
    <property name="scala-library.jar"
        value="${scala.home}/lib/scala-library.jar">
    <property name="scala-compiler.jar"
        value="${scala.home}/lib/scala-compiler.jar">

    <!-- classpath for the compiler task definition -->
    <path id="scala.classpath">
      <pathelement location="${scala-compiler.jar}">
      <pathelement location="${scala-library.jar}">
    </pathelement>

    <!-- classpath for project build -->
    <path id="build.classpath">
      <pathelement location="${server.home}/lib/servlet-api.jar">
      <pathelement location="${scala-library.jar}">
      <fileset dir="${lib.dir}">
        <include name="*.jar">
      </include>
      <pathelement location="${classes.dir}">
    </pathelement>

    <!-- classpath for unit test build  -->
    <path id="test.classpath">
      <path refid="build.classpath">
      <pathelement location="${scalatest.jar}">
    </pathelement>

    <!-- definition for the
      "scalac" and "scaladoc" ant tasks -->
    <taskdef resource="scala/tools/ant/antlib.xml">
      <classpath refid="scala.classpath">
    </classpath>

    <!-- definition for the "scalatest" ant task -->
    <taskdef classname="org.scalatest.tools.ScalaTestTask"
      name="scalatest">
      <classpath refid="test.classpath">
    </classpath>

    <!-- definition for the "reload", "deploy"
      and "undeploy" Tomcat tasks -->
    <taskdef classname="org.apache.catalina.ant.DeployTask"
      name="deploy">
      <classpath path="${server.home}/lib/catalina-ant.jar">
    </classpath>
    <taskdef classname="org.apache.catalina.ant.ReloadTask"
      name="reload">
      <classpath path="${server.home}/lib/catalina-ant.jar">
    </classpath>
    <taskdef classname="org.apache.catalina.ant.UndeployTask"
      name="undeploy">
      <classpath path="${server.home}/lib/catalina-ant.jar">
    </classpath>

  </taskdef>

  <!-- compile project -->
  <target depends="init" description="build" name="build">
    <buildnumber>
    <tstamp>
    <mkdir dir="${classes.dir}">
    <mkdir dir="${lib.dir}">
    <copy file="${scala-library.jar}" todir="${lib.dir}">
    <scalac classpathref="build.classpath" deprecation="on"
        destdir="${classes.dir}" force="never" srcdir="${source.dir}">
      <include name="**/*.scala">
    </include>
  </scalac>

  <!-- create a deployable web archive -->
  <target depends="build" description="war" name="war">
    <war basedir="${web.dir}"
      destfile="${project.dir}/${ant.project.name}.war"
       webxml="${web.dir}/WEB-INF/web.xml">
  </war>

  <!-- creates a deployable web archive with
    all classes packed into a single jar file -->
  <target depends="build" description="packedwar"
    name="packedwar">
    <jar basedir="${classes.dir}"
      destfile="${lib.dir}/${ant.project.name}.jar"
      duplicate="preserve">
      <manifest>
        <section name="Program">
          <attribute name="Title" value="${ant.project.name}">
          <attribute name="Build" value="${build.number}">
          <attribute name="Date" value="${TODAY}">
        </attribute>
      </attribute>
    </attribute>
    <delete dir="${classes.dir}">
    <war basedir="${web.dir}"
      destfile="${project.dir}/${ant.project.name}.war"
      webxml="${web.dir}/WEB-INF/web.xml">
    <delete file="${lib.dir}/${ant.project.name}.jar">
  </delete>

  <!-- deploy project on Tomcat server -->
  <target depends="war" description="deploy" name="deploy">
    <mkdir dir="${webapp.dir}">
    <copy todir="${webapp.dir}">
      <fileset dir="${web.dir}">
    </fileset>
    <deploy localwar="${project.dir}/${ant.project.name}.war"
        password="${server.manager.password}"
	path="/${ant.project.name}"
        url="${server.manager.url}"
	username="${server.manager.username}">
  </deploy>

  <!-- update and reload project on Tomcat server -->
  <target depends="build" description="reload" name="reload">
    <copy file="${scala-library.jar}" todir="${lib.dir}">
    <copy todir="${webapp.dir}">
      <fileset dir="${web.dir}">
    </fileset>
    <reload password="${server.manager.password}"
      path="/${ant.project.name}"
      url="${server.manager.url}"
      username="${server.manager.username}">
  </reload>

  <!-- remove project from Tomcat server -->
  <target depends="init" description="undeploy" name="undeploy">
    <undeploy password="${server.manager.password}"
      path="/${ant.project.name}"
      url="${server.manager.url}"
      username="${server.manager.username}">
  </undeploy>

  <!-- build unit tests -->
  <target depends="build" name="buildtest">
    <mkdir dir="${test.dir}/build">
    <scalac classpathref="test.classpath" deprecation="on"
        destdir="${test.dir}/build" force="never" srcdir="${test.dir}">
      <include name="**/*.scala">
    </include>
  </scalac>

  <!-- run unit tests -->
  <target depends="buildtest" description="test" name="test">
    <scalatest runpath="${test.dir}/build">
      <reporter config="YFABRT" type="stdout">
      <membersonly package="suite">
      <!-- <reporter type="graphic" config="YFABRT"/> -->
      <!-- <suite classname="suite.TestSuite"/> -->
    </membersonly>
  </reporter>

  <!-- delete all build files -->
  <target depends="init" description="clean" name="clean">
    <delete dir="${classes.dir}">
    <delete dir="${project.dir}/doc">
    <delete dir="${test.dir}/build">
    <delete file="${lib.dir}/scala-library.jar">
    <delete file="${project.dir}/${ant.project.name}.war">
  </delete>

  <!-- create API documentation in doc folder -->
  <target depends="build" description="scaladoc" name="scaladoc">
    <mkdir dir="${project.dir}/doc">
    <scaladoc classpathref="build.classpath"
      destdir="${project.dir}/doc"
      doctitle="${ant.project.name}" srcdir="${source.dir}"
      windowtitle="${ant.project.name}">
    </scaladoc>
  </target>

</project>

Ant script for Scala Web application

In my last blog entry I introduced an Ant script for building a stand-alone application with Scala. Today I present a script for building a Scala web application. Just as in the last example, you can download the script and the project skeleton layout for a “Hello World” application. The difference is that this application prints the infamous words on a web page rather than on the console. Things get slightly more complicated in a web application, because we need to interact with a web server to run the program. This is reflected by the Ant script below which has additional target definitions for deploying, undeploying and reloading the application on the server. I chose Tomcat for the server, because it's a popular choice for Java web development and because Tomcat is mature, lightweight, and standards-compliant. If you use another web server, you may have to make some small modifications, but the overall structure should be the same. The script disregards web frameworks altogether and the “Hello World” application is simply implemented as a servlet. Since the file structure reflects the standard layout for a Java web application, it should be straightforward to get this to work with any Java web framework.

<project default="build" name="hello">

  <!-- root directory of this project -->
  <property name="project.dir" value=".">

  <!-- root directory of Scala installation -->
  <property name="scala.home" value="C:\\Program Files\\Scala">

  <!-- root directory of Tomcat installation -->
  <property name="server.home"
    value="C:\\path\\to\\Tomcat\\installation">

  <!-- URL for Tomcat's manager application -->
  <property name="server.manager.url"
    value="http://dev:8080/manager">

  <!-- account name for Tomcat's admin account -->
  <property name="server.manager.username" value="admin">

  <!-- password name for Tomcat's admin account -->
  <property name="server.manager.password" value="admin">

  <!-- location of scalatest.jar for unit testing -->
  <property name="scalatest.jar"
     value="C:\\path\\to\\scalatest-1.0\\scalatest-1.0.jar">

  <target name="init">

    <!-- derived path names -->
    <property name="source.dir" value="${project.dir}/src">
    <property name="web.dir" value="${project.dir}/web">
    <property name="classes.dir" value="${web.dir}/WEB-INF/classes">
    <property name="lib.dir" value="${web.dir}/WEB-INF/lib">
    <property name="webapp.dir"
        value="${server.home}/webapps/${ant.project.name}">
    <property name="test.dir" value="${project.dir}/test">

    <!-- scala libraries for classpath definitions -->
    <property name="scala-library.jar"
       value="${scala.home}/lib/scala-library.jar">
    <property name="scala-compiler.jar"
      value="${scala.home}/lib/scala-compiler.jar">

    <!-- classpath for the compiler task definition -->
    <path id="scala.classpath">
      <pathelement location="${scala-compiler.jar}">
      <pathelement location="${scala-library.jar}">
    </pathelement>

    <!-- classpath for project build -->
    <path id="build.classpath">
      <pathelement location="${server.home}/lib/servlet-api.jar">
      <pathelement location="${scala-library.jar}">
      <fileset dir="${lib.dir}">
        <include name="*.jar">
      </include>
      <pathelement location="${classes.dir}">
    </pathelement>

    <!-- classpath for unit test build  -->
    <path id="test.classpath">
      <path refid="build.classpath">
      <pathelement location="${scalatest.jar}">
    </pathelement>

    <!-- definition for the "scalac"
      and "scaladoc" ant tasks -->
    <taskdef resource="scala/tools/ant/antlib.xml">
      <classpath refid="scala.classpath">
    </classpath>

    <!-- definition for the "scalatest" ant task -->
    <taskdef classname="org.scalatest.tools.ScalaTestTask" name="scalatest">
      <classpath refid="test.classpath">
    </classpath>

    <!-- definition for the "reload", "deploy"
      and "undeploy" Tomcat tasks -->
    <taskdef classname="org.apache.catalina.ant.DeployTask"
      name="deploy">
      <classpath path="${server.home}/lib/catalina-ant.jar">
    </classpath>
    <taskdef classname="org.apache.catalina.ant.ReloadTask"
      name="reload">
      <classpath path="${server.home}/lib/catalina-ant.jar">
    </classpath>
    <taskdef classname="org.apache.catalina.ant.UndeployTask"
      name="undeploy">
      <classpath path="${server.home}/lib/catalina-ant.jar">
    </classpath>

  </taskdef>

  <!-- compile project -->
  <target depends="init" description="build" name="build">
    <buildnumber>
    <tstamp>
    <mkdir dir="${classes.dir}">
    <mkdir dir="${lib.dir}">
    <copy file="${scala-library.jar}" todir="${lib.dir}">
    <scalac classpathref="build.classpath" deprecation="on"
       destdir="${classes.dir}" force="never" srcdir="${source.dir}">
      <include name="**/*.scala">
    </include>
  </scalac>

  <!-- create a deployable web archive -->
  <target depends="build" description="war" name="war">
    <war basedir="${web.dir}"
        destfile="${project.dir}/${ant.project.name}.war"
        webxml="${web.dir}/WEB-INF/web.xml">
  </war>

  <!-- creates a deployable web archive with all classes
    packed into a single jar file -->
  <target depends="build" description="packedwar" name="packedwar">
    <jar basedir="${classes.dir}"
        destfile="${lib.dir}/${ant.project.name}.jar" duplicate="preserve">
      <manifest>
        <section name="Program">
          <attribute name="Title" value="${ant.project.name}">
          <attribute name="Build" value="${build.number}">
          <attribute name="Date" value="${TODAY}">
        </attribute>
      </attribute>
    </attribute>
    <delete dir="${classes.dir}">
    <war basedir="${web.dir}"
        destfile="${project.dir}/${ant.project.name}.war"
        webxml="${web.dir}/WEB-INF/web.xml">
    <delete file="${lib.dir}/${ant.project.name}.jar">
  </delete>

  <!-- deploy project on Tomcat server -->
  <target depends="war" description="deploy" name="deploy">
    <mkdir dir="${webapp.dir}">
    <copy todir="${webapp.dir}">
      <fileset dir="${web.dir}">
    </fileset>
    <deploy localwar="${project.dir}/${ant.project.name}.war"
        password="${server.manager.password}"
	path="/${ant.project.name}"
        url="${server.manager.url}"
	username="${server.manager.username}">
  </deploy>

  <!-- update and reload project on Tomcat server -->
  <target depends="build" description="reload" name="reload">
    <copy file="${scala-library.jar}" todir="${lib.dir}">
    <copy todir="${webapp.dir}">
      <fileset dir="${web.dir}">
    </fileset>
    <reload password="${server.manager.password}"
      path="/${ant.project.name}"
      url="${server.manager.url}"
      username="${server.manager.username}">
  </reload>

  <!-- remove project from Tomcat server -->
  <target depends="init" description="undeploy" name="undeploy">
    <undeploy password="${server.manager.password}"
      path="/${ant.project.name}"
      url="${server.manager.url}"
      username="${server.manager.username}">
  </undeploy>

  <!-- build unit tests -->
  <target depends="build" name="buildtest">
    <mkdir dir="${test.dir}/build">
    <scalac classpathref="test.classpath" deprecation="on"
       destdir="${test.dir}/build" force="never" srcdir="${test.dir}">
      <include name="**/*.scala">
    </include>
  </scalac>

  <!-- run unit tests -->
  <target depends="buildtest" description="test" name="test">
    <scalatest runpath="${test.dir}/build">
      <reporter config="YFABRT" type="stdout">
      <membersonly package="suite">
      <!-- <reporter type="graphic" config="YFABRT"/> -->
      <!-- <suite classname="suite.TestSuite"/> -->
    </membersonly>
  </reporter>

  <!-- delete all build files -->
  <target depends="init" description="clean" name="clean">
    <delete dir="${classes.dir}">
    <delete dir="${project.dir}/doc">
    <delete dir="${test.dir}/build">
    <delete file="${lib.dir}/scala-library.jar">
    <delete file="${project.dir}/${ant.project.name}.war">
  </delete>

  <!-- create API documentation in doc folder -->
  <target depends="build" description="scaladoc" name="scaladoc">
    <mkdir dir="${project.dir}/doc">
    <scaladoc classpathref="build.classpath"
        destdir="${project.dir}/doc"
        doctitle="${ant.project.name}" srcdir="${source.dir}"
        windowtitle="${ant.project.name}">
    </scaladoc>
  </target>

</project>

The directory structure differs slightly from that for a standalone application. We have an additional web directory for web content. It contains the WEB-INF directory where all class files and  libraries go. During development, class files are directly copied to the server without packaging. This ensures faster deploy/test cycles. In addition, there are Ant tasks for putting class files into a single jar  and for creating a distributable war file (war, packedwar). I have tried to keep the number of properties that need to be changed down to a minimum. Obviously, you need to set the home directories of your Scala, Scalatest, and Tomcat installations. You might also have to change the admin password for the Tomcat manager application which is used for automated deployment. Here is a summary of the defined targets:

  • build = compile your webapp and put class files into the WEB-INF/classes directory.
  • war = build a deployable web archive.
  • packedwar = build a deployable web archive with all class files packed into a jar.
  • deploy = deploy your webapp on Tomcat server.
  • reload = update project files on server and reload application.
  • undeploy = remove webapp from Tomcat server.
  • test = build and run unit tests (using Scalatest).
  • clean = delete all build files.
  • scaladoc = create API documentation from sources and put it into the in ./doc directory.
  • package = create a distributable zip archive that contains all dependencies plus Scaladocs.

Scala With Ant

Although Scala is tool-agnostic, the Scala community seems to have a preference for the Apache Maven build tool. I am not quite sure why. Given that most people who learn Scala come from a Java background, what could be more natural than using Ant for Scala development? As a tried-and-true build solution, Ant avoids the headaches and additional learning curve that comes with an unfamiliar build tool. Although I have spent considerable time with Maven as part of my job, Maven and I haven't become friends, and we probably never will. I won't go into the details of the how and why. Instead I'm going to present and explain a few Ant scripts for Scala development. If you are new to Scala, I hope that this will give you a headstart with your new Scala projects. After all, you have a new language to learn and little time to bother with tools.

<project default="run" name="helloworld">

  <!-- root directory of this project -->
  <property name="project.dir" value=".">

  <!-- main class to run -->
  <property name="main.class" value="app.HelloWorld">

  <!-- root directory of Scala installation -->
  <property name="scala.home"
    value="C:\\Program Files\\Scala">

  <!-- location of scalatest.jar for unit testing -->
  <property name="scalatest.jar"
    value="C:\\Program Files\\...\\scalatest-1.0.jar">

  <target name="init">

    <!-- derived path names -->
    <property name="build.dir" value="${project.dir}/build">
    <property name="source.dir" value="${project.dir}/src">
    <property name="test.dir" value="${project.dir}/test">

    <!-- scala libraries for classpath definitions -->
    <property name="scala-library.jar"
      value="${scala.home}/lib/scala-library.jar">
    <property name="scala-compiler.jar"
      value="${scala.home}/lib/scala-compiler.jar">

    <!-- classpath for the compiler task definition -->
    <path id="scala.classpath">
      <pathelement location="${scala-compiler.jar}">
      <pathelement location="${scala-library.jar}">
    </pathelement>

    <!-- classpath for project build -->
    <path id="build.classpath">
      <pathelement location="${scala-library.jar}">
      <fileset dir="${project.dir}/lib">
        <include name="*.jar">
      </include>
      <pathelement location="${build.dir}/classes">
    </pathelement>

    <!-- classpath for unit test build  -->
    <path id="test.classpath">
      <pathelement location="${scala-library.jar}">
      <pathelement location="${scalatest.jar}">
      <pathelement location="${build.dir}/classes">
    </pathelement>

    <!-- definition for the "scalac" and
      "scaladoc" ant tasks -->
    <taskdef resource="scala/tools/ant/antlib.xml">
      <classpath refid="scala.classpath">
    </classpath>

    <!-- definition for the "scalatest" ant task -->
    <taskdef classname="org.scalatest.tools.ScalaTestTask"
      name="scalatest">
      <classpath refid="test.classpath">
    </classpath>

  </taskdef>

  <!-- delete compiled files -->
  <target depends="init" description="clean" name="clean">
    <delete dir="${build.dir}">
    <delete dir="${project.dir}/doc">
    <delete file="${project.dir}/lib/scala-library.jar">
  </delete>

  <!-- compile project -->
  <target depends="init" description="build" name="build">
    <buildnumber>
    <tstamp>
    <mkdir dir="${build.dir}/classes">
    <scalac classpathref="build.classpath" deprecation="on"
       destdir="${build.dir}/classes"
       force="never" srcdir="${source.dir}">
      <include name="**/*.scala">
    </include>
  </scalac>

  <!-- run program -->
  <target depends="build" description="run" name="run">
    <java classname="${main.class}" classpathref="build.classpath">
  </java>

  <!-- build unit tests -->
  <target depends="build" name="buildtest">
    <mkdir dir="${build.dir}/test">
    <scalac classpathref="test.classpath" deprecation="on"
      destdir="${build.dir}/test" force="never" srcdir="${test.dir}">
      <include name="**/*.scala">
    </include>
  </scalac>

  <!-- run unit tests -->
  <target depends="buildtest" description="test" name="test">
    <scalatest runpath="${build.dir}/test">
      <reporter config="YFABRT" type="stdout">
      <membersonly package="suite">
      <!-- <reporter type="graphic" config="YFABRT"/> -->
      <!-- <suite classname="suite.TestSuite"/> -->
    </membersonly>
  </reporter>

  <!-- create a startable *.jar with proper
    classpath dependency definition -->
  <target depends="build" description="jar" name="jar">
    <mkdir dir="${build.dir}/jar">
    <copy file="${scala-library.jar}" todir="${project.dir}/lib">
    <path id="jar.class.path">
       <fileset dir="${project.dir}">
         <include name="lib/**/*.jar">
       </include>
    </fileset>
    <pathconvert dirsep="/" pathsep=" " property="jar.classpath">
      <path refid="jar.class.path"></path>
      <map from="${basedir}${file.separator}lib" to="lib">
    </map></pathconvert>
    <jar basedir="${build.dir}/classes"
      destfile="${build.dir}/jar/${ant.project.name}.jar"
      duplicate="preserve">
      <manifest>
        <attribute name="Main-Class" value="${main.class}">
        <attribute name="Class-Path" value="${jar.classpath}">
        <section name="Program">
          <attribute name="Title" value="${ant.project.name}">
          <attribute name="Build" value="${build.number}">
          <attribute name="Date" value="${TODAY}">
        </attribute>
      </attribute>
    </attribute>
  </section>

  <!-- create API documentation in doc folder -->
  <target depends="build" description="scaladoc"
    name="scaladoc">
    <mkdir dir="${project.dir}/doc">
    <scaladoc classpathref="build.classpath"
      destdir="${project.dir}/doc"
      doctitle="${ant.project.name}" srcdir="${source.dir}"
      windowtitle="${ant.project.name}">
  </scaladoc>

  <!-- create a zip file with binaries for distribution -->
  <target depends="jar, scaladoc"
    description="package" name="package">
    <zip destfile="${build.dir}/${ant.project.name}.zip">
      <zipfileset dir="${build.dir}/jar"
        includes="${ant.project.name}.jar">
      <zipfileset dir="${project.dir}" includes="lib/*"/>
      <zipfileset dir="${project.dir}" includes="doc/*"/>
      <zipfileset dir="${project.dir}/txt" includes="*"/>
    </zip>
  </target>

</project>

Download "helloworld.scala" with Ant build file. Fortunately, the designers of Scala provided built-in Ant tasks in the compiler library, which makes the integration of Ant straightforward. The XML code above shows the build.xml for a typical Scala standalone application, such as a GUI program or a console application. It is reasonably complete and it provides Ant targets for all common tasks:

  • build = compile your project and put class files into the ./build directory.
  • run = run program.
  • jar = create a startable jar archive from class files.
  • test = build and run unit tests (using Scalatest).
  • scaladoc = create API documentation from sources and put it into the in ./doc directory.
  • package = create a distributable zip archive that contains all dependencies plus Scaladocs.

The directory tree is kept as simple as possible:

Scala Project Skeleton
  • build – temporary directory for class files and other binaries
  • lib – contains external dependencies (*.jar files)
  • src – contains Scala source code tree
  • test – contains Scala unit tests
  • txt – contains files (such as license text) that are packaged with the distributable zip archive

If your project depends on external jars, all you have to do is to drop these into the ./lib folder. Ant will take care of creating appropriate class paths, entering them into the manifest of the startable jar file, and including them in the distribution package. The Scala library is packed automatically so that the resulting package can be deployed on computers that don't have Scala installed (only a standard JRE is required). To use this Ant script on your computer, you need to change the values of the scala.home and scalatest.jar properties to match the location of your Scala and Scalatest installations (never mind the latter if you use another unit testing framework). If you are working in a team, it is good practice to put Ant properties into a separate properties file, so that the build file can be checked into a source code management tool. I have included them here for simplicity. One of the advantages of using Ant is that it has very good integration with IDEs, such as Eclipse. The only twist is that Eclipse does not honour the classpaths defined in build.xml for its own incremental compilation process used to mark syntactical errors. Thus if you import the Ant project into Eclipse, you have to tell Eclipse about the dependencies using the Project/Properties/Java Build Path option. Ant in Eclipse

Scala Job Market

Is there such a thing as a Scala job market? According to my sporadic research during this month, not really. A quick keyword search for “Scala” on monster.com came up with 14 listings in the US, 1 listing in the UK, 4 in Germany and 1 in India. In addition, I found 2 listings on javajobs.net and 2 on technojobs.co.uk. Most employers in these listings don’t hire people for Scala development, but the job ad says something like “…knowledge of a second JVM language, such as Scala, etc. is advantageous…” Freelancer markets like elancer.com, rentacoder.com, etc. show a similar picture. There are virtually no bids for Scala programmers at this time. On elance.com you can find 19 people who offer Scala development services (compared to roughly 11,000 Java programmers); on the German gulp.de site there are 30 Scala programmers (compared to roughly 12,000 Java programmers).

The Scala job statistics published today at www.itjobswatch.co.uk does not look all that promising either. Scala skills are requested only in 0.025 % of all IT Jobs, however, this number has grown tenfold since last year.

Scala Job Market (UK)

Source: http://www.itjobswatch.co.uk/jobs/uk/scala.do

So what does this tell us?

If you program in Scala, you must be either an academic, a hobbyist, or a programmer with too much leisure time. Currently, there is no significant demand for Scala professionals. But this might change. The Java community has received Scala quite positively so far and the language has a rapidly growing fan base. There are several high-profile websites that have recently begun development efforts in Scala, notably Twitter and LinkedIn. There are also a number of large-scale commercial projects in Scala being started recently. Martin Odersky, the creator of Scala, is quite upbeat about the commercial future of Scala. In this talk, given at LinkedIn earlier this summer, he outlines his vision for the near future of Scala. He also mentions a number of companies that have taken up projects in Scala, among them Ebay, LinkedIn, NatureNews, OfficeDepot, SAIC, Siemens, Sony, Twitter and Xerox.