Book Home Java Servlet Programming Search this book

Chapter 11. Interservlet Communication

Contents:

Servlet Manipulation
Servlet Reuse
Servlet Collaboration
Recap

Servlets running together in the same server have several ways to communicate with each other. There are three major reasons to use interservlet communication:

Direct servlet manipulation

A servlet can gain access to the other currently loaded servlets and perform some task on each. The servlet could, for example, periodically ask every servlet to write its state to disk to protect against server crashes.

Servlet reuse

One servlet can use another's abilities to perform a task. Think back to the ChatServlet from the previous chapter. It was written as a server for chat applets, but it could be reused (unchanged) by another servlet that needed to support an HTML-based chat interface.

Servlet collaboration

The most common, situation involves two or more servlets sharing state information. For example, a set of servlets managing an online store could share the store's product inventory count. Session tracking (see Chapter 7, "Session Tracking" ) is a special case of servlet collaboration.

This chapter discusses why interservlet communication is useful and how it can be accomplished.

11.1. Servlet Manipulation

Direct servlet manipulation involves one servlet accessing the loaded servlets on its server and optionally performing some task on one or more of them. A servlet obtains information about other servlets through the ServletContext object. Use getServlet() to get a particular servlet:

public Servlet ServletContext.getServlet(String name) throws ServletException

This method returns the servlet of the given name, or null if the servlet is not found. The specified name can be the servlet's registered name (such as "file") or its class name (such as "com.sun.server.webserver.FileServlet"). The server maintains one servlet instance per name, so getServlet("file") returns a different servlet instance than getServlet("com.sun.server.webserver.FileServlet").[1] If the servlet implements SingleThreadModel, the server may return any instance of the servlet from the current pool. The server may--but isn't required to--load the named servlet and execute its init() method if it isn't already loaded. The Java Web Server does not perform this load. A ServletException is thrown if there is a problem during the load.

[1]getServlet("file") returns the instance that handles /servlet/file, while getServlet("com.sun. server.webserver.FileServlet") returns the instance that handles /servlet/com.sun.server.webserver. FileServlet.

You can also get all of the servlets using getServlets() :

public Enumeration ServletContext.getServlets()

This method returns an Enumeration of the Servlet objects loaded in the current ServletContext. Generally there's one servlet context per server, but for security or convenience, a server may decide to partition its servlets into separate contexts. The enumeration always includes the calling servlet itself. This method is deprecated in the Servlet API 2.0 in favor of getServletNames() :

public Enumeration ServletContext.getServletNames()

This method returns an Enumeration of the names of the servlet objects loaded in the current ServletContext. The enumeration always includes the calling servlet itself. When used with getServlet(), this method can perform the same function as the deprecated getServlets(). The name returned can be a registered name (such as "file") or a class name (such as "com.sun.server.webserver.FileServlet"). This method was introduced in Version 2.0 of the Servlet API.

Casting the Servlet object returned by getServlet() or getServlets() to its specific subclass can, in some situations, throw a ClassCastException. For example, the following code sometimes works as expected and sometimes throws an exception:

MyServlet servlet = (MyServlet)getServletContext().getServlet("MyServlet");

The reason has to do with how a servlet can be automatically reloaded when its class file changes. As we explained in Chapter 3, "The Servlet Life Cycle", a server uses a new ClassLoader each time it reloads a servlet. This has the interesting side effect that, when the MyServlet class is reloaded, it is actually a different version of MyServlet than the version used by other classes. Thus, although the returned class type is MyServlet and it's being cast to the type MyServlet, the cast is between different types (from two different class loaders) and the cast has to throw a ClassCastException. The same type mismatch can occur if the class performing the cast (that is, the servlet containing the above code) is reloaded. Why? Because its new ClassLoader won't find MyServlet using the primordial class loader and will load its own copy of MyServlet.

There are three possible workarounds. First, avoid casting the returned Servlet object and invoke its methods using reflection (a technique whereby a Java class can inspect and manipulate itself at runtime). Second, make sure that the servlet being cast is never reloaded. You can do this by moving the servlet out of the default servlet directory (usually server_root/servlets) and into the server's standard classpath (usually server_root/classes). The servlet performing the cast can remain in the servlets directory because its ClassLoader can find MyServlet using the primordial class loader. Third, cast the returned servlet to an interface that declares the pertinent methods and place the interface in the server's standard classpath where it won't be reloaded. Every class but the interface can remain in the servlets directory. Of course, in this case, the servlet must be changed to declare that it implements the interface.

11.1.1. Viewing the Currently Loaded Servlets

Example 11-1 uses these methods to display information about the currently loaded servlets, as shown in Figure 11-1.

Example 11-1. Checking out the currently loaded servlets

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Loaded extends HttpServlet {

  public void doGet(HttpServletRequest req, HttpServletResponse res)
                               throws ServletException, IOException {
    res.setContentType("text/plain");
    PrintWriter out = res.getWriter();

    ServletContext context = getServletContext();
    Enumeration names = context.getServletNames();
    while (names.hasMoreElements()) {
      String name = (String)names.nextElement();
      Servlet servlet = context.getServlet(name);
      out.println("Servlet name: " + name);
      out.println("Servlet class: " + servlet.getClass().getName());
      out.println("Servlet info: " + servlet.getServletInfo());
      out.println();
    }
  }
}
figure

Figure 11-1. Output from the loaded servlet

There's nothing too surprising in this servlet. It retrieves its ServletContext to access the other servlets loaded in the server. Then it calls the context's getServletNames() method. This returns an Enumeration of String objects that the servlet iterates over in a while loop. For each name, it retrieves the corresponding Servlet object with a call to the context's getServlet() method. Then it prints three items of information about the servlet: its name, its class name, and its getServletInfo() text. Notice that if the Loaded servlet used the deprecated getServlets() method instead of getServletNames(), it would not have had access to the servlets' names.

11.1.2. Saving the State of the Currently Loaded Servlets

The next example demonstrates another use for these methods. It works like Loaded, except that it attempts to call each servlets' saveState() method, if it exists. This servlet could be run periodically (or be modified to spawn a thread that runs periodically) to guard against data loss in the event of a server crash. The code is in Example 11-2; the output is in Figure 11-2.

Example 11-2. Saving the state of all the currently loaded servlets

import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class SaveState extends HttpServlet {

  public void doGet(HttpServletRequest req, HttpServletResponse res)
                               throws ServletException, IOException {
    res.setContentType("text/plain");
    PrintWriter out = res.getWriter();

    ServletContext context = getServletContext();
    Enumeration names = context.getServletNames();
    while (names.hasMoreElements()) {
      String name = (String)names.nextElement();
      Servlet servlet = context.getServlet(name);

      out.println("Trying to save the state of " + name + "...");
      out.flush();
      try {
        Method save = servlet.getClass().getMethod("saveState", null);
        save.invoke(servlet, null);
        out.println("Saved!");
      }
      catch (NoSuchMethodException e) {
        out.println("Not saved. This servlet has no saveState() method.");
      }
      catch (SecurityException e) {
        out.println("Not saved. SecurityException: " + e.getMessage());
      }
      catch (InvocationTargetException e) {
        out.print("Not saved. The saveState() method threw an exception: ");
        Throwable t = e.getTargetException();
        out.println(t.getClass().getName() + ": " + t.getMessage());
      }
      catch (Exception e) {
        out.println("Not saved. " + e.getClass().getName() + ": " +
                    e.getMessage());
      }

      out.println();
    }
  }

  public String getServletInfo() {
    return "Calls the saveState() method (if it exists) for all the " +
           "currently loaded servlets";
  }
}
figure

Figure 11-2. Output from the SaveState servlet

SaveState uses reflection to determine if a servlet has a public saveState() method and to invoke the method when it exists. If the invocation goes without a hitch, it prints "Saved!". If there's a problem, it reports the problem. Why does SaveState use reflection? Because otherwise it would have to cast each Servlet object to some class or interface that includes a saveState() method, and the code for each servlet would have to be modified to extend or implement that class or interface. Using reflection is an easier approach that doesn't require code modification. Reflection also avoids the ClassCastException problem noted earlier.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.