Chapter 18. Servlets: Java Pressed into Service

Java was first seen by many programmers as a way to enhance Web pages by adding some actual code to them, to be run in the browser. But the real power of Java was unleashed at the other end of the client-server connection, when Java was pressed into service on the Web server—to help serve up pages, sometimes of its own making, to Web clients all across an enterprise.

What You Will Learn

  • What servlets are.

  • How to write a simple servlet.

  • More complex servlet matters (servlet state).

  • An example—our BudgetPro application as a servlet.

Servlets: Program-Centric Server-Side Documents

Servlets are Java programs that are run by a Web server. At its simplest, a servlet is a Java class that is invoked by a Web server (referred to in some contexts as the servlet’s container). A servlet is run not from the command line as a regular Java program, but by visiting its URL. Point a Web browser at a servlet’s address and the Web server (the one which serves up that address) will run the servlet and send its output back to the browser (see Figure 18.1). So you can see that typical output for a servlet is HTML—what better thing to send to a browser?

Servlet diagram

Figure 18.1. Servlet diagram

Now, more and more servlets are using XML as their output and then converting it to HTML via XSLT stylesheets, but we’re trying to keep things simple here.

In their most generic form, servlets are classes which implement the Servlet interface. That means that they provide three methods:

  • init(ServletConfig config)

  • service(ServletRequest request, ServletResponse response)

  • destroy()

The init() method gets called when the Web server starts up the class. (Think of the init() method as a constructor; Java doesn’t allow constructors to be defined for interfaces, so init() plays that role.)

The destroy() method gets called whenever the Web server takes the servlet out of service. This might happen when a system administrator wants to shut down the system, or shut down just that particular Web service.

Naturally, the service() method is the method that gets called whenever requests for this servlet arrive at the Web server. The server knows that the requested service is provided by this servlet, so it packages up certain data and sends it along as a request to the servlet. Thus, servlets can provide data in this generic request/response kind of protocol. Simple, but vague, right now.

Servlets get a bit more interesting when we look at the HttpServlet class. This class extends Servlet and adds two more methods that must be implemented:

  • doGet(HttpServletRequest request, HttpServletResponse response)

  • doPost(HttpServletRequest request, HttpServletResponse response)

We hope that you’ve noticed the similarity between doGet(), doPost(), and the previously mentioned service() method. More on that in a minute.

Perspective

To better understand the interaction with servlets, let’s consider the requests that come to a Web server. Web servers serve up Web pages. At first (in the early days of the Web) that just meant simple flat HTML files, along with a few image types. A Web browser would send a request to a Web server in the form of a URL, such as http://www.dom.com/file.html, which would be sent to the Web server named www at the dom.com domain. It would look up the file named file.html in its directory and send it back to the browser.

That approach worked fine, and still does today. But this only covers static Web pages, ones whose content doesn’t change. Users want to get at lots more information today, not all of which has been embodied in static Web pages. Rather than require fancier browsers with more dynamic querying or other capabilities, Web servers became smarter and were able to talk to other programs that would generate HTML on the fly and send it back as the response to an incoming request. In the Java environment, this mechanism includes the Servlet and related classes.

As for requests coming from a browser, they come in two flavors—GET and POST. The GET request is a request via a URL. Simple URLs that appear as hyperlinks on a Web page are sent as GET requests. Any additional parameters appear at the end of the URL as name=value pairs separated by “&”. The parameters are separated from the URL with a “? character:

http://www.google.com/search?hl=en&ie=ISO-8859-1&q=java

The example URL includes three parameters:

  • hl=en

  • ie=ISO-8859-1

  • q=java

The POST is virtually the same, except that the name=value pairs don’t appear on the URL but are sent in a less visible way. The net result is the same, and the same methods can be used in the servlet to retrieve the parameters. The POST requests typically come from HTML form elements, as when you fill in the fields of a form and press a submit button (though forms can specify that the browser use GET as the submission mechanism for a particular form). The biggest advantage to posting the form is that the parameters don’t appear in the URL, which is both more aesthetically pleasing and avoids problems from accidentally revisited pages or user-altered parameters.

One further twist: URLs are not necessarily literal paths to files anymore. The Web server can interpret parts of the URL as an alias for some other program. So http://www.google.com/search may not actually refer to a directory named search on the Google site, but more likely tells the Web server to use its search program. We’ll discuss this more in Chapter 19.

So servlets are given requests which have come from browsers (and other Web clients), and then they respond with output. In our examples, we’ll be sending HTML back. There are lots of other choices, too. Since browsers understand other formats, a servlet might also send back plain text or even image data. Another choice gaining popularity is having the servlet generate XML and then using a conversion via stylesheets to produce HTML. This allows for the formatting to be changed (e.g., to apply a new corporate look to the pages) without changing the content or the programs that generate the content.

Since a Web server (e.g., Apache Tomcat) is typically configured to run constantly, that is, to always be around, then a servlet is also always around. (The Web server keeps a reference to the class, so the class is not garbage collected—hence its persistence.) Well, “always” here means “as long as the Web server and the operating system are up and running.”

An aside: Not all servlets are for Web browsing. Sometimes servlets can be used as daemons that hang around in the background doing other tasks (e.g., background processing of some database records). The browser interface, if any, may only be for the purpose of providing an administrative interface to the daemon. The administrator would then have a Web page to which to go, in order to see how many records have been processed. This page may also have buttons to reset, restart, or shut down the process. While we typically think of servlets being for the production of dynamic Web pages, here the Web pages would only be an aside to the real purpose, that of processing database records.

How to Write a Servlet

So how do you write a servlet? You may already have figured it out, from what we’ve described so far. You need to:

  • Write a Java class that extends HttpServlet

  • In that class, write the following methods:

    • init()

    • destroy()

    • doGet() and/or doPost()

That’s the basic idea. There are lots of details about what arguments are supplied, what other resources are available, what methods can be used to get at parameters, and so on. We’ll discuss some of those in our example servlet.

Let’s start with a simplistic servlet, one that will dynamically generate the “Hello, world” string as a Web page (Example 18.1).

Example 18.1. A “Hello, world” servlet

/*
 * HiServlet.java
 */

package net.multitool.servlet;

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

/**
 * Simple Servlet that generates a page of HTML
 */
public class
HiServlet
  extends HttpServlet
{
  /**
   * Think of this as the constructor for the servlet.
   * We need do nothing for our example,
   * but we should call our parent object.
   */
  public void
  init(ServletConfig config)
    throws ServletException
  {
    super.init(config);
  } // init

  /**
   * Called when the Web server is shutting down
   * or wants to shut down this particular servlet.
   * We need do nothing.
   */
  public void
  destroy()
  {
  } // destroy

  /**
   * Handles the HTTP GET method.
   * @param request servlet request
   * @param response servlet response
   */
   protected void
   doGet(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, java.io.IOException
   {
   doBoth(request, response);
   } // doGet

   /**
    * Handles the HTTP POST method.
    * @param request servlet request
    * @param response servlet response
    */
   protected void
   doPost(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, java.io.IOException
   {
     doBoth(request, response);
   } // doPost

   /**
    * Requests for both HTTP GET and POST methods come here,
    * because we're not doing anything different
    * between the two request types. This way we need only one
    * version of the code that does the real work.
    * @param request servlet request
    * @param response servlet response
    */
   protected void
   doBoth(HttpServletRequest request, HttpServletResponse response)
     throws ServletException, java.io.IOException
   {

     java.io.PrintWriter out = response.getWriter();
     response.setContentType("text/html");
     /* output our page of html */
     out.println("<html>");
     out.println("<head>");
     out.println("<title>A Java Servlet</title>");
     out.println("</head>");
     out.println("<body>");
     out.println("Hello, world.");
     out.println("</body>");
     out.println("</html>");

     out.close();
   } // doBoth
   /**
    * Returns a short description of the servlet.
    */
   public String
   getServletInfo()
   {

     return "Very Simple Servlet";
   } // getServletInfo()

} // class HiServlet

Whew! That is a lot of code for only a simple “Hello, world,” but remember that this is not just a run-on-your-desktop application. This is a network-based servlet that can respond to concurrent requests from across the network and talk to Web browsers. There’s a lot of plumbing that needs to be connected to a Web server for the servlet to run, and that’s what most of this code is—just the connections. The other verbose part is all of the HTML that we spit out around our message. You can make it even more elaborate, with background colors and other HTML decorations if you want to try it yourself.

Once you’ve written a servlet, though, you can’t just run it from the command line like any Java class. [1] Much of the work of a servlet is done behind the scenes by the Web server (e.g., Tomcat). The tougher question is, “How do you run a servlet?” That involves issues of configuring the Web server, setting up directory locations, and so forth. It’s the subject of the next chapter.

Once you’ve deployed this servlet (by reading the next chapter and/or with help from your IDE), you can run the servlet and talk to it via your browser. We’ve pointed a browser window at one such deployment to get a highly uninteresting Web page (Figure 18.2) whose HTML source (in your browser menu, select View> Page Source) is shown in Figure 18.3.

A very simple page from our servlet

Figure 18.2. A very simple page from our servlet

The servlet-generated source of our simple page

Figure 18.3. The servlet-generated source of our simple page

Input, Output

OK, so we’ve dynamically created a Web page—but the contents of that page don’t change. The real use for servlets comes from having them produce dynamic content, not just from dynamically producing content.

One way for the content to be dynamic is to extract it from a database. Using what we described in Chapter 15, you can add code to pull values from tables in a database. Consider a query that will return multiple rows of results. Each row could be displayed as a row in an HTML table for display on a Web page.

Using a loop, we can generate lots of HTML with little code. This is handy for generating HTML tables. We would likely generate the <table> tag outside a for loop, but the <tr> and <td> tags would be output from within the loop, one for each iteration of the loop. (If you’re not picturing that, be patient. There are examples of this coming up. If you’re not conversant in HTML, then you better check out some of the HTML references at the end of this chapter. We’re going to assume that you speak HTML fluently. Come on—we can’t cover everything in one book.)

The other side of dynamic content comes from variable input. Google’s search engine, for example, generates different pages for different search strings. It is the variation in user input that results in varying output pages. On a Web page, user input typically comes from an HTML form. The form values can be passed either as parameters on the URL or as POST values. URL parameters are also easy to generate by hand, or to code in place in <a> tags. For example,

<a href="/servlet/doSuch?cmd=find&value=joe">

is an HTML tag for a hyperlink which will invoke the doSuch servlet and pass in the parameters cmd and value. (It’s a servlet not because the pathname is /servlet, but we use that for illustrative purposes. In fact, the servlet invoked may not even be called doSuch; it all part of servlet mapping that recognizes certain URLs as aliases for particular servlets. See Chapter 19 for a fuller explanation.)

The point is, we can invoke the same servlet repeatedly (even simultaneously) but with different values for our parameters, so we can program it for different behaviors and different output.

These parameters are available to the servlet via the request argument of the doGet() and doPost() methods. You can get an enumerator over all of the arguments (using getParameterNames()), or if you know it’s name (and you likely would, since you’re writing the program) you can ask for a particular argument.

The previous example used an argument called cmd, whose value we could retrieve thus:

String act = request.getParameter("cmd");

The parameters all come as Strings. If your arguments are numeric, you’ll have to parse them (and error-check them—HTML forms are, understandably, weak on validating their input; tons of JavaScript have been written to deal with this, but this is beyond the scope of this book.)

Some parameters may have embedded spaces and other special characters that would disrupt a URL. To deal with that, browsers encode the characters in form fields before sending them to a Web server. You can see that in some URLs—space gets replaced with a “+” character, and special characters (such as the plus sign) get replaced with a character sequence for hexadecimal values (for example, “+ becomes %2B). The getParameter() method will automatically decode those. But we need to remember this if we want to generate any literal URLs in the HTML that we produce. (See the URLEncoder class in the Javadoc documentation for servlets.)

One more annoyance that must be dealt with: What if the URL contains the same argument twice—for example, www.google.com/search?cmd=search&cmd=bogus?

If you make the call to getParameter() you will get the first value (search). If you want to handle such a situation differently, you can call getParameterValues() which will return an array of Strings for all the different values. In our example,

String [] allofem = getParameterValues("cmd");

will return an array such that:

allofem[0] = "search"
allofem[1] = "bogus"

If there was only one value, then you get an array of one element. If the parameter wasn’t used in the URL, getParameterValues() returns null.

Matters of State: Cookies, Hidden Variables, and the Dreaded “Back” Button

The toughest part about working with HTML is, perhaps, its statelessness. HTML and browsers were not designed to keep a connection going. It’s not a phone call type of connection, where the line is kept open between the browser and the Web server. Rather, it’s a one-shot, send-me-what-you’ve-got mechanism more like postal mail (but without the stamp). Here’s the rub: Just because you mail a letter, you can’t assume that you’ll get an answer back. There is no on-going connection between browser and server, except for the duration of the data transfer. Once you’ve got your complete page displayed, the connection is gone. [2] About the best one can hope for is that you’ll use what, in our postal analogy, would be like a supplied reply envelope. This allows the servlet engine of the Web server to track requests from the same user and provide a session capability across requests. It will use your browsers cookie mechanism to store this session’s ID used to track your session. If you don’t have sessions on, it will need to use URL rewriting, whereby the URLs generated will have an added parameter, the session ID.

Unlike the early days in the life of the Web, nowadays virtually everyone has cookies enabled in their browsers—anyone who shops at amazon.com, at least. This makes session tracking so much easier for the servlet developer. The Web server handles all that automatically, and you only need to make a few calls to the session-related methods of the HttpRequest.

To get a session for a user, ask for one from the HttpRequest:

HttpSession session = request.getSession(true);

The boolean parameter says whether (true) or not to create a session if one does not yet exist for this user. Once you have a session, you can store objects associated with that session:

session.setAttribute("cart", shopCart);

where shopCart is any serializable Object and "cart" could be any String that you want to use to later identify and retrieve this object, for example:

Basket myCart = (Basket) session.getAttribute("cart");

Notice that we need to explicitly cast the object type returned by getAttribute(), because it returns a generic Object.

Cookies

For any information that you want to save for longer than the duration of a session, you may want to investigate cookies—little bits of data (4K max; typically only a few bytes) sent to the browser for it to store and send back at a later time. You make a cookie thus:

Cookie snack = new Cookie("name", "value");
snack.setMaxAge(36000); // lifetime in seconds (10 hours)

Setting the maximum age of the cookie to a positive value is needed to let the browser know that it needs to store the cookie on disk. After that many seconds the cookie will be discarded by the browser as no longer relevant. Notice, too, that you must send the data inside the cookie as a string, and when you retrieve it, you’ll have to parse that string.

Then you can send the cookie as part of a response, along with your other output:

response.addCookie(snack);

Getting data back via cookies involves requesting data from the HttpServletRequest object. All the cookies associated with your URL are sent with the HTTP header to this address. You make the call:

Cookies [] allSuch = request.getCookies();

and then you have to look through the list looking for the cookie you want:

if (allSuch != null) {
    for(i=0; i allSuch.length; i++) {
        Cookie c1 = allSuch[i];
        if ("DesiredCookieName".equals(c1.getName())) {
           String result = c1.getValue();
           // ... now do something with it
        } // endif
    } // next cookie
} // endif

While cookies have gotten a lot of press, especially in the early days of Web technology, we’ve found much less use for them than for session objects. Session objects stay on the server, cannot be modified or deleted by the user, and are easier to look up and use. The drawback, or course, is their limited lifespan. But if you really want to leave data around for the next time some user visits your servlet, you may be better off putting the data in your own database and identifying that user by means of a cookie or by some login mechanism.

Let’s take a look at a complete servlet example.

Designing a BudgetPro Servlet

When designing a servlet, there are many different patterns to follow. We can’t hope to cover all the approaches that can be used for effective servlet programming. What we hope to do is show you our previous BudgetPro GUI application rewritten as a servlet, so that you can see the mechanics of a working servlet application. From this, you can become accustomed to the mechanics of a servlet so that you’ll feel comfortable with other approaches, too. All servlets need to use these basic mechanisms.

Our BudgetPro GUI application was started from the command line, with a name for the budget and a total dollar amount. We’ll use a static HTML page with a form for supplying that information. That will invoke our servlet. The servlet will use HTML pages analogous to the windows we used in our GUI—there will be a main screen that shows the current account listing its subaccounts, and there will also be a screen for creating new subaccounts.

One nice feature of HTML-based Web applications is that you can use hyperlinks as a way to both select something and take an action on it. We’ll use that feature in lieu of a View Subaccount button. Instead of selecting a subaccount and then pressing View Subaccount, the user will only have to click on the name of the subaccount. As a hyperlink, it will make the request to the servlet to view that subaccount.

We will still use a button to send us to the screen for creating the subaccounts. We could have used a hyperlink, but this makes the browser page look a bit more like the GUI version.

Prototype

When designing servlets, it’s handy to use static HTML pages as a prototype for the work to be done. You can mock up the various screens using HTML, simulate interactions by using hyperlinks to move between the screens, and get a feel for what the screens and interactions will look like.

Such a prototype also serves as a “runnable” specification. It can sometimes be easier to show the action than to describe it with words. And if you take care when you are building these static HTML pages, most of the HTML can be reused directly in the final product. (This will be even more true when we get to JSP.)

Design

Let’s review what we need our servlet application to do for us. Given an account name and the initial dollar amount, we need to:

  • Create a top-level account with that amount of dollars

  • Display the current account and its total and remaining dollars, along with a list of its subaccounts, if any

  • Create subaccounts, specifying a name and dollar amount

  • Make a selected subaccount be the current one, displayed as above

After each or any of these actions, the servlet has to spit out the HTML page for the user to view. If the user wants to create a subaccount, then the servlet produces a form page for entering the name and dollar amount for the subaccount. When the user presses a Create button on that page, the browser tells the servlet (via the form data) that the servlet should create the subaccount and redisplay the current account with this new subaccount added to its list.

Tip

It may help to think of the servlet as a two-step process, with a current and future perspective. The first step is the action that the servlet must perform based on the supplied parameters (e.g., create a new account). The second step is the creation of the page allowing the user to take the next (future) action. That page reflects the state of things after the parameter-driven action has occurred. In our example, that means showing the list of subaccounts including the one that we just created.

Let’s spell out in more detail what our interactions with the servlet will be, and describe what output we expect for each of those inputs. We will create a keyword to tell the servlet what function we want it to perform; we’ll call the parameter func. We will sometimes need two other parameters: name and dollars.

Table 18.1 shows our design as a compact reference.

Table 18.1. BudgetPro servlet actions

func parameter

Other params

Action

Next screen

begin

name, dollars

Create a top-level account, save in the session.

main

mkacct

none

none

subacct

cancel

none

Get account from session.

main

create

name, dollars

Get account from session; create subaccount.

main

cd

name

Get account from session, look up subaccount by name, save as current in session.

main

back

none

Get account from session, get parent from account, save as current in session.

main

The code for our servlet is at http://www.javalinuxbook.com/. Let’s look at some of the key parts of the servlet in more detail. We’ll look at: 1) reading the parameters, 2) the core business logic of the servlet, as described in Table 18.1, and 3) how we create and output the HTML.

The parsing of the parameters is very straightforward. The request parameter, part of the signature of the doGet() and doPost() methods, can be used to retrieve the parameters we need:

String act = request.getParameter("func");
String name = request.getParameter("name");
String dollars = request.getParameter("dollars");

Notice that we always ask for all three parameters, even though we will often use only one (act). Once we have the requested function in act, it’s just a matter of if-then-else-ing our way through the possible values and taking the appropriate actions. We store, or retrieve, the current account in the session manager, thereby providing continuity between browser requests (Example 18.2).

The output is the page to send back to the browser. We create that page as an object, either an AccountView or a SubPage. The HttpServletResponse provides us with an output channel on which to write.

java.io.PrintWriter out = response.getWriter();
if (nextPage != null) {
    response.setContentType("text/html");
    out.println(nextPage.toString());
}

Example 18.2. Implementing the BudgetPro servlet actions

if ("begin".equals(act)) {
    Account top = new Account(name, theUser, dollars);
    session.setAttribute("top", top);
    session.setAttribute("current", top);
    nextPage = new AccountView(top);
} else if ("mkacct".equals(act)) {
    // show the subaccount creation page
    nextPage = new SubPage(null);
} else if ("cancel".equals(act)) {
    Account current = (Account) session.getAttribute("current");
    nextPage = new AccountView(current);
} else if ("create".equals(act)) {
    Account current = (Account) session.getAttribute("current");
    try {
        current.createSub(name, dollars);
        nextPage = new AccountView(current);
    } catch (NumberFormatException nfe) {
        // show the subaccount creation page (with error message)
        nextPage = new SubPage("Bad number format");
    }
} else if ("cd".equals(act)) {
    Account current = (Account) session.getAttribute("current");
    Account nextAcct = current.getSub(name);
    session.setAttribute("current", nextAcct);
    nextPage = new AccountView(nextAcct);
} else if ("back".equals(act)) {
    Account current = (Account) session.getAttribute("current");
    Account nextAcct = current.getParent();
    session.setAttribute("current", nextAcct);
    nextPage = new AccountView(nextAcct);
} else {
    log("Unknown func=["+act+"]");
    response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
}

The way that we construct the output, it will all get sent back to the user in one fell swoop. That’s fine for relatively short pages with rapid response time. If response time is a major concern and you are sending large quantities of data, you may want to change things a bit. Instead of building up the output in a StringBuffer and then getting it all back with a toString() call, you could take each of our append() calls and make them individual out.println() calls, to send each snippet of HTML separately. The output can be flushed explicitly, too, using

response.flushBuffer();

You might do such a call just before beginning a database operation, or place such calls at strategic points through your output.

Review

We have seen that servlets are Java programs that are run by a Web server. They typically, but not necessarily, produce output intended for a browser. By implementing the HttpServlet interface, your Java class will have all the methods needed for it to be run by a Web server. We looked at a simple example and saw its output to a Web browser, then we looked at another example using our BudgetPro application.

What You Still Don’t Know

There is more that we haven’t discussed, so if you’re going to do some serious work with servlets, be sure to do some additional reading, especially on these topics:

  • The servlet lifecycle and the need for thread safety.

  • How to keep the servlet output from being cached.

  • Dealing with failures.

  • Initialization parameters.

  • Other kinds of output.

  • Sharing between servlets.

  • How to configure and deploy servlets (this is coming up in the next chapter).

Resources

The definitive place for all the details is the Java Web site at Sun, [3] particularly the pages dealing with javax.servlet.http classes.

Some of the best material on servlets comes from:

  • Core Servlets and JavaServer Pages by Marty Hall and Larry Brown, ISBN 0-13-009229-0, a Prentice Hall PTR book.

  • Its sequel, More Servlets and JavaServer Pages by Marty Hall, ISBN 0-13-067614-1, also by Prentice Hall PTR.

  • Java Servlet Programming, Second Edition by Jason Hunter and William Crawford, ISBN 0596000405, from O’Reilly.

Exercises

1.

Modify the BudgetPro servlet so that it responds differently for the doGet() and doPost() methods. Have doPost() continue to work as is, but have doGet() report the number of different users and the number of accounts that they have created. (You may need to “instrument” the code—that is, add additional statements—to start counting such things.)

2.

Change BudgetPro to do its output on the fly instead of building the entire page before output. Can you notice any difference in the display time?

3.

Design error handling for BudgetPro to prevent the user from allocating more than is available in the (sub)account. Will you use Java exceptions? If so, which object will throw them and which will catch them? How will you inform the user of the error? Implement your design.



[1] Well, actually, you could if it had a main() method defined. Our example doesn’t, but a servlet class is still a Java class, and you might define a public static void main() method that would allow you to run it from the command line as a way to drive the rest of the class for simple testing. Of course, such a simple test harness wouldn’t be driving a Web browser, and so on but technically it is possible. We didn’t want to lie to you.

[2] You can go to another page, just be staring at the page for a long long time, or you might have shut down your browser completely—and the server-side servlet will never know.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.96.5