Enterprise Java Technologies Tech Tips
Tips, Techniques, and Sample Code
Welcome to the Enterprise Java Technologies Tech Tips for
June 26, 2003. Here you'll get tips on using enterprise Java
technologies and APIs, such as those in Java 2 Platform,
Enterprise Edition (J2EE).
This issue covers:
* Listing Web Archive Resources
* Servlet Life Cycle Listeners
These tips were developed using Java 2 SDK, Standard Edition,
v 1.4 and Java 2 SDK, Enterprise Edition, v 1.3.1 (Reference
Implementation).
This issue of the Tech Tips is written by Mark Johnson, president
of elucify technical communications (http://www.elucify.com/),
and co-author of Designing Enterprise Applications with the
Java 2, Enterprise Edition, 2nd Edition
(http://java.sun.com/blueprints/guidelines/
designing_enterprise_applications_2e/). Mark Johnson runs an open
forum for discussion of the tips at
http://groups.yahoo.com/group/techtipsarchive/.
You can view this issue of the Tech Tips on the Web at
http://java.sun.com/jdc/EJTechTips/2003/tt0626.html
See the Subscribe/Unsubscribe note at the end of this newsletter
to subscribe to Tech Tips that focus on technologies and products
in other Java platforms.
You can download the sample archive for these tips at
http://java.sun.com/jdc/EJTechTips/download/ttjun2003.ear.
The context root for the application is ttjun2003. The index.html
welcome file indicates how to use the sample code. Any use of
this code and/or information below is subject to the license
terms at http://developer.java.sun.com/berkeley_license.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LISTING WEB ARCHIVE RESOURCES
Occasionally, you may want to write a Web application component
that produces content, or changes its behavior, based on the
contents of the Web archive. For example, imagine you ship an
application to several different customers, each of whom requires
some custom functionality and content, and all of whom need
occasional updates and extensions. In addition, the application
might enable and disable various features depending on the absence
or presence (and contents) of various license files. Web
components in this type of application could decide how to behave
based on the contents of the WAR file.
The method javax.servlet.ServletContext.getResourcePaths allows
a Web component to query the container about the resources
contained in the Web archive. The method returns a java.util.Set
of String objects that provide a listing of all of the resources
in the Web archive in a particular directory. The single argument
is a String that gives a partial path which all of the returned
paths must match. The argument must itself start with a forward
slash ("/").
All of the strings returned by this method start with a forward
slash. Resources corresponding to WAR file directories also end
with a slash. Resources corresponding to files do not end with
a slash. If the path argument is "/", the method behavior
defaults to listing the contents of the root directory of the
archive. In some cases (as you will see in the example below),
the method reports Web resources that exist in the archive but
are not accessible by the component.
The Sample Code
The sample code for this tip, ListResourcesServlet, allows a user
to browse the contents of the tip sample code archive. The
servlet receives a get request from the browser. The request
contains a single parameter called "path" that indicates the path
within the archive to list. Each item in the requested directory
is presented to the user as a hyperlink to the named resource.
When the user clicks on a hyperlink to a WAR directory, the doGet
method lists the contents of that directory. Otherwise, the named
resource is a file, which the servlet tries to serve.
The doGet method of the servlet is simple -- all it does is
distinguish between directory and file resources, based on whether
or not the path ends in "/".
public void doGet(HttpServletRequest req,
HttpServletResponse res) throws IOException,
ServletException {
// The argument "path" is the path of the thing
// we want to display.
String path = req.getParameter("path");
if (path == null) {
throw new ServletException(
"ListResourcesServlet: parameter 'path'" +
" required");
}
// If its name ends in "/", list the resources
// in that directory.
// Otherwise serve the indicated file.
if (path.charAt(path.length()-1) == '/') {
listDirectory(res, path);
} else {
serveFile(req, res, path);
}
}
The serveFile method is straightforward: it simply forwards the
request to the path to the resource. The Web container then
either serves the resource, or generates an error message if the
resource is inaccessible.
protected void serveFile(ServletRequest req,
ServletResponse res,
String path)
throws IOException, ServletException {
ServletContext ctx =
_config.getServletContext();
RequestDispatcher rd =
ctx.getRequestDispatcher(path);
rd.forward(req, res);
}
The listDirectory method uses the getResourcePaths method to list
the contents of a WAR directory. The method first sets the
content type and gets a PrintWriter to the servlet output. Then
it prints a line for the ".." directory, which corresponds to the
directory above the requested directory.
// List the resources in the directory rooted
// in "path"
protected void listDirectory(ServletResponse res,
String path) throws IOException, ServletException {
// Get output writer
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println(
"
Listing of resources in directory " +
path + "
");
// If the path variable is empty, list "/".
if (path == null) {
path = "/";
}
// Create a link to ".."
listDirectoryLine(out, "..", dotdot(path));
The dotdot method creates the name of the directory above the
path. It does this by simply eliminating the last directory name
(delimited by slashes) from the path. The listDirectoryLine
method prints a string (its second argument) as a hyperlink to
a path (its third argument) to the servlet output writer. The
listDirectory method then uses the getResourcePaths method to list
the contents of the requested path.
// List directory contents as resource links
ServletContext ctx = _config.getServletContext();
Set files = ctx.getResourcePaths(path);
Iterator i = files.iterator();
while (i.hasNext()) {
String url = (String)i.next();
listDirectoryLine(out, url, url);
}
The ServletContext object for a servlet is accessed using the
getServletContext method of the ServletConfig object. (When a
servlet container initializes a new servlet, it passes a
ServletConfig object to the servlet's init method. A reference to
the ServletConfig can be stored in a field for use by subsequent
servlet method calls.) The method then calls the getResourcePaths
method to get all resources in the archive matching the given
path. Finally, the method iterates over the result set, printing
a line for each resource.
Sample Code Results
Follow the instructions in the section "Running the Sample Code"
to download and deploy the sample archive, and to access the
application. Click the link for Tip 1 on the displayed page to
browse the contents of the sample WAR file. This runs the
servlet, with the path equal to "/", and produces the following
output (all but the first line are hyperlinks to the corresponding
resources).
Listing of resources in directory /
..
/src/
/original.war
/META-INF/
/WEB-INF/
/index.html
/htmlsrc/
Notice that all but two of the resources ("/original.war" and
"/index.html") end in a slash. Remember that the end slash
indicates a directory. The entries that don't end in a slash are
files. If you click on the file "index.html", the servlet serves
the index.html file (which is the welcome file for the
application). By comparison, clicking on the entry "/WEB-INF/"
produces the following output:
Listing of resources in directory /WEB-INF/
..
/WEB-INF/web.xml
/WEB-INF/classes/
Clicking on the link for /WEB-INF/web.xml produces the following
unexpected result:
J2EE SDK/1.3.1 - HTTP Status 404 - /WEB-INF/web.xml
type Status report
message /WEB-INF/web.xml
description The requested resource (/WEB-INF/web.xml) is not
available.
If you look in the archive, you will see that the /WEB-INF/web.xml
file does indeed exist. So why does the server claim that the file
is not available? The answer is that all files under /WEB-INF (and
/META-INF) are protected from being served to Web clients. So,
although the getResourcePaths method reports all paths, not all
paths in a Web archive are accessible to Web components.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SERVLET LIFE CYCLE LISTENERS
The Java Servlet 2.3 specification
(http://jcp.org/aboutJava/communityprocess/first/jsr053/index.html)
provides mechanisms for notifying application code about events
in the Web application life cycle. Life cycle events include
creation and destruction of a servlet context or an HTTP session,
and creation, modification, or removal of servlet context
attributes and HTTP session attributes. Furthermore, any object
may be notified when it is bound to or unbound from an HTTP
session attribute.
Application life cycle listeners are often used to provide
consistent, managed access to other resources. For example,
consider a set of servlets within the same Web application that
share a database connection on a single server. To improve
performance, some servlets save a database connection as a
servlet context attribute, and reuse it across multiple servlet
service invocations for multiple clients. When the application is
shut down, the database connection should be closed. To manage
the database connection, an application developer creates a
servlet context event listener. The listener class opens the
database and initializes a context attribute with a reference when
the application is deployed. The listener class also closes the
database when the application is undeployed.
To create a life cycle listener, an application developer writes
classes that implement listener interfaces for life cycle events,
and the deployer configures these classes to be used by a Web
application. The class files for the listeners are packaged in
the Web archive, and configured using the deployment descriptor
web.xml. A listener class must have a public constructor with no
arguments. Each Web application can have multiple listeners of
each type.
The interfaces provided for listening to Web application life
cycle events are:
- javax.servlet.ServletContextListener. The methods in this
interface are called when a new servlet context is created or
just before the servlet context is destroyed.
- javax.servlet.http.HttpSessionListener. The methods in this
interface are called when an HTTP session is created or
destroyed. Whether the call to the method occurs before or
after the session is invalidated, depends on which
specification version (2.3 or 2.4) is being used. It's
worthwhile to verify the behavior of the platform
implementation you are using if your application behavior
depends on this detail.
- javax.servlet.ServletContextAttributeListener. The methods in
this interface are called when a servlet context attribute is
created, modified, or removed from a servlet context.
- javax.servlet.http.HttpSessionAttributeListener. The methods in
this interface are called when a session attribute is created,
modified, or removed from an HTTP session.
- javax.servlet.http.HttpSessionBindingListener. The methods in
this interface are called when an object is bound or unbound from
an HTTP session attribute.
Another interface, HttpSessionActivationListener, allows objects
bound to session attributes to listen for session passivation or
activation.
Let's have a brief look at some listening events, and the
listeners that watch for them (the HttpSessionActivationListener
interface is not covered in this tip).
Listening for Servlet Context Creation and Destruction
There is a single ServletContext for each Web application, for
each Java virtual machine*. Because of this, a ServletContext is
typically initialized when a Web application is deployed. The
ServletContext is destroyed when a Web application is undeployed.
The Web container calls ServletContextListener.contextInitialized
when the Web application is ready to process request. The
container calls the method contextDestroyed when the application
is going to be shut down.
Listening for HTTP Session Creation and Destruction
An HTTP session is associated with a single application client,
and is often created only when requested by the application code.
As mentioned earlier, the listener class, HttpSessionListener,
has methods that are called at HTTP session and destruction. The
class has two methods. The first method, sessionCreated, is
called when the Web container creates the session. The second
method, sessionDestroyed, is called when a session is destroyed.
A session may be destroyed either explicitly by invalidation or
by timeout.
Listening for Servlet Context Attribute Operations
Operations on servlet context attributes can be monitored by
implementing java.servlet.ServletContextAttributeListener in
a listener class. The Web container calls interface methods
attributeAdded, attributeReplaced, and attributeRemoved when
servlet context attributes are added, replaced, or removed,
respectively.
Listening for HTTP Session Attribute Operations
A listener class that implements
java.servlet.http.HttpSessionAttributeListener is notified
when there are changes to HTTP session attributes. The Web
container calls interface methods attributeAdded,
attributeReplaced, and attributeRemoved when HTTP session
attributes are added, replaced, or removed, respectively.
Furthermore, if the object being added or removed to the session
implements HttpSessionBindingListener, the Web container calls
the binding listener's valueBound or valueUnbound method to
notify the object of the change.
Configuring Listener Classes
At deploy time, listener classes are declared using the
tag in the deployment descriptor file, web.xml. The Web container
is responsible for creating and providing callbacks to instances
of the listener classes. An excerpt from this month's sample code
shows how to define listeners for an application:
TTJun2003WebApp
Demonstrate resource listing and life cycle listeners
com.elucify.tips.jun2003.LifeCycleServletContextListener
com.elucify.tips.jun2003.LifeCycleHttpSessionListener
...
Sample Code
If you haven't already done so, follow the instructions in the
section "Running the Sample Code to download and deploy the
sample archive, and access the application. Then go to the
section titled "Tip 2: Listener Interfaces" in the display page.
You'll see an HTML form that allows you to set, modify, and
delete HTTP session attributes (but not servlet context
attributes), as well as set the HTTP session timeout value.
The sample code defines listeners for three types of events:
servlet context creation/destruction, session attribute
modification, and session attribute value binding notification
(if the corresponding check box is checked). Each method simply
prints its name to standard output, to indicate that the callback
to the method occurred. The callbacks print feedback to the server
process's standard output unit. (In the Reference Implementation,
this output is directed to the server log.) For instance,
submitting the following information in the Set Attribute portion
of the form:
Name: cookie-name
Value: myCookie
prints the following in the server log of the Reference
Implementation:
LifeCycleServlet: instance created
LifeCycleServlet.init: servlet initialized
LifeCycleHttpSessionListener: sessionCreated
LifeCycleSessionAttributeListener: attributeAdded
name = 'cookie-name', value = 'myCookie'
LifeCycleServletContextListener is a typical listener class in
the sample code. Here is an excerpt from that class:
public class LifeCycleServletContextListener
implements ServletContextListener {
public LifeCycleServletContextListener() {
}
public void contextInitialized(
ServletContextEvent sce) {
System.out.println(
"LifeCycleServletContextListener: contextInitialized");
}
public void contextDestroyed(
ServletContextEvent sce) {
System.out.println(
"LifeCycleServletContextListener: contextDestroyed");
}
}
You will notice when you deploy the Web archive that the
contextInitialized call occurs before any service calls are
performed.
Use the Web form in the application welcome file to set, update,
and remove attributes, both with and without binding notification.
Experimenting with the listeners allows you to explore how they
behave in response to application life cycle events. The complete
source code for the sample listener classes and the corresponding
servlet (LifeCycleServlet) are bundled in the sample archive. You
can also browse the source in HTML using the link to the code on
the displayed page.
For another example of a ServletContextListener in action, see
the source code for the Java Pet Store Sample Application
(http://java.sun.com/blueprints/code/index.html). It uses
ServletContextListener to instantiate auxiliary objects when the
application is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RUNNING THE SAMPLE CODE
Download the sample archive for these tips (from
http://java.sun.com/jdc/EJTechTips/download/ttjun2003.ear).
The application's context root is ttjun2003. The downloaded
EAR file also contains the complete source code for the sample.
You can deploy the application archive (ttjun2003.ear) in the
J2EE Reference Implementation using the deploytool program:
$J2EE_HOME/deploytool -deploy ttjun2003.ear localhost
Replace localhost with the name of the host on which the server
is installed. For a standard installation on a single machine,
the hostname typically (and literally) is localhost. You
can access the application at http://localhost:8000/ttjun2003.
For a J2EE-compliant implementation other than the Reference
Implementation, use your J2EE product's deployment tools to
deploy the application on your platform.
See the index.html welcome file for instructions on running the
application.
. . . . . . . . . . . . . . . . . . . . . . .
IMPORTANT: Please read our Terms of Use, Privacy, and Licensing
policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
http://developer.java.sun.com/berkeley_license.html
* FEEDBACK
Comments? Please give us your feedback on the Enterprise Java
Technologies Tech Tips to:
http://developers.sun.com/contact/feedback.jsp?category=sdn
* SUBSCRIBE/UNSUBSCRIBE
Subscribe to other Java developer Tech Tips:
- Core Java Technologies Tech Tips. Get tips on using core
Java technologies and APIs, such as those in the Java 2
Platform, Standard Edition (J2SE).
- Wireless Developer Tech Tips. Get tips on using wireless
Java technologies and APIs, such as those in the Java 2
Platform, Micro Edition (J2ME).
To subscribe to these and other JDC publications:
- Go to the JDC Newsletters and Publications page,
(https://softwarereg.sun.com/registration/developer/en_US/subscriptions),
choose the newsletters you want to subscribe to and click
"Update".
- To unsubscribe, go to the subscriptions page,
(https://softwarereg.sun.com/registration/developer/en_US/subscriptions),
uncheck the appropriate checkbox, and click "Update".
- To use our one-click unsubscribe facility, see the link at
the end of this email:
- ARCHIVES
You'll find the Enterprise Java Technologies Tech Tips archives at:
http://developer.java.sun.com/developer/EJTechTips/index.html
- COPYRIGHT
Copyright 2003 Sun Microsystems, Inc. All rights reserved.
4150 Network Circle, Santa Clara, California 95054 USA.
This document is protected by copyright. For more information, see:
http://java.sun.com/jdc/copyright.html
Enterprise Java Technologies Tech Tips
June 26, 2003
Trademark Information: http://www.sun.com/suntrademarks/
Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks
or registered trademarks of Sun Microsystems, Inc. in the
United States and other countries.
* As used in this document, the terms "Java virtual machine"
or "JVM" mean a virtual machine for the Java platform.