Server-Side Java
Pete Welter and John Meier
Introduction
Much of the attention paid to Java has been focused on its ability to create fancy effects and graphic applications viewable in Web browsers. This article focuses on using Java in another context - on the server side of the Web. Java's file and socket APIs are the most robust and mature parts of a majority of Java implementations. Server-side Java can also count on running in a given Java implementation, unlike applets, which must run in whatever Java implementation is used by a given browser.
Java is fast becoming the primary language for object-oriented programming. There is a good chance someone will have written the code that you need, however, and because there is a single Java specification, compiling and integrating the code is usually quite simple. Commercial tools have matured quickly and are quite stable and usable, with enough competition to spur rapid improvements. In terms of speed, just-in-time compilers (JITs) bring the speed of Java up to a small multiple of C++ (2x to 10x), while the Java language provides a simpler and more forgiving environment than C++ (exceptions instead of core dumps, garbage-collection instead of memory leaks).
If you're doing a lot of system calls, or you need to access a lot of OS-specific information or utilities, then a language like Perl might be a better fit. If raw execution speed is a top priority, then C++ or C might be better. The majority of server-side programs, however, fall into neither of these categories.
In the following examples, we'll be applying Java to the familiar Common Gateway Interface (CGI) method of server-side Web programming, with examples using a simple CGI class as a basis for dynamically generating pages, and for processing forms.
Hello World
The CGI class will provide a framework for developing the first CGI Java example of a server-side Java program, the proverbial "Hello World." (A discussion of the details of the CGI class appears later in this article.)
1: public class HelloWorldCGI extends CGI {
2: public static void main(String[] argv) {
3: CGI cgi = new HelloWorldCGI();
4: cgi.handle(argv);
5: }
6:
7: protected void printBody() throws Exception {
8: outputStream.println("<HEAD><TITLE>Hello \
World</TITLE></HEAD>");
9: outputStream.println("<BODY><H1>Hello \
World!</H1></BODY>");
10: }
11: }
Line 1 defines the name of the class HelloWorldCGI, and says to "extend" (subclass) the CGI class, allowing it to use all of CGI's functionality, overriding only the parts needed for the specific CGI. The main() method is called by the Java interpreter when starting up this class. In this case, the program creates an instance of HelloWorldCGI, calling its handle() method, which will take care of all of the handling of the CGI. Don't worry about the details of main(); all you really need to understand is that you're creating an instance of the CGI class (in this case, HelloWorldCGI).
The printBody() method (lines 7-10) is responsible for outputting the HTML for the generated page. Note that the HTML generated starts with the <HEAD> tag, not the usual <HTML>. The CGI class outputs the Content-type header, and the opening and closing <HTML> tags for you. If something were to go wrong inside of printBody(), and an exception were thrown, then the CGI class catches that exception and outputs its error message. Because the opening of the page has already been sent to the browser, the error message is displayed in the browser, which is much more useful than the typical "Server Error" message.
After compiling the HelloWorldCGI class, running the program from the command line (assuming that Java is installed in /usr/java, and that the current directory contains CGI.class and HelloWorldCGI.class):
/usr/java/bin/java HelloWorldCGI
produces:
Content-type: text/html
<HTML><HEAD><TITLE>Hello World</TITLE></HEAD>
<BODY><H1>Hello World!</H1></BODY></HTML>
To complete this example of a server-side Java program, the Web server must invoke this class. However, there's a catch: CGI parameters are passed to programs via environment variables, and Java doesn't know about environment variables.
Shell Scripts as Java "Glue"
In Java, System properties perform an analogous role to environment variables. Calling the function:
System.getProperty("os.name")
will return the name of the operating system that Java is running on. Fortunately, there is a way to set System properties from the outside. The -D option for the Java interpreter allows System properties to be set, so invoking Java like this:
java -DmyProperty=myValue MyClass
will set the myProperty System property to the value myValue. Using this feature, we've written a simple shell script (saving it in a file named HelloWorldCGI) to invoke Java, setting System properties with the names and values of corresponding environment variables:
1: #!/bin/sh
2: cd /usr/ssjava
3:
4: /usr/bin/java/bin/java 5: -DCONTENT_LENGTH="$CONTENT_LENGTH" \
6: -DREQUEST_METHOD="$REQUEST_METHOD" 7: -DQUERY_STRING="$QUERY_STRING" \
8: -DPATH_TRANSLATED="$PATH_TRANSLATED" 9: -DHTTP_USER_AGENT="$HTTP_USER_AGENT" \
10: -DSERVER_SOFTWARE="$SERVER_SOFTWARE" 11: HelloWorldCGI
Line 2 sets the current directory to the directory where the classes reside. (For these examples, assume this is /usr/ssjava.) Line 4 invokes the Java interpreter, while lines 5-10 pass in a basic set of environment variables. Java automatically invokes the main() method of the class HelloWorldCGI.
The simplest way to make the server run the shell script is to keep the shell script in the same directory with all of the classes, and add a CGI directory that maps the virtual directory /ssjava to the actual directory /usr/ssjava. For Apache, this is done by adding a ScriptAlias line to the srm.conf file:
ScriptAlias /ssjava /usr/ssjava
For Netscape servers, it's done under the Programs section in the Administration server. Other Web servers have similar methods for defining CGI directories.
After making sure that the script has execute permissions, invoke it from a browser with:
http://www.yourserver.com/ssjava/HelloWorldCGI
and Hello World appears in lovely bold text.
The CGI Class
CGI.java (see Listing 1) is a simplified version of code we've used for handling CGIs in our Java-based products.
Besides handling errors the CGI class hides the messiness of CGI parameter passing. The CGI class allows variables to be passed in from GET or POST requests, or from the command line (for testing), without subclasses having to know any of these details. The most commonly used methods of the CGI class are:
printBody()
should be overridden by subclasses to output HTML
getValues(String name)
returns all of the values for a given name as an Enumeration
(see java.util)
getValue(String name)
returns the value of a variable with a given name, or the
first value for variables with multiple values
getHeader(String name)
returns the value of a header field, for example
"HTTP_USER_AGENT"
getRequestType()
returns "GET" or "POST," depending on the method used for
the request
getVariables()
returns all of the variable names as an Enumeration
(see java.util)
One example of using these methods is in the CGI.printBody() method, which prints out all of the variables specified in the CGI.
For testing, CGI lets you set variables by providing arguments of the form name=value. You can also specify environment variables by prefacing the argument with a -H, for example:
-HPATH_TRANSLATED=/usr/ssjava/size.html
Being able to run CGIs from the command line makes debugging them much simpler. You can add debug print statements to trace variables or to profile code. Even better, most Java development environments allow their source-level debuggers to start with command-line arguments. Using these tools can save a tremendous amount of debugging time, and can also be a highly effective method of learning Java.
Form Handling
The GuestBookCGI (see Listing 2) class handles both parts of a mini-guestbook application; it outputs the form, and then processes the form, depending on whether or not you're doing a POST action. The GuestBookCGI class illustrates the use of the CGI class' getValue() method, both for HTML generation, and for retrieving the result of a POST form. In addition, the POST case uses the java.io.RandomAccessFile class to append text to a file. Append is an operation which is less intuitive in Java than in many other languages (in which you can open a file in "append" mode).
First, the program runs Guestbook as a GET (the default request method):
/usr/java/bin/java GuestBookCGI name=George
which results in:
Content-type: text/html
<HTML>
<HEAD><TITLE>Guestbook</TITLE></HEAD>
<BODY><H2>Please register in the guest book:</H2>
<FORM METHOD=POST ACTION=GuestBookCGI>
Name: <input name=name value="George" size=20><P>
E-mail: <input name=email> value="" size=20><P>
<input type=submit value="Register in Guestbook">
</BODY>
</HTML>
Note that George is the default value for the "name" input field.
The next example illustrates the form processing part of Guestbook:
/usr/java/bin/java Guestbook -HREQUEST_METHOD=POST name=Betty
email=betty@bettyinc.com
which results in:
Content-type: text/html
<HTML><HEAD><TITLE>Guestbook</TITLE></HEAD>
<BODY>Adding you to the guestbook...<P>
<H2>Thanks Betty!</H2>
</BODY></HTML>
and an entry in the guestbook.txt file like:
---------------
name=Betty
email=betty@bettyinc.com
Creating the glue shell script is a matter of changing the class to be invoked to GuestBookCGI, and saving the shell script as "GuestBookCGI." To access it from a browser, use:
http://www.yourserver.com/ssjava/GuestBookCGI?name=George
which illustrates passing variables to a CGI program via the GET method.
Servlets
The CGI applications we've discussed so far require the shell to start up a Java interpreter every time the CGI is run - an expensive way to implement server-side functionality in terms of CPU resources. There is an more efficient alternative, called a servlet.
Like CGI programs, servlets are Java programs that are run on the server in response to a request for a URL. They can handle form requests, generate HTML pages, connect to other server applications, and access files just as CGIs can. But unlike a CGI, a servlet does not require a new process to be created for every request. Instead, the servlet runs directly in the Java virtual machine built into the Web server.
There are two major APIs for writing servlets - one that works with Netscape servers and another that works on the Java server from Sun. Eventually these are expected to converge to a single servlet API supported on all of the major web servers.
Enabling Servlets on Netscape FastTrack or Enterprise Servers
Using servlets on a Netscape server is as simple as enabling the Java interpreter and accessing the URL for the servlet from your browser:
1) Open your Web browser to the Netscape Administration server.
2) Pick your server from the list of servers installed.
3) Choose Programs from the button bar.
4) Choose Java item from the list on the left.
5) Click the Yes button to enable the Java interpreter. Don't change the Java Applet Directory. Accept the default (something like /usr/ns- home/plugins/java/applets) because it already has some useful examples in it.
6) Press the OK button.
7) Choose Save and Apply button to save your changes and restart the server.
Next, verify that servlets are working using one of the Netscape examples. Open your browser to the following URL, replacing www.yourserver.com with the domain name for your Web server:
http://www.yourserver.com/server-java/BrowserDataApplet
This runs the BrowserDataApplet example and you should see something that looks like this:
Hello 206.168.191.19
You're using Mozilla/4.0b3 [en] (WinNT; I)
You accessed /server-java/BrowserDataApplet
using the GET method
Developing Servlets
The Servlet class (see Listing 3) that we've provided has the same interface as the CGI class. With it, you can easily convert a Java CGI app into a servlet.
We'll do a HelloWorld example of a servlet (see Listing 4). First, copy the Java source files from Listing 3 and Listing 4into the Netscape servlet directory (/usr/ns-home/plugins/java/applets/ for example).
To compile a servlet, use the script provided by Netscape, which modifies the CLASSPATH to include the servlet classes.
cd /usr/ns-home/plugsin/java/applets
../javac HelloWorldServlet.java
Then, access the servlet from a browser using:
http://www.yourserver.com/server-java/HelloWorldServlet
One item to note when developing and testing servlets: the servlet class is loaded when it is first accessed and only unloaded when the server shuts down. After you make changes and recompile the servlet code, you'll have to restart the Web server to force it to load the new servlet class.
Conclusion
Java is an excellent language for server-side development because it creates programs that are portable, robust, efficient, and easily debugged. Java CGIs are a good way to get started because they follow the familiar CGI model and can be used with any Web server. When your Web server supports servlets, you can easily convert from Java CGIs and gain a boost in performance.
References
Source code for these examples
http://www.freshtech.com/ssjava
ftp.mfi.com in /pub/sysadmin
Java Development Kit
http://www.javasoft.com
The Java Tutorial
http://www.javasoft.com/nav/read/Tutorial/index.html
Netscape Java Servlet API
http://developer.netscape.com/library/documentation \
/enterprise/unix/javapi.htm
Javasoft Servet API
http://jserv.javasoft.com/products/javaserver \
/webserver/fcs/doc/index_developer.html
About the Author
Pete Welter (pete@freshtech.com) and John Meier (john@freshtech.com) are software engineers at Freshwater Software, Inc. (http://www.freshtech.com) in Boulder, CO. They are responsible for developing SiteScope, Freshwater's Java-based Web server monitoring product.
|