novocode.com
About Novocode.com  |  Contact  |  Site Map  |  Search

Need more information? Shop for books about Servlets at Amazon.com:

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 [API 2.0] 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:

 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 [API 2.0]

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.