Servlet Collaboration:
The Servlet collaboration is all about sharing information among the servlets.
Collaborating servlets is to pass the common information that is to be shared
directly by one servlet to another through various invocations of the methods.
To perform these operations, each servlet need to know the other servlet with
which it is collaborated. Here are several ways to communicate with one another:
Using RequestDispatchers include() and forward() method;
Using HttpServletResponse sendRedirect() method;
Using ServletContext setAttribute() and getAttribute() methods;
Using Java's system-wide Properties list;
Using singleton class object.
Servlets running together in the same server have several ways to communicate
with one another. There are two main styles of servlet collaboration:
1)Sharing information:
This involves two or more servlets sharing state or resources. For example, a
set of servlets managing an online store could share the store's product
inventory count or share a database connection. Session tracking is a special
case of sharing information.
2)Sharing control:
This involves two or more servlets sharing control of the request. For example,
one servlet could receive the request but let another servlet handle some or all
of the request-handling responsibilities.
In the past (before Servlet API 2.1) we would have listed another style
of collaboration: direct manipulation . With this style of collaboration, a
servlet could obtain a direct reference to another through the getServlet( )
method and invoke methods on the other servlet. This style of collaboration is
no longer supported; the getServlet( ) method has been deprecated and defined to
return null for API 2.1 and later. The reason: a servlet may be destroyed by the
web server at any time, so nothing but the server should hold a direct reference
to a servlet. Everything that could be done with getServlet( ) can be
accomplished better and safer using the alternatives we'll learn about in this
topic.
Collaboration Through the System Properties List:
One simple way for servlets to share information is by using Java's system-wide
Properties list, found in the java.lang.System class. This Properties
list holds the standard system properties, such as java.version and
path.separator, but it can also hold application-specific properties. Servlets
can use the properties list to hold the information they need to share. A
servlet can add (or change) a property by calling:
System.getProperties().put("key", "value");
That servlet, or another servlet running in the same JVM, can later get the
value of the property by calling:
String value = System.getProperty("key");
The property can be removed by calling:
System.getProperties().remove("key");
It's best if the key for a property includes a prefix that contains the name of
the servlet's package and the name of the collaboration group. For example, "com.oreilly.servlet.ShoppingCart".
The Properties class is intended to be String based, meaning that each key and
value is supposed to be a String. This limitation, though, isn't commonly
enforced and can (although it's quite a hack) be ignored by servlets that want
to store and retrieve non-String objects. Such servlets can take advantage of
the fact that the Properties class extends the Hashtable class, so the
Properties list can (quite rudely) be treated as a Hashtable when storing keys
and values. For example, a servlet can add or change a property object by
calling:
System.getProperties().put(keyObject, valueObject);
It can retrieve the property object by calling:
SomeObject valueObject = (SomeObject)System.getProperties().get(keyObject);
It can remove the property object by calling:
System.getProperties().remove(keyObject);
This misuse of the Properties list causes the getProperty(), list() and save()
methods of the Properties class to throw ClassCastException objects when they
naturally--but erroneously--assume each key and value to be a String. For this
reason, if there's any chance these methods might be called, you should instead
use one of the techniques for servlet collaboration we describe later in the
chapter. Also, remember the class files for keyObject and valueObject should be
found in the server's classpath, not in the default servlet directory where they
would be loaded, and perhaps reloaded, by the special servlet class loaders.
There are three more caveats to using the system Properties list for servlet
collaboration: the information isn't naturally persistent between server
restarts, the information can be viewed (and modified or deleted) by other
classes executing in the servlet's JVM, and some servlet security managers may
not grant servlets access to the system property list.
Using properties to sell burritos:
Despite the stern warnings, servlet collaboration through the system-wide
Properties list works well for servlets that are sharing insensitive,
noncritical, easily replaceable information. As a fun example, imagine a set of
servlets that sell burritos and share a "special of the day." An administrative
servlet could set the special of the day using the following code:
System.getProperties().put("com.LaCostena.special.burrito",
"r4r");
System.getProperties().put("r4r.in.special.day", new Date());
Thereafter, every other servlet on the server can access the special and display it with code like this:
String burrito = System.getProperty(r4rin.special.burrito");
Date day = (Date)System.getProperties().get("r4r.in.special.day");
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
String today = df.format(day);
out.println("Our
burrito special today (" + today + ") is: "
+ burrito);
Faster image chaining:
Servlets performing image effects in a servlet chain can boost their speed
dramatically by using the system Properties list to pass their images. we
saw the standard method by which the servlets in a chain pass images from link
to link. The first servlet takes an Image object, encodes it to a stream of
bytes, and passes the bytes to the next servlet. The receiving servlet decodes
the bytes back into the original Image object. The technique works fine, but it
can be prohibitively slow for large images. An alternative solution is for the
first servlet to save the Image object itself in the system-wide Properties
list, then pass on a small unique key by which the next servlet in the chain can
locate the Image. In a sense, the old approach works by shoving an entire
elephant through the small portal between servlets.
demonstrates exactly how a servlet passes on a key to an Image object saved in
the system Properties list.
Passing an Image through the Properties list:
import java.awt.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ChainImageSource extends HttpServlet {
int keynum = 0; // used to create a unique key
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// Get an Image
String imageFile = req.getRealPath("/system/images/serverduke.gif");
Image image = Toolkit.getDefaultToolkit().getImage(imageFile);
// Create a unique key under which to store the image
String key = "com.oreilly.servlet.ChainImageSource." + keynum++;
// Store the image in the system Properties list using that key
System.getProperties().put(key, image);
// Tell the next servlet to expect an image key
res.setContentType("java-internal/image-key");
PrintWriter out = res.getWriter();
// Send the key
out.println(key);
}}
Notice how the servlet generates its unique key. It prefixes the key with the
string "com.oreilly.servlet.ChainImageSource", something no other servlet is
likely to use. Then it appends a different integer value for each image. Also
notice how this servlet uses the custom content type "java-internal/image-key"
to indicate that it's passing on an image key.
Example shows the other half of this servlet chain--a servlet that uses the key
to fetch the original Image object.
Example Fetching an image passed through the Properties list
import java.awt.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ChainImageSink extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// See what content type we're receiving
String contentType = req.getContentType();
Image image = null;
// An "image/*" content type means to expect the image as an encoded
// byte stream
if (contentType != null && contentType.startsWith("image")) {
}
// A "java-internal/image-key" content type means to expect a key
else if ("java-internal/image-key".equals(contentType)) {
// Read the first line of content to get the key
String key = req.getReader().readLine();
// Retrieve the Image stored under that key
image = (Image)System.getProperties().get(key);
// Always remove the Image, to avoid a memory leak
System.getProperties().remove(key);
}
// Other content types cannot be handled
else {
throw new ServletException("Incoming content type must be " +
"\"image/*\" or \"java-internal/image-key\"");
}
// Proceed to use the image as appropriate...
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
out.println("Received the image: " + image);
}}
The most important thing to notice with this example is that the receiving
servlet bears the responsibility of removing the Image from the system
Properties list to avoid a potentially large memory leak.
This leash-passing technique works only when the source servlet can be
absolutely sure its key is being sent to another servlet, not to a dumbfounded
user who expected an image. This can be ensured if every chain has as its final
servlet a special servlet whose sole purpose is to accept an image key and emit
that Image's encoded byte stream.
Collaboration Through a Shared Object:
Another way for servlets to share information is through a shared object. A
shared object can hold the pool of shared information and make it available to
each servlet as needed. In a sense, the system Properties list is a special case
example of a shared object. By generalizing the technique into sharing any sort
of object, however, a servlet is able to use whatever shared object best solves
its particular problem.
Often the shared object incorporates a fair amount of business logic or rules
for manipulating the object's data. This business logic protects the shared
object's actual data by making it available only through well-defined methods.
It can enforce data integrity, trigger events to handle certain conditions, and
basically abstract lots of little data manipulations into a single method
invocation. This capability isn't available with the Properties list.
There's one thing to watch out for when collaborating through a shared object:
the garbage collector. It can reclaim the shared object if at any time the
object isn't referenced by a loaded servlet. To keep the garbage collector at
bay, it's wise for every servlet using a shared object to save a reference to
the object.