Java servlets are essentially Java programs which extend the functionality of a server. They are not confined to web servers, but are most often referred to in this context. Virtually all references to servlets cite them as a replacement for CGI scripts, so it is easiest to think of them as Java programs that perform CGI functions.
The intriguing thing about servlets is their claimed performance. Traditional CGI scripts written in Perl, C, etc. all have a disadvantage in that a new process must be created for each call of the script. The overhead of process creation and management can be very taxing on a loaded server. Servlets solve this problem by creating a thread for each request, rather than an entire process. A single process for each servlet is created, then a request to the servlet causes a thread to be created to handle it.
Sounds great, but how does one use servlets on Linux? Well, you need a web server that supports both servlets and a Java virtual machine. There are several choices in the web server department. Sun's Java Web Server will probably run on Linux (since it is written in Java), but it is commercial. We'll be using Apache in this article because it is free and is widely used. This means we need a servlet extension for Apache. Livesoftware makes a product called JRun which I have heard great things about, but again, we'll stick strictly to the Apache side of the world and go with their mod_jserv extension.
Next, you'll need to choose a JDK (Java Development Kit) for your system. Again, there are several choices. Two worth mentioning are the Blackdown JDK (http://java.blackdown.org/) and the new OpenGroup JDK (http://www.camb.opengroup.org/RI/java/linux/), which uses a native threads implementation. Since threads are important to servlet performance, your JDK choice might significantly impact performance. I used the Blackdown JDK, because I was familiar with it and knew it was stable. However, the OpenGroup's work is worth looking into if you have the time.
To get servlets up and running on your system, follow the steps below. Note I assume the JDK you chose is installed and working.
The first step is to download the latest version of Apache (http://www.apache.org/) and the latest version of JServ (http://java.apache.org/). These were 1.3.1 and 0.9.11 respectively, at the time of writing.
The second step is to unpack each of the archives using the tar command:
tar -zxvf apache_1.3.1.tar.gz tar -zxvf jserv0.9.11.tar.gz
The third step is to compile Apache with the JServ module. Specify any options you need in the Apache configuration, but make sure to include an --add-module switch to specify where the mod_jserv.c file is located. Here is a simple example of an Apache configuration command:
./configure --prefix=/usr/local/apache --add-module=/usr/local/src/ jserv0.9.11/mod_jserv.cThis will automatically add the JServ module to your Apache configuration. Once configuration is finished, go ahead and make and install the package:
make make installThe fourth step is to compile JServ. Before compiling JServ, you must pick a location for installation. If you are happy with the directory where you unpacked it in step two, then you are set. Otherwise, just move the JServ directory to wherever you want it to reside. To keep things clean, you might want to put it in your Apache installation tree.
Next, the CLASSPATH variable needs to be set. Even though the JServ documentation suggests this may not be necessary, I found the package didn't compile unless it was explicitly set. The CLASSPATH variable must specify the path to your JDK classes and the JSDK classes included in the JServ package. My JDK lives in /usr/local/jdk1.1.6 and the JDK classes archive is in the /lib directory of this tree. I have JServ in /usr/local/apache/jserv, so my CLASSPATH variable would be set as follows:
export CLASSPATH=/usr/local/jdk1.1.6/lib/\ classes.zip:/usr/local/apache/jserv/servclasses.zip
Once this is set, change to the JServ directory and compile it:
cd /usr/local/apache/jserv makeStep five is to configure Apache. JServ requires that a number of configuration parameters be added to your Apache server configuration files. The files are typically located in the /etc directory of your Apache installation tree. Open up httpd.conf with your favorite editor and add the following configuration directives:
ServletBinary: the full pathname to your java binary. For example:
ServletBinary /usr/local/jdk1.1.6/bin/java
ServletClassPath: specifies the path to your various Java classes. JServ requires you to specify the path to the JDK, JSDK and JServ classes. For example:
ServletClassPath\ /usr/local/jdk1.1.6/lib/classes.zip #path to the JDK classes ServletClassPath\ /usr/local/apache/jserv/servclasses.zip #path to the JSDK classes ServletClassPath /usr/local/apache/jserv/classes #path to the JServ classesServletAlias: this is one of the most important directives, since it configures the location of your servlets and how they are accessed. The syntax of the directive is:
ServletAlias uri directory_or_filenamewhere the uri argument specifies how your servlets will be accessed via URLs, and the second argument points to the actual location of the servlets. The second argument can either specify a directory containing the servlets or a ZIP/JAR file containing a collection of servlets.
For example, if your ServletAlias directive was
ServletAlias /servlets /usr/local/apache/servlets
then URLs addressing your servlets would look like http://yourhostname/servlets, and the actual servlets would reside in the /usr/local/apache/servlets directory.
ServletProperties: gives the location of a file containing properties for your servlets. The path can be absolute or relative to Apache's server root and, if not specified, defaults to
conf/servlets.properties
Many properties can be set within the properties file. Arguments can be passed to all servlets with the statement:
servlets.default.initArgs=arg1=val1,arg2=val2,...Arguments can be passed to individual servlets as follows:
servlet.servletname.initArgs=arg1=val1,arg2=val2,...The sixth and final step is to fire up Apache. Well, you should be ready to go at this point, so go into the /sbin directory of your Apache tree and start up the server:
cd /usr/local/apache/sbin ./apachectl start
The Servlet interface provides the foundation for all servlets. All servlets must either implement this interface or be extensions of a class which implements it. The Servlet package provides a class called HttpServlet which implements the Servlet interface, so as a servlet developer, much of the work is done for you. The Servlet interface allows the creation of generic servlets, but we will only look at how to create servlets that act as CGI scripts. For this, all you have to worry about is the HttpServlet class.
Let's step through the code of the simple servlet shown in Listing 1 to see how it works. This servlet overrides the doGet method provided by the HttpServlet class. doGet is called when a client makes a GET request to the servlet. Here we have the servlet respond with a simple web page that gives the standard “Hello World” message. The doGet method gets two objects, HttpServletRequest and HttpServletResponse, which encapsulate information that allows the servlet to obtain information from and communicate with the client. For example, the HttpServletResponse object contains a PrintWriter object that can be used to send information back to the client. In this example, we use it to send our “Hello World” message back to the client. We also use the setContentType method of the HttpServletResponse object to inform the client that it will be receiving text/HTML data from the servlet.
Now that we've seen a simple example, let's step back a bit and look at how HTTP Servlets work. Servlets extending the HttpServlet class handle all of their client requests through its service method. The service method understands standard HTTP requests, and calls appropriate methods to handle each request. In the example above, the service would recognize the GET request and call the doGet method accordingly. Similarly, doPost, doPut and doDelete methods are provided to handle other types of HTTP requests.
A servlet's life begins when the servlet's init method is called. The web server calls the init method when it loads the servlet, but before any client requests are handled. The init method is called only once when the servlet is loaded. So, if you need to perform any initialization before your servlet starts handling requests, overload the init method as follows:
public void init(ServletConfig config) throws ServletException { super.init(config); ... }
The init method is passed an object which contains configuration information about the servlet. It is a good idea to store this object to make it available if the client needs it. The easiest way to do this is to call the init method of superclass and pass the ServletConfig object to it. One final tip regarding the initialization of servlets: if your initialization fails and the servlet can't handle client requests, throw an UnavailableException. After initialization takes place, the servlet is up and the service method handles client requests.
Finally, when the servlet is unloaded from the web server, its destroy method is called. The web server waits until all service methods are finished or a certain number of seconds have passed (whichever comes first) before calling the destroy method. In the case of long-running service methods, it is possible the destroy method will be called before all service calls have been completed.
This situation can be handled with a few additional methods and variables. First, create a variable that keeps track of how many service methods have been called and provide synchronized methods for increasing and decreasing the counter, as well as one to return the value of your service counter.
public MyServlet extends HttpServlet { private int numServices = 0; protected synchronized void enterService() { numServices++; } protected synchronized void exitService() { numServices-; } protected synchronized int serviceCount() { return numServices; } }
Now, that we have these counters, we need to modify the service method to increment and decrement them accordingly. This is done by adding a call to the enterService method at the top of the service method, calling the service method of the superclass to handle the real work and then decrementing the counter by calling the exitService method.
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { enterService(); try { super.service(req, resp); } finally { exitService(); } }Next, a flag is needed to determine if the servlet is in the process of shutting down. To accompany this flag, use accessor methods to set the flag and return its value.
MyServlet continued { private Boolean exiting; protected setExiting(Boolean flag) { exiting = flag; } protected Boolean isExiting() { return exiting; } }Now the destroy method should first check if any services haven't completed, then loop until all services are finished.
public void destroy() { if (serviceCount() > 0) { setShuttingDown(true); } while(serviceCount() > 0) { try { Thread.sleep(interval); } catch (InterruptedException e) { } } }Finally, modify any of your methods that may run for a long time to check if the servlet is shutting down, and act accordingly.
public void doPost(...) { /* You could do something like this or put * the check into a loop * that takes a long time */ if(!isExiting) { ... } }So there you have it: a quick introduction to getting servlets up and running on your Linux box and writing some simple ones. If you want to learn more about writing servlets, books are available which cover them in depth. I would also recommend looking at the Java Tutorial, available on Sun's web site; it contains a nice introduction to servlets I used when I started learning about them.