Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

How do I Write a Java Servlet?


Dr. Dobb's Journal October 1997: Java Q&A

Cliff, vice president of technology at Digital Focus, can be contacted at cliffbdf@ digitalfocus.com. To submit questions, check out the Java Developer FAQ Web site at http://www.digitalfocus.com/faq/.


Anyone who has written CGI programs is familiar with the request/ response stream application model. When a web server receives a request for a CGI program, it loads the program as a new process, feeds it the input that follows the request, and returns the output from the program to the requesting source. The main advantage of CGI programs is that they are accessible via the Web, and require only a generic piece of client software -- a browser.

The list of disadvantages is much longer, however. There is no standard language for CGI programs, so people have used every language imaginable -- Perl, Tcl, csh, C, C++, and now Java. Since Java is becoming the language of choice for web-deployed applications, it makes sense to unify the language used on the server with the language used for the client. The proliferation of CGI programs written in many languages presents a maintenance problem for web site administrators. Often, a large site will have CGI programs written in a variety of languages, depending on the preferences of individual programmers. Some of these programs can be difficult to decipher, especially if they perform communications services or are multithreaded.

CGI programs are also notoriously hard to debug. While there are tools to assist in debugging these programs, they are nonstandard, and you don't know whether the tools you are familiar with will be available on each web server. An inherent problem with CGI programs is that they execute in a context not directly controllable or viewable by you. When an error occurs, more often than not, the only output is a message saying that there was an error.

From a performance point of view, CGI programs are abysmal. Typical process creation and startup time for a single request is of the order of hundreds of milliseconds. The delay becomes longer if the program needs to access other connection-oriented resources, such as databases. Since CGI programs are stateless, it is difficult to design applications that reuse resource connections, so new connections must be created with every request. This, of course, can be overcome with a more sophisticated multitier design, but the inherent problem is the statelessness of the protocol and the simple-minded invocation technique.

Because each invocation of a servlet runs as a thread instead of a comparatively heavyweight process, servlets have better performance than CGI programs. This kind of invocation strategy can be a hundred times faster, requiring less memory and leading to much better scalability. In addition, once invoked, a servlet object remains in memory indefinitely, ready to respond to further invocations, until the administrator stops it or stops the web server, (and therefore, it may contain state). In other words, it does not have to initialize itself every time it is invoked, and it can maintain persistent connections to data sources.

There are other protocols for creating remote or distributed applications. For example, CORBA lets you create object servers that provide a remote call interface. This is a powerful solution, but requires the installation of CORBA services on the server. This is another major component to administer. Servlets, on the other hand, can be installed on any web server that supports servlets. The Java Servlet Development Kit (JSDK) describes the procedure to configure the Netscape, Microsoft, and Apache servers so that they can support servlets.

It won't be long before most HTTP servers support Java servlets out of the box, primarily for security reasons. Security requires that sophisticated system administrators set up and manage CGI facilities at ISP installations in such a way that the system and other users are protected from errant CGI programs, without completely restricting CGI altogether. Most small ISPs don't bother with restrictions, because they do not have the required expertise to set this up. Larger ISPs, like UUNet, allow CGI programming, but CGI processes are restricted to run in a sandbox, or "rbox." The servlet security manager automatically provides such a sandbox, in a standardized way. Over time, administrators will come to understand this security model, which is simpler to deal with than all the issues that can come into play with CGI-related security. Understanding the Java servlet security model does not require a high level of expertise in a particular operating system, and the issues are the same regardless of the platform. This translates into easier administration.

What is a Servlet?

A Java servlet is a Java class that implements the Servlet interface. Conceptually, a servlet is a class that can be instantiated automatically by a server, receive stream input, and send stream output back to the invoking client. The inspiration for servlets comes from the HTTP protocol -- thus, the servlet API is primarily oriented toward HTTP-related functions, but the details of HTTP are abstracted out. HttpServlet, an abstract class that implements Servlet, provides HTTP-specific functionality for processing GET and POST requests. You extend this class if you need to access HTTP header information, such as a user name if authentication is employed (for example, the URL is password protected), or if you need to process POST requests.

In many applications, you will implement the Servlet interface directly. For example, if your servlet has a dual personality and can support RMI invocation, then it will need to extend java.rmi.server.RMIUnicastRemoteObject. You will then have to implement the Servlet methods directly. They are:

  • init(ServletConfig): Initializes the servlet. Once initialized, the servlet remains active until the web server is stopped, or until it is explicitly removed by an administrative action. This is where you would set up the servlet for business; opening a database connection, and reading initialization data, for example. You must also save the ServletConfig parameter that is passed in, because the server may ask for it back by calling your getServletConfig() method. This method is called the first time a servlet is requested.
  • getServletConfig(): Returns the ServletConfig object reference you saved when the server called your init(ServletConfig) method.
  • getServletInfo(): Returns author, version, and copyright.
  • service(ServletRequest, ServletResponse): The entry point called by the server whenever someone invokes your servlet. Each request runs in a separate thread, in the process space (virtual machine) of this servlet object. This method should therefore be reentrant (thread safe). The input stream to read from can be obtained from the ServletRequest argument, and the output stream to write to can be obtained from the ServletResponse argument.
  • destroy(): Releases servlet object resources -- stops threads, closes database connections, and the like. This is the last chance to write persistent data.

The Servlet Development Kit

Javasoft supplies a development kit for creating servlets, the Java Servlet Development Kit (JSDK), available at http://jserv .javasoft.com/. This kit comes complete with Servlet Runner, a test server for testing your servlets.

At the same location, you can find Javasoft's Java Web Server, written entirely in Java. Needless to say, the Java Web Server supports servlets, but you do not have to download it to develop a servlet (although you might want to in order to test deployment modes not supported by the Servlet Runner). In the same way that Hot Java is the canonical final test of an applet running in a browser, and the applet viewer is merely a quick test bench, the Java Server is a full-featured high-performance server for testing servlets (and publishing web content), while the Servlet Runner is a simple test bench (for example, the Servlet Runner cannot test servlets deployed as embedded server includes).

How do I Write a Servlet?

To demonstrate how to write a servlet, I present a servlet called CustomerManager. This servlet can be invoked via an RMI interface and as a servlet. This dual personality is useful in many applications. The servlet could be, for example, a web-based interface for querying status. At the same time, a more full-featured interface for management functions could be accessible only to an RMI client applet.

In my example, the class CustomerManagerImpl (see Listing One implements the Servlet interface. The methods in this interface are those called by a web server to initialize and invoke the servlet. Each invocation will result in a call to my service (ServletRequest, ServletResponse) method. In addition, the very first invocation after each web-server restart will first call my init() method prior to calling service(). Therefore, I perform all first-time initialization in init().

This object will also be called by an RMI client. The services published for that client are specified in the RMI interface for this object (which I call CustomerManager). Example 1 lists its methods (exception specifications have been omitted for brevity). These methods manage a customer database. In my demo, I use an object file, instead of a real database, for simplicity. Thus, in the servlet init() method, I do two things: I register this object as an RMI server object, by calling java.rmi.registry.Registry.rebind(); and I read in the stored Customer object file data, and create an in-memory representation of that data. In a database application, I would likely establish a persistent database connection instead.

The four RMI-based methods in Example 1 must be synchronized, because they access the Customer object. The bodies of these methods are straightforward: They simply provide access to the data in the Customer object, which is a Vector of Customer objects. The Customer object contains data on a customer, including firstname, last name, and age. This data is deliberately simple, so as to not upstage the main points being demonstrated here.

The RMI methods are those called by the management interface to my demonstration system. I provide such an interface in the form of the CustomerManagerApplet applet, which establishes a GUI framework for displaying customer data. It also lets users establish a connection to the servlet (which is an RMI server object) by calling the java.rmi.Naming.lookup() method. Once connected, it allows the user to list, add, modify, and delete customer data. Figure 1 shows this applet.

The CustomerManagerImpl servlet (available electronically; see "Availability," page 3) should be invoked by the web server at least once prior to attempting to access it via the management interface. The reason being that it does not perform an RMI bind (or even exist!) until the web server instantiates it and its init() method is called, so the CustomerManagerApplet will not be able to access it prior to invocation. Remember, this servlet object has a dual personality, and it is the servlet personality that knows how to initialize it, so you must call it that way first.

The service() method is called when the servlet is invoked via a web server. There is only one method entry point, so you have to figure out what to do based on parameters or data passed into the input stream. In this case, you retrieve parameters specifying the first name and last name of a customer, then use those to look the customer up in our customers list. If you find the customer, you retrieve all available information about that customer, and write it to the output stream. If you do not find the customer, you write a suitable message saying so. The intended use of this service is a simple human-readable customer status query. Figure 2 shows the browser output of the servlet.

The complete code for this servlet, including a script file to build it, is available from DDJ at http://www.ddj.com/, and from Digital Focus at http://www.digitalfocus.com/ddj/code/.

How do I Deploy and Invoke It?

Javasoft defines three ways to invoke a servlet:

  • URL. Specify the URL of a servlet directly. This can also be an alias registered with the web server. For example, http:// myhost/servlet/mypackage.myservlet?parm1=abc&parm2=def would invoke the servlet myservlet in package mypackage, in the servlets directory on host myhost. Parameters are specified in the normal HTTP GET query manner.
  • Server-Side Include. Embed the name of the servlet in an HTML page, much the way you embed an applet in an HTML page. However, the <servlet> tag in HTML is processed by the server prior to sending it to the client, and output from the servlet is substituted in its place; see Example 2. The HTML file must have a .shtml extension. A codebase parameter may also be specified. It appears that the Java Web Server, at present, requires a servlet deployed in this way to reside in the server's servlet directory, even if a codebase parameter is specified.
  • Chaining. This is configured at the administration level. Chaining allows servlets to be invoked automatically depending on the MIME type of a data stream. Chaining can be used to automatically filter or process data of certain types.

Signing a Servlet

Servlets are more secure than CGI programs, because they run under the watchful eye of a Java security manager. My demonstration program does two things that could potentially violate Java security policies -- it opens a socket (via RMI), and it reads and writes a file. In the Java Web Server, local servlets installed in the servlets directory are considered trusted, and can perform any of these operations with no difficulty. Thus, all you have to do is place your servlet into the servlets directory (or, if it is in a package, put the package directory in the servlets directory), and then use the Java Web Server admin tool to install the servlet. The Java Web Server also lets you install remote servlets, however, by specifying a remote URL in the admin tool. Remote servlets are not trusted, and they must be signed: You specify the name of the signed JAR file as part of the URL. You can then assign privileges to the remote servlet via an admin access-control list mechanism.

What Servers Support Servlets?

The JSDK comes with patches or plug-ins for Microsoft Internet Information Server, Netscape's Enterprise and FastTrack servers, and the Apache web server. In the case of IIS, all you need to do is install a DLL and make an entry in the registry. IIS 2.0 and 3.0 are supported. For the Netscape servers, you must compile servlets with JDK 1.02, and there are some limitations related to multithreading. The Apache server is the most troublesome (always the case with freeware!), since you have to obtain the source and recompile it.

These reconfigurations may seem inconvenient, but should be weighed against the tremendous benefits of the increased security, performance, and maintainability of servlets when compared to CGI. Further, future versions of these servers will likely have servlet support built in. Pure Java servers, like the Java Web Server, already support servlets.

Conclusion

Servlets are inherently far more secure than their CGI counterparts, and have much better performance.

Servlets are not the only model for remote Java computing, but they are a powerful and simple one, appropriate for many environments, especially those that are primarily web based. When servlets become more widely understood, they may well revolutionize the server side of the Web the same way that Java applets have already revolutionized the client side.


Listing One

/* CustomerManagerImpl.java * Copyright 1997 by Digital Focus. May be used for non-commercial purposes.
 * No warrantee or guaranteed is given or implied. */
 
package customer;
import java.io.*;
import javax.servlet.*;
/** Servlet which has dual-personalities: http and RMI.
 * Allows you to write services that can be accessed from any browser, and 
 * across a firewall without tunneling. */
public class CustomerManagerImpl 
   extends java.rmi.server.UnicastRemoteObject
    implements CustomerManager, Servlet
{
    private ServletConfig servletConfig;
    private java.util.Vector customers;
    static
    {
        // Set security manager for RMI invocation, to protect 
        //    against malicious stubs
        if (System.getSecurityManager() == null)
        {
            System.setSecurityManager(new java.rmi.RMISecurityManager());
            System.out.println("Servlet server has no security manager; 
                                                using RMI security manager");
        }
    }
    public CustomerManagerImpl() throws java.rmi.RemoteException
    {
        super();
    }
    // CustomerStatus methods. The remote entry point implementations are 
    // synchronized, because they may be called my multiple server threads, 
    // either as an RMI service, or as a servlet service.
    public synchronized Customer getCustomer(String firstname, 
                String lastname) throws java.rmi.RemoteException, Exception
    {
        return findCustomer(firstname, lastname);
    }
    public synchronized Customer[] getCustomers() 
                                  throws java.rmi.RemoteException, Exception
    {
        Customer[] ca = new Customer[customers.size()];
        for (int i = 0; i < customers.size(); i++)
        {
            Customer c = (Customer)(customers.elementAt(i));
            ca[i] = c;
        }
        return ca;
    }
    public synchronized void addCustomer(String firstname, String lastname, 
                       int age) throws java.rmi.RemoteException, Exception
    {
        Customer c = new Customer(firstname, lastname, age);
        customers.addElement(c);
        save();
    }
    public synchronized void remCustomer(String firstname, 
                String lastname) throws java.rmi.RemoteException, Exception
    {
        Customer c = findCustomer(firstname, lastname);
       if (c == null) throw new Exception("Not found");
        customers.removeElement(c);
        save();
    }
    protected Customer findCustomer(String firstname, String lastname)
    {
        for (int i = 0; i < customers.size(); i++)
        {
            Customer c = (Customer)(customers.elementAt(i));
            if (c.getFirstname().equals(firstname) && 
                                            c.getLastname().equals(lastname))
            {
                return c;
            }
        }
        return null;
    }
    protected synchronized void load() throws Exception
    {
        // synchronized to protect access to customers object
        System.out.println("Loading CustomerFile...");
        File file = new File("CustomerFile");
        FileInputStream fis = null;
        try { fis = new FileInputStream(file); } 
                                   catch (FileNotFoundException ex) {}
        if (fis == null)
        {
            customers = new java.util.Vector();
        }
        else
        {
            ObjectInputStream ois = 
                        new ObjectInputStream(new FileInputStream(file));
            customers = (java.util.Vector)(ois.readObject());
            ois.close();
        }
        System.out.println("...done loading.");
    }
    protected synchronized void save()
    {
        // synchronized to protect access to customers object
        System.out.println("Saving CustomerFile...");
        File file = new File("CustomerFile");
        try
        {
            ObjectOutputStream oos = 
                         new ObjectOutputStream(new FileOutputStream(file));
            oos.writeObject(customers);
            oos.close();
        }
        catch (IOException ex)
        {
            System.out.println("Error writing customer file:");
            ex.printStackTrace();
       }
        System.out.println("...done saving.");
    }
    // Servlet methods...
    public void destroy()
    {
        // Save file
        save();
    }
    public ServletConfig getServletConfig()
    {
        return servletConfig;
    }
    public String getServletInfo()
    {
        return "CustomerStatus Servlet";
    }
    public void init(ServletConfig c) throws ServletException
    {
        servletConfig = c;
        init();
    }
    public void init() throws ServletException
    {
        try
        {
            java.rmi.registry.Registry registry = 
                            java.rmi.registry.LocateRegistry.getRegistry();
            registry.rebind("CustomerManager", this);
            System.out.println("Server bound to CustomerManager");
        }
        catch (Exception ex)
        {
            System.out.println("Could not bind:");
            ex.printStackTrace();
        }
        // Load file
        try
        {
            load();
        }
        catch (Exception ex)
        {
            System.out.println("Error reading customer file:");
            ex.printStackTrace();
            throw new ServletException("Terminating due to prior error");
        }
    }
    public static void main(String[] args)
    {
        try
        {
            CustomerManagerImpl m = new CustomerManagerImpl();
            m.init();
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
    public void service(ServletRequest request, ServletResponse response) 
        throws ServletException, IOException
    {
        System.out.println("Service...");
        // Identify customer
        String firstname = request.getParameter("firstname");
        String lastname = request.getParameter("lastname");
        if ((lastname == null) || (firstname == null))
        {
            response.getOutputStream().println("Both last name and 
                                              first name must be specified");
            return;
        }
        // Retrieve information on the customer
        System.out.println("firstname=" + firstname);
        System.out.println("lastname=" + lastname);
        Customer customer = null;
        try
        {
            customer = getCustomer(firstname, lastname);
        }
        catch (Exception ex)
        {
            throw new ServletException("Error looking up customer.");
        }
        
        // Return information on the customer
        if (customer == null)
        {
          response.getOutputStream().println("Customer could not be found.");
          return;
        }
        response.getOutputStream().println(customer.toString());
    }
}



Back to Article

DDJ


Copyright © 1997, Dr. Dobb's Journal

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.