Blog

Josh Trotter

How to build and package a GWT app to a WAR file using Ant

by Josh Trotter
in GWT
23 Apr 2011  |  12 Comments

This article is intended as a step by step guide for automating the construction of a WAR file from your GWT application using an Ant script. It covers the following steps

  • Establishing the project structure
  • Compiling the source - Java & Javascript
  • Packaging and accessing static resources - Images, CSS, property files
  • Packaging the deployment archives - JAR & WAR files

Establishing the project structure

Create a new GWT Web Application project and setup the directory structure as per the following screenshot

com.rubiconred.buildtutorial: This is where the .gwt.xml module file lives and we have added a public directory. The public directory is where we will place all of our static resources (e.g. Image files) that are accessed directly from within our GWT source code (see further below).

com.rubiconred.buildtutorial.client: This is where we place our GWT source code that is not referenced anywhere in our server code. This code will be compiled to Javascript but not to .class files. Note that you should not be putting RemoteService classes in here as these interfaces are implemented by server-side RemoteServiceServlet classes.

com.rubiconred.buildtutorial.shared: This is where we place our GWT source code that is referenced by both client and server code. This code will be compiled to both Javascript and to .class files. This is the place to put RemoteService classes.

com.rubiconred.buildtutorial.server: This is where we have access to the full Java APIs and will be the location for our RemoteServiceServlet classes. This code will not be compiled to Javascript but only to .class files.

Note: To ensure the client & shared packages will both be compiled to Javascript the following must be present in your .gwt.xml module file (generated by default if using Eclipse plugin)

<!-- Specify the paths for translatable code -->
  <source path='client'/>
  <source path='shared'/>

war/client_resources: We will use this directory to store our static resources that are accessed directly from the BuildTutorial.html or BuildTutorial.css files. Remember static resources accessed from within the GWT code should be placed in the public directory as described above.

war/server_resources: We will use this directory to store static resources used by our server code, such as properties or XML files.

build.xml: Create a new file build.xml and place it on the project root directory. Following is the skeleton of this build script which we will flesh out over the next few steps to automate the build and packaging process for us.

<?xml version="1.0"?>
<project name="BuildTutorial" basedir="." default="war">
 
    <target name="prepare"/>
    <target name="clean"/>
    <!-- Compile the java source code using javac -->
    <target name="compile" depends="prepare/">       
    <!-- Invoke the GWT compiler to create the Javascript for us -->
    <target name="gwt-compile" depends="compile"/>
    <!-- Package the compiled Java source into a JAR file -->
    <target name="jar" depends="compile"/>   
    <!-- Copy the static server resources into the required
    directory ready for packaging -->   
    <target name="copy-resources"/>   
    <!-- Package the JAR file, Javascript, static resources
    and external libraries into a WAR file -->
    <target name="war" depends="gwt-compile, jar, copy-resources"/>
    <!-- Deploy the WAR file (optional) -->
    <target name="deploy" depends="war"/>
   
</project>

Compiling the project source

We will begin by establishing the Ant tasks in our build script that will compile our source code. For a GWT app this includes the additional task of invoking the GWT compiler to build our client and shared code to Javascript.

First we should establish the properties that we will reference throughout the build script. Add the following to the top of build.xml and update the property values to match your own project and domain names.

    <property name="gwt.module.name" value="com.rubiconred.buildtutorial.BuildTutorial"/>
    <property name="server.resources.name" value="server_resources"/>
    <property name="jar.name" value="buildtutorial.jar"/>
    <property name="war.name" value="buildtutorial.war"/>
    <property name="src.dir" location="src"/>
    <property name="server.resources.dir" location="war/${server.resources.name}"/>
    <property name="build.dir" location="build"/>   
    <property name="build.server.resources.dir" location="war/WEB-INF/classes/server_resources"/>       
    <property name="lib.dir" location="war/WEB-INF/lib"/>
    <property name="gwt.client.dir" location="com/rubiconred/soauiext/client"/>

Next add the following to establish the classpath that we will reference when compiling. Any third party JARs you are using in your project should be available in the war/WEB-INF/lib directory.       
    <path id="project.classpath">       
        <fileset dir="${lib.dir}">
            <include name="**/*.jar"/>
        </fileset>
    </path>

Setup Ant tasks to create and delete the build directory. The build directory is used as a temporary staging area for the compiled source code during the build process.

    <target name="prepare">
        <mkdir dir="${build.dir}"/>
    </target>
   
    <target name="clean">
        <delete dir="${build.dir}"/>
    </target>

Next is the Ant task to invoke the Java compiler. Note that while we are compiling everything in the src dir (client, shared, server) later when we package this into the JAR we will exclude the client classes. The client classes are only included here as they are required by the GWT compiler in the next task.

    <target name="compile" depends="prepare">       
        <javac srcdir="${src.dir}" destdir="${build.dir}">
            <classpath refid="project.classpath"/>
        </javac>       
    </target>

Now we are ready to invoke the GWT compiler. In order for the GWT compiler to work the following JAR files must be on the classpath

  • gwt-dev.jar
  • gwt-user.jar

Add these files to the war/WEB-INF/lib directory. You should find them in your Eclipse installation directory (GWT 2.2) ECLIPSE_HOME\plugins\com.google.gwt.eclipse.sdkbundle.2.2.0_2.2.0.v201102111811\gwt-2.2.0

    <target name="gwt-compile" depends="compile">
        <java failonerror="true" fork="true" classname="com.google.gwt.dev.Compiler">
            <classpath>
                <!-- src dir is added to ensure the module.xml file(s) are on the classpath -->
                <pathelement location="${src.dir}"/>               
                <pathelement location="${build.dir}"/>
                <path refid="project.classpath"/>
            </classpath>
            <jvmarg value="-Xmx256M"/>
            <arg value="${gwt.module.name}"/>
         </java>
     </target>

Packaging and accessing static resources

One of the trickiest parts of packaging and deploying a GWT app is figuring out where to place the various types of static resource files and how to access them in your code so that they can be located after you have deployed to an application server just as they are in your development environment. The following list shows a strategy that I have found effective

Resource Type
Client resources accessed directly within your GWT code
e.g. Image files used by an Image widget

Access Method
Image image = new Image(GWT.getModuleBaseURL() + "/images/myImage.png");

Resource Type
Client resources accessed from the HTML or CSS files
    

Access Method
background-image
: url(client_resources/images/light_blue_vert.png);

Resource Type
Server resources accessed from server side code
e.g. Properties file

 

Access Method
 InputStream in = this.getClass().getClassLoader().getResourceAsStream("server_resources/serverProps.properties");
props = new Properties();
props.load(in);

 

As part of the build process we need to copy the server_resources into the WEB-INF/classes directory so that they will be properly packaged into the WAR file and can be accessed when we deploy. The following Ant task will take care of this. It may also be necessary to invoke this target during development to keep the resource directories in sync so that they can be accessed when running in development mode too.

   <target name="copy-resources">
        <copy todir="${build.server.resources.dir}" preservelastmodified="true">
            <fileset dir="${server.resources.dir}"/>           
        </copy>
    </target>

Packaging the deployment archives

The first step here is to package the compiled Java source into a JAR file that will then be packaged into the WAR file in the WEB-INF/lib directory as part of the next step. Because we don't want to deploy any unnecessary code to the server we exclude the client classes from the package.

    <target name="jar" depends="compile">       
        <jar jarfile="${lib.dir}/${jar.name}" basedir="${build.dir}/">
            <!-- Don't wrap any of the client only code into the JAR -->
            <exclude name="${gwt.client.dir}/**/*.class"/>
        </jar>   
    </target>

Now we are ready to build the WAR file. The following task packages the JAR, Javascript, static resources and third party JAR files (excluding gwt-dev.jar & gwt-user.jar) that we established during the previous build targets into the deployable WAR file that we require.

    <target name="war" depends="gwt-compile, jar, copy-resources">
        <war basedir="war" destfile="${war.name}" webxml="war/WEB-INF/web.xml">
            <exclude name="WEB-INF/**" />
            <exclude name="${server.resources.name}/**"/>
            <webinf dir="war/WEB-INF/">
                <include name="classes/${server.resources.name}/**" />
                <include name="**/*.jar" />
                <exclude name="**/gwt-dev.jar" />
                <exclude name="**/gwt-user.jar" />
            </webinf>
        </war>
    </target>

Final Steps

Run the default task by right clicking on the build.xml file and selecting Run As > Ant Build. If you refresh the project directory in Eclipse you will notice a new build directory containing your compiled source has been added. You can remove this using the Ant target clean if you wish. You will also find buildtutorial.war has been added to the root directory of your project. If you copy this WAR file over to your server and deploy it onto the application server you will be able to run your GWT app in the deployed environment. It is also possible to automate the deployment step with Ant but that is beyond the scope of this article.

And here is the final build.xml contents

<?xml version="1.0"?>
<project name="BuildTutorial" basedir="." default="war">

    <property name="gwt.module.name" value="com.rubiconred.buildtutorial.BuildTutorial"/>
    <property name="server.resources.name" value="server_resources"/>
    <property name="jar.name" value="buildtutorial.jar"/>
    <property name="war.name" value="buildtutorial.war"/>
    <property name="src.dir" location="src"/>
    <property name="server.resources.dir" location="war/${server.resources.name}"/>
    <property name="build.dir" location="build"/>   
    <property name="build.server.resources.dir" location="war/WEB-INF/classes/server_resources"/>       
    <property name="lib.dir" location="war/WEB-INF/lib"/>
    <property name="gwt.client.dir" location="com/rubiconred/soauiext/client"/>

    <path id="project.classpath">       
        <fileset dir="${lib.dir}">
            <include name="**/*.jar"/>
        </fileset>
    </path>  

    <target name="prepare">
        <mkdir dir="${build.dir}"/>
    </target>
   
    <target name="clean">
        <delete dir="${build.dir}"/>
    </target>  

    <!-- Compile the java source code using javac -->
    <target name="compile" depends="prepare">       
        <javac srcdir="${src.dir}" destdir="${build.dir}">
            <classpath refid="project.classpath"/>
        </javac>       
    </target>      
    <!-- Invoke the GWT compiler to create the Javascript for us -->
   <target name="gwt-compile" depends="compile">
        <java failonerror="true" fork="true" classname="com.google.gwt.dev.Compiler">
            <classpath>
                <!-- src dir is added to ensure the module.xml file(s) are on the classpath -->
                <pathelement location="${src.dir}"/>               
                <pathelement location="${build.dir}"/>
                <path refid="project.classpath"/>
            </classpath>
            <jvmarg value="-Xmx256M"/>
            <arg value="${gwt.module.name}"/>
         </java>
     </target>
    <!-- Package the compiled Java source into a JAR file -->
    <target name="jar" depends="compile">       
        <jar jarfile="${lib.dir}/${jar.name}" basedir="${build.dir}/">
            <!-- Don't wrap any of the client only code into the JAR -->
            <exclude name="${gwt.client.dir}/**/*.class"/>
        </jar>   
    </target>
    <!-- Copy the static server resources into the required
    directory ready for packaging -->   
    <target name="copy-resources">
        <copy todir="${build.server.resources.dir}" preservelastmodified="true">
            <fileset dir="${server.resources.dir}"/>           
        </copy>
    </target>   
    <!-- Package the JAR file, Javascript, static resources
    and external libraries into a WAR file -->
    <target name="war" depends="gwt-compile, jar, copy-resources">
        <war basedir="war" destfile="${war.name}" webxml="war/WEB-INF/web.xml">
            <exclude name="WEB-INF/**" />
            <exclude name="${server.resources.name}/**"/>
            <webinf dir="war/WEB-INF/">
                <include name="classes/${server.resources.name}/**" />
                <include name="**/*.jar" />
                <exclude name="**/gwt-dev.jar" />
                <exclude name="**/gwt-user.jar" />
            </webinf>
        </war>
    </target>   
   
</project>

 

 


 
Comments (12)

Hi Magallo,

Comments are processed by an administrator before appearing - hence the delay. As for your question I believe this is an example where you want additional flexibility and control beyond the auto-generate features of Eclipse and so you should really consider creating your own build script as described in this article.

20 Apr 2012, Josh Trotter

I try to add a comment but it doesn't appear... What's happen?

19 Apr 2012, Magallo

Hi, I read this article and I found it very interesting. However I see that in Eclipse there is the possibility to deploy a war file just opening the context menu with right-click of the mouse on the module name file .java in the package explorer and choosing the "Google Web Toolkit->Deploy module" menu item. Doing this, Eclipse will automatically create a build.xml file (not in the root, but in the main package, i.e. in this article example it would be com.rubiconred.buildtutorial). Then the build.xml script is generated and the package is created. This works but there is a problem. The file gwt-dev-jar is copied in the WEB-INF/lib directory and it is included in the war file. I would like to change this build.xml file but every time I build choosing "Google Web Toolkit->Deploy module" from the context menu, it is deleted and re-generated so I can't edit it manually. Where I can set some customization so that the build.xml automatically generated does what I want? Please help.

17 Apr 2012, Magallo

I created a build file for GWT 2.4 but the gwt-compile fails. The compile task works however. I get the following:

[java] [ERROR] Unable to find 'com/google/gwt/user/theme/clean/Clean.gwt.xml' on your classpath; could be a typo, or maybe you forgot to include a classpath entry for source?

I know the gwt-user.jar is in the classpath otherwise the sources would not compile.

8 Mar 2012, BeeDeeSmile

Thank you is ok.

it's work

9 Jan 2012, djilal

Thanks a lot, Its really helpful.

20 Dec 2011, Ashok Goyal

Hi,

I am getting the following error

"Unable to find 'com/google/gwt/user/User.gwt.xml' on your classpath; could be a typo, or maybe you forgot to include a classpath entry for source?"

I am using gwt-2.1.0 version.I could not find the issue.

Can you help me out of it.

7 Dec 2011, kamal

Hi Jay,

The problem you describe sounds like the gwt module file starATS.gwt.xml is available on the classpath of your eclipse project but not the classpath defined for the build script. If this module file is an external dependency (ie it is not in your project src directory) then you need to make sure it is available in the project.classpath path defined in the build script, the easiest way to do this is to place the JAR containing the module file inside the war/WEB-INF/lib directory.

If it is not an external dependency then it may simply be a typo in the value you have defined for the gwt.module.name property. The correct format is to define the package name followed by the name of the module file (without the .gwt.xml extension). For example it might be com.astrixsoft.projectname.starATS

4 Oct 2011, Josh Trotter

I have define sample project as describe.

I am able to build using GWT compiler from Eclipse IDE , when I run from build script its give me following error :

[java] [ERROR] Unable to find 'src/com/astrixsoft/starATS.gwt.xml' on your classpath; could be a typo, or maybe you forgot to include a classpath entry for source?

4 Oct 2011, Jay

Its not my style to leave a comment,

But it took me 5 minutes, to follow your instructions to create the war i wanted.

Kudos for being short & clear.

28 Jun 2011, Nemo

Thank you so much. This is the most straightforward instruction about building a war file for GWT on the planet. It works.

25 Jun 2011, mobilego

Thank you! A real ant script doing ALL the work!

2 Jun 2011, Lasse

RSS Feed Subscribe to the Rubicon Red Blog

 
Leave A Comment

Name *

Email * (will not be published)

Website

Comment *

Please type the characters you see below

Visual verification
Hard to read? Click here for a new code.