4. Miscellaneous
This chapter shows how to use features and techniques that
have not been explained and used in the example Servlets
of the previous chapters. The examples in this chapter are
often only fragments of Java source code and not necessarily
complete Servlets.
4.1 Not Thread-Safe Servlets
If a Servlet's service method (or one of the
do... methods for subclasses of HttpServlet)
cannot be implemented in a
thread-safe manner, the Servlet can declare that it implements the
javax.servlet.SingleThreadModel
interface
which guarantees that the service method is not called
concurrently. This interface does not contain any methods but simply
acts as a flag which indicates that the Servlet
is not thread-safe:
1: public class NotThreadSafeServlet extends HttpServlet
2: implements SingleThreadModel
3: {
4: protected void doGet(HttpServletRequest req,
5: HttpServletResponse res)
6: throws ServletException, IOException
7: {
8: // This method is called from HttpServlet's service method.
9: // Thus it will never be called concurrently.
10: }
11: }
|
If a server gets concurrent requests for such a Servlet it could e.g.
serialize the calls or create multiple instances of the Servlet which
form a Servlet pool.
The SingleThreadModel interface was added
in JSDK 2.0. Not thread-safe Servlets which have to run with JSDK 1.0
can use the synchronized keyword to ensure that a method
does not run concurrently:
|
1: public class NotThreadSafeServlet extends HttpServlet
2: {
3: protected synchronized void doGet(HttpServletRequest req,
4: HttpServletResponse res)
5: throws ServletException, IOException
6: {
7: // This method is synchronized on the Servlet object.
8: // Thus it will never be called concurrently.
9: }
10: }
|
4.2 An Application for the Command Pattern
If you need to manage a lot of temporary data for a request, it is
cumbersome to pass references to a lot of objects to all methods. This
also makes it more difficult to introduce new data because you have to
change all method signatures to include a reference to the new object.
One way to solve this problem is implementing the
SingleThreadModel interface as shown in the
previous section. This, however, can be
inefficient if the server does not create a Servlet pool to
allow concurrent access to multiple instances of the Servlet.
A usually more efficient solution makes use of a Command
object:
| CommandServlet.java
|
1: import java.io.*;
2: import javax.servlet.*;
3: import javax.servlet.http.*;
4:
5: public class CommandServlet extends HttpServlet
6: {
7: interface Command
8: {
9: public void execute()
10: throws ServletException, IOException;
11: }
12:
13: protected void doGet(HttpServletRequest req,
14: HttpServletResponse res)
15: throws ServletException, IOException
16: {
17: final PrintWriter out = res.getWriter();
18:
19: Command c = new Command(){
20:
21: private boolean lineBreak = true;
22:
23: public void execute()
24: throws ServletException, IOException
25: {
26: out.println("<HTML><BODY>");
27: printLink("foo.html");
28: printLink("bar.html");
29: out.println("</BODY></HTML>");
30: }
31:
32: private void printLink(String s)
33: throws IOException
34: {
35: out.print("<A href=\"" + s + "\">" + s + "</A>");
36: if(lineBreak) out.print("<BR>");
37: }
38: };
39:
40: c.execute();
41: }
42: }
|
The nested Command interface provides an execute
method which can be implemented by a sub-type to contain the code that
should be executed for a request. This is done by the anonymous class
which is defined in the Servlet's doGet method (lines 19
to 38).
For each request, the Servlet creates an instance of the anonymous
class (line 19) and calls its execute method (line 40). The
class has access to all final variables of the containing
doGet method and can also define own variables (like
lineBreak in line 21).
The implementation of the execute method contains the
code that would normally be placed into doGet. The
execute method can now call the printLink
method without explicitly passing the variables out
and lineBreak to it.
4.3 Client-Side Data with Cookies
We've already used Cookies indirectly through Sessions.
While session data is stored on the server side and only an index
to the server-side data (the session ID) is transmitted to the client,
Cookies can also be used directly to store data on the client side.
The Servlet API provides the class javax.servlet.http.Cookie
for a convenient object-oriented representation of Cookies so you don't
need to compose and decompose Cookie and Set-Cookie
HTTP headers yourself.
Even if you don't care where the data is stored it is sometimes useful
to manipulate Cookies directly via the Cookie class to get
more control over Cookie parameters like Domain and Path,
e.g. to share Cookies between different Servlets or even servers.
Example. Imagine an authentication Servlet which receives
a username and password from a login form via doPost and
verifies them against a central authentication database. It then computes
an authentiation string (e.g. a Base64-encoded "user:password"
combination, as used by HTTP Basic Authentication). This string
is now put into a Cookie and sent back to the client:
Cookie authCookie = new Cookie("xyz-Auth", credentials);
authCookie.setVersion(1);
authCookie.setDomain(".xyz.com");
res.addCookie(authCookie);
|
The Cookie's domain is set to ".xyz.com" so it will be sent to all
hosts in domain "xyz.com" like "a.xyz.com" and "foo.xyz.com" (but not
"c.d.xyz.com"). Note that the Domain attribute is supported by
RFC2109-style
Cookies (version 1) but not by old Netscape Cookies
(version 0, the default for newly created Cookie objects).
All Web Servers on hosts in xyz.com are running an instance of another
Servlet which serves protected data after verifying the authentication
credentials:
boolean verified = false;
Cookie[] cookies = req.getCookies();
for(int i=0; i<cookies.length; i++)
{
String n = cookies[i].getName(),
d = cookies[i].getDomain();
if(n != null && n.equals("xyz-Auth") &&
d != null && d.equals(".xyz.com"))
{
String credentials = cookies[i].getValue();
verfied = verifyCredentials(credentials);
break;
}
}
if(!verified)
{
res.sendRedirect(...);
return;
}
|
The credentials are retrieved from the Cookie and verified by the
authentication database. If the credentials are invalid or missing
the client is redirected to the login page on the authentication
server, otherwise the protected content is returned.