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).
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).
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>
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.
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.
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 |
buffer
| Set the buffer size, in kilobytes.
The default is |
contentType
| Set the MIME type or MIME type and
charset. The default is |
errorPage
| An optional path to a custom error-handling page. |
extends
| An optional fully qualified class
name of a class implementing |
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 |
language
| The scripting language for this
page. The default is |
session
| Set to |
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.
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 |
exception
|
java.lang.Throwable
| For error pages only, the exception that triggered the error. |
out
|
javax.servlet.jsp.JspWriter
| A |
page
|
java.lang.Object
| The implementation class instance
currently processing the request. When Java is the scripting
language, |
pageContext
|
javax.servlet.jsp.PageContext
| The page context for this JSP. |
request
| | The protocol-specific request object for this request. |
response
| | The protocol-specific response object for this request. |
session
|
javax.servlet.http.HttpSession
| For pages retrieved over HTTP, the
current user’s |
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.