< Zurück | Inhalt | Weiter >

Part IV


Developing Web Interfaces


Chapter 18


Servlets:

Java Pressed into Service


image

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.

image



18.1 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.



403


18.2 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 ad- dress 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?

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 re- quested 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)

image

18.3 Perspective 405

image


Linux system


Web server


Servlet class


HTTP request


HTML


PC


Web browser


image

Figure 18.1 Servlet diagram


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.


18.3 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.php, which would be sent to the Web server named www at the dom.com domain. It would look up the file named file.php 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 ca- pabilities, 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 pro- gram. So http://www.google.com/search may not actually refer to a direc- tory 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 un- derstand 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 collect- ed—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.


18.4 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).


image

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


image


Whew! That is a lot of code for only a simple “Hello, world,” but remem- ber 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, set- ting 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 unin- teresting Web page (Figure 18.2) whose HTML source (in your browser menu, select View > Page Source) is shown in Figure 18.3.



image

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.



image


Figure 18.2 A very simple page from our servlet


image

Figure 18.3 The servlet-generated source of our simple page


18.5 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 simultane- ously) 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 automat- ically 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.


18.6 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 mecha- nism more like postal mail (but without the stamp). Here’s the rub: Just be- cause 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 pro- vide a session capability across requests. It will use your browsers cookie mecha- nism 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.


 


18.6.1 Cookies

18.7.1 Prototype

18.7.2 Design