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.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

* Copy this password:

* Type or paste password here:

37,539 Spam Comments Blocked so far by Spam Free Wordpress

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>