Building GWT applications with “ant”
Today I needed to deploy an early preview version of our GWT based product to the testing server on the customer site. I didn’t mentioned early but our product is a large content repository for famous Bulgarian telecom. It took me 2 hours or maybe more to write and debug ant based build script but it didn’t work. This was the script:
<project name="MyApp" default="dist" basedir="."> <property name="project.name" value="MyApp" /> <property name="src.dir" location="src" /> <property name="src.web.dir" location="src/web" /> <property name="lib.dir" location="lib" /> <property name="build.dir" location="build" /> <property name="build.web.dir" location="build/web" /> <property name="metainf.dir" location="src/web/META-INF" /> <property name="dist.dir" location="dist" /> <property name="gwt.entrypoint.class" value="myproject.MyModule" /> <property name="gwt.sdk.location" location="C:/Program Files/GWT-1.4.10" /> <!-- Include property values --> <property file="build.properties" /> <!-- Java Compile Classpath --> <path id="compile.classpath"> <fileset dir="${lib.dir}" includes="**/*.jar" /> <pathelement path="${java.class.path}/" /> </path> <!-- GWT Compile Classpath --> <path id="gwt.compile.classpath"> <pathelement path="${java.class.path}/" /> <pathelement location="${gwt.sdk.location}/gwt-user.jar" /> <pathelement location="${gwt.sdk.location}/gwt-dev-windows.jar" /> <pathelement location="${src.dir}" /> <fileset dir="${lib.dir}" includes="**/*.jar" /> </path> <!-- Delete everything this build.xml created --> <target name="clean" description="Clean all build directories"> <delete dir="${build.dir}" /> <delete dir="${dist.dir}" /> <delete dir="${gwt.entrypoint.class}" /> </target> <!-- Prep up the tokens --> <target name="init"> <tstamp /> <filter token="implVendor" value="${project.vendor}" /> <filter token="implVendorId" value="${project.vendor.id}" /> <filter token="implVersion" value="${project.version}" /> <filter token="projectName" value="${project.name}" /> <filter token="projectPackage" value="${project.package}" /> <filter token="specVendor" value="${project.spec.vendor}" /> <filter token="specVersion" value="${project.spec.version}" /> </target> <!-- Create the folder structure --> <target name="prepare" depends="init"> <mkdir dir="${build.dir}" /> <mkdir dir="${build.web.dir}" /> <mkdir dir="${build.web.dir}/META-INF" /> <mkdir dir="${build.web.dir}/WEB-INF" /> <mkdir dir="${build.web.dir}/WEB-INF/classes" /> <mkdir dir="${build.web.dir}/WEB-INF/lib" /> <mkdir dir="${dist.dir}" /> </target> <!-- Copy static files to build. GWT compilation will generate static files so run gwt-compile first--> <target name="static" depends="prepare"> <!-- Copy and filter configuration files --> <copy todir="${build.dir}/META-INF" filtering="on" file="${metainf.dir}/MANIFEST.MF" /> <!-- Copy static web resources --> <copy todir="${build.web.dir}" filtering="on"> <fileset dir="${src.web.dir}" /> </copy> <!-- Copy libraries from "lib.dir" directory --> <copy todir="${build.web.dir}/WEB-INF/lib" flatten="true"> <fileset dir="${lib.dir}"> <include name="**/*.jar" /> <exclude name="**/gwt-user.jar" /> </fileset> </copy> </target> <!-- AJAX Compilation with GWT --> <target name="gwt-compile" depends="static"> <java classpathref="gwt.compile.classpath" classname="com.google.gwt.dev.GWTCompiler" fork="true"> <arg value="-out" /> <arg value="${build.dir}" /> <arg value="${gwt.entrypoint.class}" /> </java> </target> <target name="gwt-copy" depends="gwt-compile"> <copy todir="${build.web.dir}"> <fileset dir="${build.dir}/${gwt.entrypoint.class}" includes="*"> </fileset> </copy> </target> <!-- compile files needed for RPC processing --> <target name="java-compile" depends="gwt-copy" description="Compile application components"> <!-- Compile AJAX Java Sources --> <javac srcdir="${src.dir}" destdir="${build.web.dir}/WEB-INF/classes" debug="${compile.debug}" deprecation="${compile.deprecation}" optimize="${compile.optimize}"> <classpath refid="gwt.compile.classpath" /> </javac> </target> <!-- Create Binary Distribution --> <target name="dist" depends="java-compile" description="Create binary distribution"> <mkdir dir="${dist.dir}" /> <!-- Create a runnable web application archive --> <jar jarfile="${dist.dir}/${project.name}-${DSTAMP}.war" basedir="${build.web.dir}" manifest="${build.dir}/META-INF/MANIFEST.MF" /> </target> <target name="deploy" depends="dist"> <copy todir="${tomcat.webapps}" file="${dist.dir}/${project.path}-${project.version}.war" /> </target> </project>
I changed the original project name and class names in the above script name to keep the NDA signed for this project.
The build was successfull, the created WAR file was correct, but the application didn’t work. Why?
The reason was the library “gwt-user.jar”. It is intented to run in GWT Shell, but when deploying on Java EE server you need to replace it with “gwt-servlet.jar”. Otherwise the RPC services will not work saying “Class not found: com.google.gwt.user.client.rpc.RemoteService”. It is strange error because this class is part of “gwt-user.jar” but cannot be loaded.
I had the “gwt-user.jar” and “gwt-servlet.jar” in my “lib” directory but I included the first in the Eclipse’s build path and excluded the second. In the ant script I do the opposite. … and it now works. Chreers!
I was using Tomcat 6, Java 6, Eclipse 3.3 (ahhhh bugger). I first mapped the GWT RPC services to servlets in the WEB-INF/web.xml deployment descriptor. Initially I had an idea to write custom task for the ant script that reads the MyModule.gwt.xml file and XSL transforms it to a section in the web.xml file but this was too complex. I prefer working solutions insted of complex general (and sometime working) ones.