B.1. Servlet and JavaServer Pages Overview
Java servlet technology is a means by which to execute Java programs efficiently in a web environment. The Java Servlet Specification defines the conventions of this environment, which may be summarized briefly as follows:
- Servlets run inside a servlet container, which itself either runs inside a web server or communicates with one. Servlet containers are also known as servlet engines.
- The servlet container receives requests from the web server and executes the appropriate servlet to process the request. The container then receives the response from the servlet and gives it to the web server, which in turn returns it to the client. A servlet container thus provides the connection between servlets and the web server under which they run. The container acts as the servlet runtime environment, with responsibilities that include determining the mapping between client requests and the servlets that handle them, as well as loading, executing, and unloading servlets as necessary.
- Servlets communicate with their container according to established conventions. Each servlet is expected to implement methods with well-known names to be called in response to various kinds of requests. For example, GET and POST requests are routed to methods named doGet( ) and doPost( ).
- Servlets that can be run by a container are arranged into logical groupings called "contexts." (Contexts might correspond, for example, to subdirectories of the document tree that is managed by the container.) Contexts also can include resources other than servlets, such as HTML pages, images, or configuration files.
- A context provides the basis for an "application," that is, a group of related servlets that work together, without interference from other unrelated servlets. Servlets within a given application context can share information with each other, but servlets in different contexts cannot. For example, a gateway or login servlet might establish a user's credentials, which then are placed into the context environment to be shared with other servlets within the same context as proof that the user has logged in properly. Should those servlets find the proper credentials not to be present in the environment when they execute, they can redirect to the gateway servlet automatically to require the user to log in. Servlets in another context cannot gain access to these credentials. Contexts thus provide a measure of security by preventing one application from invading another. They also can insulate applications from the effects of another application crashing; the container can keep the non-crashed applications running while it restarts the one that failed.
- Sharing of information between servlets may take place at several scope levels, which allows them to work together within the scope of a single request or across multiple requests.
The following listing shows what a simple servlet looks like. It's a Java program that implements a SimpleServlet class. The class has a doGet( ) method to be invoked by the servlet container when it receives a GET request for the servlet. It also has a doPost( ) method to handle the possibility that a POST request may be received instead; it's simply a wrapper that invokes doGet( ). SimpleServlet produces a short HTML page that includes some static text that is the same each time the servlet runs, and two dynamic elements (the current date and client IP address) that vary over time and for each client:
import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class SimpleServlet extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter out = response.getWriter ( ); response.setContentType ("text/html"); out.println ("
"); out.println (""); out.println ("Simple Servlet"); out.println (""); out.println ("
"); out.println ("
Hello.
"); out.println ("
The current date is " + new Date ( ) + ".
"); out.println ("
Your IP address is " + request.getRemoteAddr ( ) + ".
"); out.println ("
"); out.println (""); } public void doPost (HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { doGet (request, response); } }
As you will no doubt observe, this "simple" servlet really isn't so simple! It requires a fair amount of machinery to import the requisite classes and to establish the doGet( ) and doPost( ) methods that provide the standard interface to the servlet container. Compare the servlet to the following PHP script, which does the same thing in a much more concise fashion:
Simple PHP Page
Hello.
The current date is .
Your IP address is .
The contrast between the Java servlet and the PHP script illustrates one of the problems with writing servletsthe amount of repetitious overhead:
- A certain minimal set of classes must be imported into each servlet.
- The framework for setting up the servlet class and the doGet( ) or doPost( ) methods is fairly stereotypical, often varying among servlets only in the servlet class name.
- Each fragment of HTML is produced with an output statement.
The first two points can be addressed by using a prototype file that you copy when beginning a new servlet. The third point (wrapping each line of HTML within a print statement) is not so easily addressed and is possibly the single most tedious aspect of servlet writing. It also leads to another issue: a servlet's code may be easy enough to read as Java, but it's sometimes difficult to discern the structure of the HTML that the code generates. The problem is that you're really trying to write in two languages at once (i.e., you're writing Java that writes HTML), which isn't really optimal for either language.
B.1.1 JSP PagesAn Alternative to Servlets
One of the reasons for the invention of JavaServer Pages was to relieve the burden involved in creating web pages by means of lots of print statements. JSP uses a notational approach that is similar to PHP: HTML is written literally without being wrapped in print statements, and code to be executed is embedded in the page within special markers. The following listing shows a JSP page that is equivalent to the SimpleServlet servlet, but looks much more like the corresponding PHP script:
Simple JSP Page
Hello.
The current date is <%= new java.util.Date ( ) %>.
Your IP address is <%= request.getRemoteAddr ( ) %>.
The JSP page is more concise than the servlet in several ways:
- The standard set of classes required to run a servlet need not be imported.
- The HTML is written more naturally, without using print statements.
- No class definition is required, nor are any doGet( ) or doPost( ) methods.
- The response and out objects need not be declared, because they're set up for you and ready to use as implicit objects. In fact, the JSP page just shown doesn't refer to out explicitly at all, because its output-producing constructs write to out automatically.
- The default content type is text/html; there's no need to specify it explicitly.
- The script includes literal Java by placing it within special markers. The page just shown uses <%= and %>, which mean "evaluate the expression and produce its result." There are other markers as well, each of which has a specific purpose. (For a brief summary, see "Elements of JSP Pages" later in this appendix.)
When a servlet container receives a request for a JSP page, it treats the page as a template containing literal text plus executable code embedded within special markers. The container produces an output page from the template to send to the client. Literal text from the template is left unmodified, the executable code is replaced by any output that it generates, and the combined result is returned to the client as the response to the request. That's the conceptual view of JSP processing, at least. What really happens when a container processes a JSP request is as follows:
- The JSP page is translated into a servletthat is, into an equivalent Java program. Instances of template text are converted to print statements that output the text literally. Instances of code are placed into the program so that they execute with the intended effect. This is all placed within a wrapper that provides a unique class name and that includes import statements to pull in the standard set of classes necessary for the servlet to run properly in a web environment.
- The container compiles the servlet to produce an executable class file.
- The container executes the class file to generate an output page, which is returned to the client as the response to the request.
- The container also caches the executable class so that when the next request for the JSP page arrives, the container can execute the class directly and skip the translation and compilation phases. If the container notices that a JSP page has been modified the next time it is requested, it discards the cached class and recompiles the modified page into a new executable class.
Notationally, JSP pages provide a more natural way to write web pages than do servlets. Operationally, the JSP engine provides the benefits of automatic compilation after the page is installed in the document tree or modified thereafter. When you write a servlet, any changes require recompiling the servlet, unloading the old one, and loading the new one. That can lead to an emphasis on messing with the servlet itself rather than a focus on the servlet's purpose. JSP reverses the emphasis so that you think more about what the page does than about the mechanics of getting it compiled and loaded properly.
The differences between servlets and JSP pages do not imply any necessity of choosing to use only one or the other. Application contexts in a servlet container can include both, and because JSP pages are converted into servlets anyway, they can all intercommunicate.
JSP is similar enough to certain other technologies that it can provide a migration path away from them. For example, the JSP approach is much like that used in Microsoft's Active Server Pages (ASP). However, JSP is vendor and platform neutral, whereas ASP is proprietary. JSP thus provides an attractive alternative technology for anyone looking to move away from ASP.
B.1.2 Custom Actions and Tag Libraries
A servlet looks a lot like a Java program, because that's what it is. The JSP approach encourages a cleaner separation of HTML (presentation) and code, because you need not generate HTML from within Java print statements. On the other hand, JSP doesn't require separation of HTML and code, so it's still possible to end up with lots of embedded Java in a page if you're not careful.
One way to avoid inclusion of literal Java in JSP pages is to use another JSP feature known as custom actions. These take the form of special tags that look a lot like HTML tags (because they are written as XML elements). Custom actions allow tags to be defined that perform tasks on behalf of the page in which they occur. For example, a tag might communicate with a database server to issue a query. Custom actions typically come in groups, which are known as tag libraries and are designed as follows:
- The actions performed by the tags are implemented by a set of classes. These are just regular Java classes, except that they are written according to a set of interface conventions that allow the servlet container to communicate with them in a standard way. (The conventions define how tag attributes and body content are passed to tag handler classes, for example.) Typically, the set of classes is packaged into a JAR file.
- The library includes a Tag Library Descriptor (TLD) file that specifies which tags are associated with which classes. This allows the JSP processor to determine which class to invoke for each custom tag that appears in a JSP page. The TLD file also indicates how each tag behaves, such as whether it has any required attributes. This information is used at page translation time to determine whether a JSP page uses the tags in the library correctly. For example, if a tag requires a particular attribute and the tag is used in a page without it, the JSP processor can detect that problem and issue an appropriate error message.
Tag libraries make it easier to write entire pages using tag notation rather than switching between tags and Java code. The notation is JSP-like, not Java-like, but the effect of placing a custom tag in a JSP page is like making a method call. This is because a tag reference in a JSP page maps onto a method invocation in the servlet that the page is translated into.
To illustrate the difference between the embedded-Java and tag library approaches, compare two JSP scripts that set up a connection to a MySQL server and display a list of tables in the cookbook database. The first one does so using Java embedded within the page:
<%@ page import="java.sql.*" %>
Tables in cookbook Database
Tables in cookbook database:
<% Connection conn = null; String url = "jdbc:mysql://localhost/cookbook"; String user = "cbuser"; String password = "cbpass"; Class.forName ("com.mysql.jdbc.Driver").newInstance ( ); conn = DriverManager.getConnection (url, user, password); Statement s = conn.createStatement ( ); s.executeQuery ("SHOW TABLES"); ResultSet rs = s.getResultSet ( ); while (rs.next ( )) out.println (rs.getString (1) + "
"); rs.close ( ); s.close ( ); conn.close ( ); %>
The same thing can be done using a tag library, such as the JSP Standard Tag Library (JSTL). JSTL consists of several tag sets grouped by function. Using its core and database tags, the preceding JSP page can be converted as follows to avoid entirely the use of literal Java:
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jstl/sql" prefix="sql" %>
Tables in cookbook Database
Tables in cookbook database:
SHOW TABLES
The taglib directives identify the TLD files that the page uses and indicate that actions from the corresponding tag sets will be identified by prefixes of c and sql. (In effect, a prefix sets up a namespace for a set of tags.) The tag sets up the parameters for connecting to the MySQL server, issues a query, loops through the result, and adds each table name in the result to the output page. (I'm glossing over details, of course; the JSTL tags are described further in Recipe 16.4.)
If it's likely that you'd connect to the database server the same way from most JSP pages in your application context, a further simplification can be achieved by moving the tag to an include file. If you name the file jstl-mcb-setup.inc and place it in the application's WEB-INF directory,[B] any page within the application context can set up the connection to the MySQL server by accessing the file with an include directive. Modifying the preceding page to use the include file results in a script that looks like this:
[B] By convention, application contexts use their WEB-INF directory for private context-specific information. See Recipe B.3.
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jstl/sql" prefix="sql" %> <%@ include file="/WEB-INF/jstl-mcb-setup.inc" %>
Tables in cookbook Database
Tables in cookbook database:
SHOW TABLES
You're still using Java when you use a tag library, because tag actions map onto Java class invocations. But the notation follows XML conventions, so it's less like writing program code and more like writing HTML page elements. If your organization produces web content using a "separation of powers" workflow, custom actions allow elements of the page that are produced dynamically to be packaged in a way that is easier for designers and other non-programmers to deal with. They don't have to develop or work directly with the classes that implement tag actions; that's left to the programmers that write the classes that correspond to the tags.