★ wanayoo — archive 1999 http://java.oreilly.com/bite-size/java_1299.htmlNouvelle recherche | Portail wanayoo

BITE-SIZE JAVA

Search Product
Index Press
Room

Perl Center
---
Java Center
---
Web Center
---
Linux Center
---
UNIX Center
---
Windows Center
---
Oracle Center
---
Sys Admin Center
---
Security
Center
---
C/C++ Programming Center
---
Design Center
---
O'Reilly Software Online
---
Online
Books Center
---
Web-Based Training
---
O'Reilly

O'Reilly Java Resource Center

Bite-Size Java Service with a Smile
by Jonathan Knudsen

Jonathan Knudsen

This month we'll dive into the the wonderful world of Java servlets. Servlets are Java programs that run on a web server. Along with other server-side Java technologies like Java Server Pages and Enterprise Java Beans, Java Servlets are becoming a very popular solution for building distributed applications. One of the things that's especially nice about servlets (as compared to applets or applications) is that clients don't need to be running Java. Anybody with a web browser can use servlets, because all the Java runs server-side. The bottom line is that it's a lot easier to deploy Java server applications than Java client applications, at least for now.

In this column, you'll learn about servlets by running three examples:

  • HitCounter is a web page hit counter servlet.

  • LoginServlet demonstrates how to respond to HTML form information.

  • ImageServlet dynamically generates JPEG images using the 2D API.

Request and Response

Web browsing is a process of requests and responses. When you type a URL in your browser, the browser requests a page from a server. The server responds by sending the page. Servlets just extend this idea a little bit. When the browser requests a certain "page", the server runs a servlet to generate a response.

The old way to run programs from a web server was called CGI. Java servlets are a simpler, more efficient, and more elegant solution.

Running Servlets

Servlets run inside a web server. Many web servers now support servlets, including Apache. For this column, I used Sun's own Java Web Server Development Kit (JWSDK). A lighter-weight solution for testing servlets is the servletrunner utility, distributed as part of the Java Servlet Development Kit.

A Hit Counter

A simple servlet generates a count of page hits, giving an indication of how many times the page has been accessed. The crux of this servlet is just an increasing variable, but the rest of the code demonstrates the basic structure of Java servlets. Here's the whole thing:

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class HitCounter
      extends HttpServlet {
    private static final String kFilename = "hitcounter.txt";
    private int mHits;
  
    public void init() {
        try {
          BufferedReader in = new BufferedReader(
                new FileReader(kFilename));
          mHits = Integer.parseInt(in.readLine());
          in.close();
        }
        catch (Exception e) { mHits = 1; }
    }
  
     public void doGet(HttpServletRequest request,
           HttpServletResponse response)
           throws IOException, ServletException {
        response.setContentType("text/plain");
        PrintWriter out = response.getWriter();
        out.println("Counter: " + mHits++ + " hits");
    }
  
    public void destroy() {
        try {
          PrintWriter out = new PrintWriter(
                new FileWriter(kFilename));
          out.println(mHits);
          out.close();
        }
        catch (IOException ioe) {}
    }
}

The first thing to notice is that we're importing the servlet packages javax.servlet and javax.servlet.http. The Servlet API is a standard extension API; to use these packages, you'll need to make sure that these packages are in your CLASSPATH somewhere.

Our servlet extends HttpServlet, which is the base class for most web-based servlets. The servlet has a life cycle, as indicated by the three methods of HitCounter:

public void init()

This method is called by the web server when an instance of the servlet is first created.
public void doGet(HttpServletRequest request, HttpServletResponse response)
This method is called whenever a client (i.e. somebody with a web browser) requests the servlet. The actual request is represented by an instance of HttpServletRequest. The response that will be generated by the servlet is represented by an instance of HttpServletResponse.
public void destroy()
This method is called when a servlet instance is about to be destroyed. Usually, a web server will only destroy a servlet instance when it shuts down, but it might also unload a servlet for efficiency's sake.
In this servlet, the hit counter is represented by a member variable of our servlet, mHits. Every time the servlet generates a response in doGet(), the counter is incremented.

The output itself is sent by obtaining a PrintWriter from the HttpServletResponse object. This PrintWriter represents a pipe directly to the client's browser. Before you send anything, however, you should specify the type of data you will be generating by calling setContentType(). In this case, we're just sending plain text. If you're actually going to create HTML, you'd set the content type to "text/html".

The init() and destroy() methods of HitCounter make the hit count persistent by storing it in a file. The init() method tries to load the current count from the file. If the file is not present, or something else goes wrong, the count is initialized to 1. The destroy() method writes out the current count to the file.

Deploying the Servlet

The process of deploying the servlet depends on the web server you're using. For the JWSDK, I simply added this servlet to the examples/Web-inf/servlets subdirectory of the JWSDK. To test the servlet out, I started the JWSDK running and pointed my browser to the new servlet using this URL: http://127.0.0.1:8080/examples/servlet/HitCounter. Note that the location of the servlet class file does not exactly correspond to the URL of the servlet. If you have everything running correctly, you should see something like this:

Reload the page to make the count go up. Every time you view the page, the servlet is run and the hit counter increases by one.

If you make changes in your servlet, you may need to restart the web server to use the new version of the servlet. The JWSDK, for example, requires a restart.

Server-Side Includes

A web counter is nice and all, but typically it's just a small part of a larger page. Many web servers support server-side includes as a way to embed the output of a servlet in a static HTML page. This way, you could create the bulk of the page in a regular HTML authoring tool, then embed the servlet output wherever you wanted to put the hit counter. This is a little slower for the web server, because it has to search through the pages it's sending out for embedded servlets. Nevertheless, the added convenience may well be worth it. Check your web server's documentation to see if this feature is supported.

Using a Form

The real power of servlets comes from interaction with the user. The next example shows how you can use a servlet to respond to information typed into an HTML form. First, let's look at the form itself, which you can save in a file called LoginForm.html:

<html>
<head><title>LoginForm</title></head>
<body>
<form action=/old?u=http%3A%2F%2F127.0.0.1%3A8080%2Fexamples%2Fservlet%2FLoginServlet&y=1999 method=post>
Please log in:
<br>Name:&nbsp;<input type=text name="name" value="" size=18>
<br>Email:&nbsp;<input type=text name="email" value="" size=18>
<br><input type=submit value="OK">
</body>
</html>

This form has two fields on it where a user can type a name and email address, as shown here:

The HTML specifies that pressing the OK button on this form will call LoginServlet. This servlet can retrieve the form information by calling the getParameter() method of HttpServletRequest. The servlet could do anything it wanted with this information--add it to a database, send some email, add the person to a mailing list. The simple example here just sends back a page with the form information. Here's the entire servlet:

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class LoginServlet
      extends HttpServlet {
    public void doPost(HttpServletRequest request,
          HttpServletResponse response)
          throws IOException, ServletException {
        response.setContentType("text/plain");
        PrintWriter out = response.getWriter();
        out.println("Your name: " + request.getParameter("name"));
        out.println("Your email: " + request.getParameter("email"));
    }
}

This servlet overrides doPost() instead of doGet() because form information will be POSTed to this servlet. The familiar request and response objects are passed to doPost(), just like doGet(). This servlet just pulls out the form information from the request object and prints it out in a response to the user.

Compile and deploy LoginServlet the same way you did for HitCounter. Now point your browser at LoginForm.html. When you press the OK button, LoginServlet is run and generates a response like this:

Generating Images

By now, you are probably getting a feel for this. The basic concept is very simple: your servlet receives a request and generates a response. It's when you couple this basic architecture with the power of the Java APIs that the real magic takes place. Servlets can easily take advantage of cool features like Enterprise Java Beans, RMI, and JDBC.

The next example shows how to dynamically generate an image using the 2D API. There are three caveats that apply to this example:

  1. The servlet environment of your web server should support Java 2. Otherwise, the 2D stuff won't work.

  2. This example also uses the JPEG codecs that come with the Java 2 SDK from Sun. If you're trying to use a different implementation of Java 2, the JPEG codecs may not be available.

  3. The 2D rendering may not work if you try to run the servlet on a server that has no GUI. The 2D API still has a dependency on the windowing system.
That said, it's still a simple example:

import java.awt.*;
import java.awt.image.*;
import java.io.*;

import com.sun.image.codec.jpeg.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class ImageServlet
      extends HttpServlet {
    private static final int kSize = 256;
    private static final String kMessage = "Camelopardalis";
    private BufferedImage mImage;
  
    public void init() {
        mImage = new BufferedImage(kSize, kSize,
                BufferedImage.TYPE_INT_RGB);
    }
  
    public void createImage(String message) {
        Graphics2D g2 = mImage.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
              RenderingHints.VALUE_ANTIALIAS_ON);

        g2.setPaint(new GradientPaint(0, 0, Color.white,
              kSize / 4, kSize / 8, Color.orange, true));
        g2.fillRect(0, 0, kSize, kSize);
        g2.setPaint(Color.red);
        g2.setFont(new Font("Serif", Font.PLAIN, 36));
        g2.translate(20, kSize * 3 / 4);
        g2.scale(1, 4);
        g2.drawString(message, 0, 0);
    }
  
    public void doGet(HttpServletRequest request,
          HttpServletResponse response)
          throws IOException, ServletException {
        String message = request.getParameter("message");
        if (message == null) message = kMessage;
        synchronized(mImage) {
           createImage(message);
           response.setContentType("image/jpeg");
           OutputStream out = response.getOutputStream();
           JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
           encoder.encode(mImage);
           out.close();
        }
    }
}

The init() method simply creates the image that will be sent as a response. Most of the work is done in createImage(), which draws some stuff into the image. In doGet(), the image is created, then encoded to a JPEG stream and sent as a reply. There are a couple of important things here.

First, doGet() needs to be thread-safe. The web server may receive multiple concurrent requests for ImageServlet, which means that multiple threads may be running through doGet() at a time. Because we're using the same image for everybody, we have to be sure to send back the correct results to each request. To make sure things don't get messed up, doGet() synchronizes its image creation and encoding on mImage.

This example also demonstrates that you can receive parameters from a GET. They are encoded in the URL. For example, you could change the "message" parameter by using a URL like this: http://127.0.0.1:8080/examples/servlet/ImageServlet?message=Boogie!. If no "message" parameter is supplied, ImageServlet uses a default.

Compile and install this servlet as before. Your server can now generate fancy 2D output like this:

Summary

Programming servlets is straightforward. The hard part is setting up and configuring your web server. Once that's done, however, servlets are a easy and flexible solution to a lot of tough problems. Servlets excel as a cleaner and more elegant alternative to CGI programs. Furthermore, they can provide a simple and ubiquitous front end to enterprise applications.

Download the source code.


Jonathan Knudsen is an author and developer at O'Reilly & Associates. He is the author of Java Cryptography, Java 2D Graphics, and  The Unofficial Guide to LEGO® MINDSTORMS™ Robots.


O'Reilly Home | O'Reilly Bookstores | How to Order | O'Reilly Contacts
International | About O'Reilly | Affiliated Companies

© 1999, O'Reilly & Associates, Inc.