The purpose of today's lab is to learn about two Java technologies currently in vogue called Ant and Tomcat. We are teaching them together because in order to use Tomcat, it is very helpful to first know Ant.
In this class, you have mainly used graphical IDEs such as DrJava and Eclipse to compile and run your programs. However, when working on larger software projects involving multiple people, one typically wants to structure the project in such a way that it is not tied to a particular IDE. One benefit is that each developer can work in whatever environment she prefers --- DrJava, Eclipse, emacs, vi, etc. Another benefit is that automated programs can be invoked from the command line to perform useful tasks, for example, on a nightly schedule to make sure the source files all compile. Build files are used for this purpose. A build file specifies how a set of source files should be compiled (or tested, deployed, and so on). A tool is launched from the command-line to interpret the build file and carry out the instructions.
An older technology for build files that you will almost certainly encounter sooner or later is the Unix command-line tool make. It is still very commonly used, especially for C and C++ projects, although it can be used just as easily for other languages. The build files used by make are called makefiles, and this term is more or less synonomous with build files. Just to give you an idea of what a makefile looks like, here is an example corresponding to a C program with a single source file main.c:
all: main
main.o: main.c
gcc -g -Wall -c main.c
main: main.o
gcc -g main.o -o main
clean:
/bin/rm -f main main.o
Despite its ubiquity, make is widely viewed as a less-than-ideal tool, suffering from an arcane and finicky syntax, a lack of built-in high level commands which makes writing portable makefiles difficult (that is, makefiles that run on many platforms instead of just one), and other problems. For these reasons, many other build file systems have been proposed over the years, although few have caught on.
ant is a build file system for Java programs. It has only been around for a few years, but it has already become widely used. An ant build file is an XML file following a certain syntax. (XML stands for "extensible markup language" and is a plain text file format that looks a lot like HTML.)
The easiest way to learn how ant build files work is by example. We'll try writing an ant build file for an old assignment, Homework 7. An ant build file is usually called build.xml. We'll put it in the same directory as the source files (although other configurations are possible):
% cd ~/hw09 % emacs build.xml &Now, here's what we'll put in our first build.xml file:
<project name="hw09" default="build" basedir=".">
<target name="build">
<javac srcdir="."/>
</target>
</project>
We're ready to compile the project using the build file. Since we
used the default file name build.xml, we just have to type
ant to build the project:
% ant
Buildfile: build.xml
build:
[javac] Compiling 11 source files
BUILD SUCCESSFUL
Total time: 2 seconds
So far, this isn't very interesting; we could have done all this by
typing javac *. But there is much more you can do. For one
thing, there are different targets which you can define, to
perform different operations. These operations are usually lists of
tasks such as javac above. Suppose we wanted to add a
target to clean up after a compilation by removing all the
.class files. We could do this by modifying our file to
read:
<project name="hw07" default="build" basedir=".">
<target name="build">
<javac srcdir="."/>
</target>
<target name="clean">
<delete verbose="true">
<fileset dir="." includes="**/*.class"/>
</delete>
</target>
</project>
Here we have defined a new target called clean. When
executed, this target deletes class files in the current directory via
the delete task. Now, to remove the .class files,
we do:
% ant clean Buildfile: build.xml clean: [delete] Deleting 14 files from /Users/tjgreen/hw07 [delete] Deleting /Users/tjgreen/hw07/AllAcross.class [delete] Deleting /Users/tjgreen/hw07/ArrayBT$Iter.class [delete] Deleting /Users/tjgreen/hw07/ArrayBT.class [delete] Deleting /Users/tjgreen/hw07/ArrayNode$ArrayOrder.class [delete] Deleting /Users/tjgreen/hw07/ArrayNode.class [delete] Deleting /Users/tjgreen/hw07/BinaryHeap.class [delete] Deleting /Users/tjgreen/hw07/BinaryTree.class [delete] Deleting /Users/tjgreen/hw07/BTIterator.class [delete] Deleting /Users/tjgreen/hw07/LinkedBT$Iter.class [delete] Deleting /Users/tjgreen/hw07/LinkedBT.class [delete] Deleting /Users/tjgreen/hw07/LinkedNode.class [delete] Deleting /Users/tjgreen/hw07/Node.class [delete] Deleting /Users/tjgreen/hw07/PriorityQueue.class [delete] Deleting /Users/tjgreen/hw07/Tester.class BUILD SUCCESSFUL Total time: 2 seconds
Notice how in this case we explicitly specified the name of the target we wanted to execute, namely clean. When ant is invoked with no arguments, it executes the default target build. So we could have built the project in the first place by typing ant build instead of just ant. Notice also the verbose output. This is because we have specified verbose="true" in the delete task.
In the example above, in order to specify which files were to be deleted, we used a special tag called fileset. By saying dir=".", we specified that the fileset should match files in the current directory. By saying includes="**/*.class", we specified that it should match files ending with .class. Filesets are quite powerful, allowing you to specify recursive paths to follow, both inclusion and exclusion patterns (e.g. include all files ending with .class except for files containing the string test), and more. See the documentation for details.
It is very convenient to be able to introduce something like variables into your build files. For example, you might want to use a variable for the build directory path, so that you can change the build directory easily later without having to touch many places in the build file. The mechanism ant uses for this is called properties. The special property tag allows one to define a property which can then be used in the rest of the build file. They are like Java final variables in the sense that their values are immutable and cannot be changed once defined. Let's see how our example build file would look if we used properties for the source and output directories:
<project name="hw07" default="build" basedir=".">
<property name="srcdir" value="."/>
<property name="destdir" value="out"/>
<target name="build" depends="clean">
<mkdir dir="${destdir}"/>
<javac srcdir="${srcdir}" destdir="${destdir}"/>
</target>
<target name="clean">
<delete verbose="true" quiet="true">
<fileset dir="$destdir}" includes="**/*.class"/>
</delete>
</target>
</project>
Note how we have also used the depends attribute in our build target definition to force all builds to be clean. (This is just to demonstrate the usage of depends.)
Here we summarize a few of the more commonly-used ant tasks. There are many more; a complete list is available in the documentation.
| Task | Description | Example |
| ant | Invokes ant itself on the specified build file. | <ant antfile="subbuild.xml"/> |
| chmod | Change the permissions of a file or all files inside specified directories. |
<chmod file="${dist}/start.sh" perm="700"/>
|
| copy | Copies a file or set of files to a new file or directory. | <copy file="myfile.txt" todir="../some/other/dir"/> |
| delete | Deletes a single file, a specified directory and all its files and subdirectories, or a set of files specified by one or more filesets. |
<delete file="/lib/ant.jar"/> |
| javac | Compiles the specified Java source files. |
<javac sourcepath="" srcdir="${src}"
destdir="${build}">
<include name="**/*.java"/>
<exclude name="**/Example.java"/>
</javac>
|
| javadoc | Generates code documentation using the javadoc tool. | <javadoc sourcepath="." destdir="doc"> |
| mkdir | Creates a directory, creating parent directories as needed. |
<mkdir dir="${dist}/lib"/>
|
| move | Moves a file or directory to a new file or directory, or sets of files to a new directory. | <move file="file.orig" todir="dir/to/move/to"/> |
| property | Sets a property or set of properties (from file or resource) in the project. | <property name="foo.dist" value="dist"/> |
In addition to the built-in ant tasks such as those above, there is a very handy mechanism for supplying custom ant tasks to be used for special purposes, via the taskdef tag.
| taskdef | Adds a task definition to the current project, such that this new task can be used in the current project. | <taskdef name="myjavadoc" classname="com.mydomain.JavadocTask"/> |
In the next section, we will see this mechanism used to deploy tomcat applications.
Here is an example of a "hello, world" servlet. Note that it implements doGet. This means that it will be invoked in response to HTTP get requests. These correspond to the type of request generated when you type in a URL in your browser's address bar or click on a link on a web page. There is another kind of HTTP request called post. This corresponds to the type of request generated when you fill out a web page form and click the "submit" button, for example. To handle this kind of request, a servlet can override the method doPost.
import java.io.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* The simplest possible servlet.
*
* @author James Duncan Davidson
*/
public class HelloWorldExample extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
String title = "Hello World!";
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<body>");
out.println("<a href=\"/examples/servlets/helloworld.html\">");
out.println("<img src=\"/examples/images/code.gif\" height=24 " +
"width=24 align=right border=0 alt=\"view code\"></a>");
out.println("<a href=\"/examples/servlets/index.html\">");
out.println("<img src=\"/examples/images/return.gif\" height=24 " +
"width=24 align=right border=0 alt=\"return\"></a>");
out.println("<h1>" + title + "</h1>");
out.println("</body>");
out.println("</html>");
}
}
To test or run a tomcat application, you have to deploy the files to the web server. Deployment basically just means copying the files (or a pointer to the files). It is a pain in the neck to have to do this by hand, so tomcat ships with a custom ant task to automate the process. Here is an excerpt from a real-world ant build file for a tomcat project (this comes from last year's final project in cse330):
<project name="pmail-skeleton" default="compile" basedir=".">
...
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask"
classpath ="${catalina.home}/server/lib/catalina-ant.jar"/>
...
<target name="install" depends="compile"
description="Install application to servlet container">
<chmod file="${build.home}" perm="755" type="both"/>
<chmod dir="${build.home}" perm="755" type="both" includes="**/*"/>
<deploy url="${manager.url}"
username="${manager.username}"
password="${manager.password}"
path="${app.path}"
localWar="file://${build.home}"/>
</target>
...
</project>