Chapter 4. JavaServer Pages

In Chapter 3, we looked at Java servlets, J2EE’s primary mechanism for communicating with web browsers. Servlets are a great technology, but they don’t solve every problem faced by web developers, and they introduce a few issues of their own. One major problem is that developing complex HTML-based user interfaces with servlets is time-consuming. Embedding strings of println() statements is tedious and error-prone and requires that pages be assembled by a fully qualified programmer who, just perhaps, should be off doing other things. Configuration is also a problem, although it has been simplified a great deal since the original Servlet API. Adding a new servlet to a web application involves editing the deployment XML file and typically reloading the application or restarting the server.[18] Changes to a servlet create the same issues, turning rapid prototyping into regular prototyping.

On the design side, servlets can also blend application logic with presentation logic, undoing one of the primary benefits of the client/server architectures that they often replace. In a canonical J2EE environment, where business logic is abstracted into Enterprise JavaBeans or other middleware, this is not much of a concern. But in real life, full J2EE systems aren’t always appropriate. Many applications don’t need the full weight of an application server, and often developers lack the interest or time to create a full J2EE implementation. And even within a J2EE application, there are substantial benefits to separating flow control (accomplished by servlets) from actual pages.

One obvious approach is to use static HTML content for most pages, reserving servlets for forms processing with a redirect or forward to another HTML page upon completion. Such an approach breaks down fairly quickly, as most pages in a web application have at least some dynamic element. A better solution, however, is to embed dynamic elements directly into the static content. This is the reverse of the approach servlets use, where static content is embedded within the dynamic framework.[19] Static web pages with a few dynamic content extensions can be edited using any HTML authoring tool and previewed live in the browser, either with or without the dynamic elements (obviously, the efficacy of this last technique depends on the complexity of the page).

There have been a few different approaches to creating dynamic web pages without a full servlet. Generally, these involve inserting either custom tags or control statements within HTML document templates and then running the documents through a server-side processor that replaces tags and directives with dynamically generated content. The resulting pure HTML is delivered to the client. This type of templating system is shown in Figure 4-1. In a servlet environment, the template processor will be implemented as one or more servlets. Examples of templating engines include WebMacro (http://www.webmacro.org) and Velocity (http://jakarta.apache.org/velocity).

Generic templating engines

Figure 4-1. Generic templating engines

Sun’s templating solution is called JavaServer Pages, or JSP, which is currently at Revision 2.0. JSP is a highly integrated dynamic content system that is built on top of the Java Servlet API. In fact, JSPs are compiled into Java servlets before being executed by the web server. This allows JSP developers to obtain all the advantages of Java servlets (efficient lifecycles, integration with Java middleware, and so on) without spending hours integrating HTML into actual Java code. JSP also allows page developers to access properties of Java beans without writing code. Finally, custom JSP tags can be created by developers for use by page designers, removing Java code from the JSPs entirely.

Along with the Servlet API, the JSP API is part of the J2EE specification. J2EE 1.4 incorporates JSP 2.0, which was released in 2003. Earlier versions of J2EE supported JSP 1.2. We will indicate changes when possible, but this chapter focuses on the 2.0 version of the standard.

JSP is also closely integrated with the JavaServer Faces and Struts application frameworks, which are described in Chapters 5 and 19, respectively. In both systems, JSPs provide the user interface, collaborating with extensions provided by the application framework. Other parts of this book that you may find especially relevant include Chapter 2, where we provide details on assembling and deploying web components, and Chapter 21, where we discuss code annotation systems that can be used to automatically generate deployment descriptors and other artifacts for web applications.

In this chapter, we are going to look at JSP from a Java programmer’s perspective as opposed to that of a web site designer. Of course, one of the great things about JSPs, and other templating technologies, is that you don’t have to be a programmer to use them. However, for a more thorough introduction to JSP, as well as some substantial additional detail for experienced developers, we suggest JavaServer Pages by Hans Bergsten (O’Reilly).

JSP Basics

The JSP specification is part of the full J2EE specification, and JSP engines are included with most major servlet engines. The Tomcat 5.5 server from the Apache project is the reference implementation for both the Servlet 2.4 and JSP 2.0 specifications, and is freely available. For information on downloading and installing Tomcat, see http://jakarta.apache.org/tomcat. The rest of this chapter assumes some familiarity with the concept of a web application and servlets. Chapter 3 covers servlets and web applications in more detail. For now, it is enough to know that a servlet is a compiled Java class that responds to a web request, and a web application is a collection of servlets and other resources.

Since the JSP architecture is based on the servlet architecture, JSP support within a web server provides a translation layer. Individual JSPs are text files stored on the web server and accessed via their real path. For example, if a JSP named index.jsp resides at the root of the “enterprise” web application, it would be accessed by a request to http://localhost:8080/enterprise/index.jsp, assuming that the web server is running on port 8080 of localhost. When the JSP is first requested, the JSP engine uses the JSP file to generate the source code for a servlet. The generated source is then compiled, installed into the servlet engine, and used to service the request. Once the JSP has been compiled, the compiled version is saved and used to service additional requests according to the standard servlet lifecycle. Figure 4-2 shows the process in more detail.

When the JSP file is modified, the server detects the change and rebuilds the corresponding servlet. The compilation phase imposes a slight delay the first time the page is retrieved. If necessary, many servers allow precompilation of JSPs to get around this problem.

Now is a good time to look at a simple JSP, so here’s an example that displays the current date:

<html>
<body>
 Hello, visitor. It is now <%= new java.util.Date().toString() %>
</body>
</html>
JSP lifecycle

Figure 4-2. JSP lifecycle

This looks easy enough. The only part of the example that is not regular HTML is the % tag. JSP elements come in two forms: the simple <% %> entity, used for directly embedding Java code and issuing JSP specific directives, and XML style “action tags,” which we’ll see a little later. The <%= %> tag is an expression tag, which inserts the value of a single Java expression into the page’s output. For space reasons, we’re not going to include the Java code produced by the system, but if you want to look at it, you can find it in the web server’s working areas. For Tomcat 5, the path is /<tomcat_path>/work/Catalina/<servername>/<webappname>. Figure 4-3 shows the output sent to the client browser.

Output from date display JSP

Figure 4-3. Output from date display JSP

Now, let’s try something a little more complicated. The <% %> element can insert regular Java code, which can control the flow of the page. The following example checks whether it’s before or after noon (we’ll count nighttime as the afternoon for the purpose of this sample) and displays an appropriate greeting:

<html>
<body>
<%
java.util.Date theDate = new java.util.Date();
%>
<% if (theDate.getHours() < 12) { %>
Good morning,
<% } else { %>
Good afternoon,
<% } %>
 visitor. It is now <%= theDate.toString() %>
</body>
</html>

Two things should be noted about this example. The first is the declaration of a local variable in the first pair of <% %> tags. Unlike the %= tags, nothing is displayed. The next JSP element includes a Java if statement that operates on the variable. Note that we don’t finish the if statement in the JSP tag, but return to template text. This text will be displayed if the if clause evaluates to true. The next JSP element closes the if and begins an else. It is followed by the text to be displayed in the else case, and a final JSP element to close the else. The rest of the file is the same as in the first example, except we use the Date object created earlier rather than instantiating a new one.

When viewed in the afternoon, the JSP-generated servlet produces HTML that looks like this:

<html>
<body>
 
Good afternoon,
 
 visitor. It is now Mon Dec 24 14:50:39 EST 2007
</body>
</html>

Note that there is some whitespace where some of the JSP tags were. This is because we placed whitespace around them in the source JSP file. The JSP processor removes the tags themselves, but has no way of determining whether it should also remove the carriage returns that follow them. This is not a problem for HTML, since the web browser compresses all whitespace characters into a single space character, but you should be aware of this behavior when debugging or producing plain-text output.

Directives and Declarations

JSP also allows the insertion of JSP directives , which control behavior for the entire page. Directive tags begin with <%@ rather than <%. There are three directives. The first, include, simply includes a static file into the page output. So to include a copyright notice at the bottom of each JSP, you can just add the tag:

<%@ include file="copyright.html"%>

The second directive, page, is more flexible. It defines parameters that relate to the current JSP and is valid across all invocations of that page. Table 4-1 displays the possible attributes of the page directive.

Table 4-1. JSP page directive values

Name

Description

autoFlush

Set to true or false, identifying whether the page buffer should be flushed when full. The default is true.

buffer

Set the buffer size, in kilobytes. The default is 8kb, and values must be in the format of number kb or none.

contentType

Set the MIME type or MIME type and charset. The default is text/html.

errorPage

An optional path to a custom error-handling page.

extends

An optional fully qualified class name of a class implementing javax.servlet.jsp.JspPage or javax.servlet.jsp.HttpJspPage.

import

A comma-separated list of Java classes or packages to import.

info

Text describing this JSP. The text entered here can be used by a server administration tool.

isErrorPage

Identifies this page as an error page, making the exception object (see next section) available to script elements.

isThreadSafe

Set to true or false, indicating whether this JSP should implement the servlet SingleThreadModel.

language

The scripting language for this page. The default is java, and the JSP specification doesn’t require support for any other language, although some application servers support JavaScript.

session

Set to true or false depending on whether the page should participate in user sessions. The default is true.

The final directive, taglib, is discussed later in this chapter.

JSP supports another tag sequence that globally affects all instances of a page. This is the declaration element, which begins with <%! and declares a global variable within the JSP. Unlike variables declared in a standard <% %> tag pair, variables declared with <%! will be available for all invocations of the JSP. For example:

<%! int globalHitCounts = 0; %>
This page has been accessed <%= ++globalHitCounts %> times.

However, use of declarations introduces thread safety and lifecycle issues. For example, there is no guarantee that the JSP will not be rebuilt and reloaded. If this happens, the hit count will return to 0. Therefore, for most applications, you should use other approaches for maintaining global data.

Built-in Objects

In addition to objects declared within a JSP file itself, Java code running within a JSP has access to a set of classes provided by the web server. These classes allow for communication between JSPs, interaction with the JSP container, and support for sessions and elaborate output. In servlets, for instance, request parameters are retrieved from the browser via the getParameter() method of an HttpServletRequest object. JSPs can do the same thing. The following JSP fragment uses the built-in request object (which maps to HttpServletRequest) and the out object (which provides a PrintWriter) to echo the form parameters submitted by the client):

<ul>
<%
 java.util.Enumeration e = request.getParameterNames();
 while(e.hasMoreElements()) {
    String name = (String)e.nextElement();
    out.println("<li>" + name + ":" +
    request.getParameter(name));
 }
%>
</li>

Table 4-2 lists the objects available to a JSP.

Table 4-2. Objects available in JSPs

Name

Type

Description

application

javax.servlet.ServletContext

The servlet context for this web application.

config

javax.servlet.ServletConfig

The ServletConfig object associated with this page.

exception

java.lang.Throwable

For error pages only, the exception that triggered the error.

out

javax.servlet.jsp.JspWriter

A PrintWriter subclass that writes to the page’s output stream.

page

java.lang.Object

The implementation class instance currently processing the request. When Java is the scripting language, page is synonymous to this.

pageContext

javax.servlet.jsp.PageContext

The page context for this JSP.

request

javax.servlet.ServletRequest or javax.servlet.http.HttpServletRequest

The protocol-specific request object for this request.

response

javax.servlet.ServletResponse or javax.servlet.http.HttpServletResponse

The protocol-specific response object for this request.

session

javax.servlet.http.HttpSession

For pages retrieved over HTTP, the current user’s HttpSession object.

Sharing Data Between JSPs, Other JSPs, and Servlets

HTTP is a stateless protocol, and by extension, the Web is inherently a stateless environment. If a client makes a series of connections to a web server, there is no built-in foolproof mechanism for the web server to associate the sequential requests with the same user. Similarly, a JSP or servlet will treat each request individually, regardless of whether the user has made other requests in the recent past.

The simplest form of communication between JSPs is a hyperlink. A JSP produces an HTML-formatted link, which the user can then click on to request another JSP. HTTP allows name=value parameters to be associated with an HTTP request. So, for example, if JSP 1 needs to inform JSP 2 of the user’s language, it might use a JSP fragment like this one to produce a link:

<a href="page2.jsp?language=<%= userLanguage %>">Go to page 2</a>

This HTML fragment includes a JSP expression that passes a variable called userLanguage as a request parameter to page2.jsp, which can then retrieve it from the session.

This is a clumsy approach, however, for all but the simplest applications. For one thing, as soon as the user accesses a page that doesn’t pass the language parameter on, such as a static HTML page or a link off-site, the information is gone and must somehow be retrieved again. A better place to store this information is the user’s session, accessible via the session object made available to all JSPs served over HTTP. For more on the session object, see Chapter 5. Information that is global to the entire application can be stored in the ServletContext object, which is accessible via the application object.

All information loaded into the session and application contexts will be available to any JSP that participates in the session or is part of the application. Servlets within the application will also be able to access the data. In the next section, we’ll look at some other ways of using these mechanisms to share data across JSPs.



[18] Dynamic class reloading and change detection solve some of these problems, but not for all servers and not always reliably.

[19] Since the dawn of civilization, humankind has sought to embed code within content. Great, if virtual, wars have been fought over the advisability of actually doing so. At this point, very few people consider it wise to embed all of their presentation HTML directly within a servlet, but there is wide divergence of opinion on how much code is appropriate for a JSP . In truth, the answer depends on your application. It’s safe to say that the larger or longer lived you expect your application to be, the more likely you will benefit from a clean separation of code and content.

Get Java Enterprise in a Nutshell, Third Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.