4.6 Sharing Data Among Web Applications
One of the major purposes of Web applications is to keep data separate. Each Web application maintains its own table of sessions and its own servlet context. Each Web application also uses its own class loader; this behavior eliminates problems with name conflicts but means that static methods and fields can't be used to share data among applications. However, it is still possible to share data with cookies or by using ServletContext objects that are associated with specific URLs. These two approaches are summarized below.
Cookies. Cookies are maintained by the browser, not by the server. Consequently, cookies can be shared across multiple Web applications as long as they are set to apply to any path on the server. By default, the browser sends cookies only to URLs that have the same prefix as the one from which it first received the cookies. For example, if the server sends a cookie from the page associated with http://host/path1/ SomeFile.jsp, the browser sends the cookie back to http://host/path1/ SomeOtherFile.jsp and http://host/path1/path2/Anything, but not to http://host/path3/Anything. Since Web applications always have unique URL prefixes, this behavior means that default-style cookies will never be shared between two different Web applications.
However, as described in Section 2.9 (Cookies), you can use the setPath method of the Cookie class to change this behavior. Supplying a value of "/", as shown below, instructs the browser to send the cookie to all URLs at the host from which the original cookie was received.
Cookie c = new Cookie("name", "value"); c.setMaxAge(...); c.setPath("/"); response.addCookie(c);
ServletContext objects associated with a specific URL. In a servlet, you obtain the Web application's servlet context by calling the getServletContextmethod of the servlet itself (inherited from GenericServlet). In a JSP page, you use the predefined applicationvariable. Either way, you get the servlet context associated with the servlet or JSP page that is making the request. However, you can also call the getContextmethod of ServletContextto obtain a servlet contextnot necessarily your ownassociated with a particular URL. This approach is illustrated below.
ServletContext myContext = getServletContext(); String url = "/someWebAppPrefix"; ServletContext otherContext = myContext.getContext(url); Object someData = otherContext.getAttribute("someKey");
Neither of these two data-sharing approaches is perfect.
The drawback to cookies is that only limited data can be stored in them. Each cookie value is a string, and the length of each value is limited to 4 kilobytes. So, robust data sharing requires a database: you use the cookie value as a key into the database and store the real data in the database.
One drawback to sharing servlet contexts is that you have to know the URL prefix that the other Web application is using. You normally want the freedom to change a Web application's prefix without changing any associated code. Use of the get-Context method restricts this flexibility. A second drawback is that, for security reasons, servers are permitted to prohibit access to the ServletContext of certain Web applications. Calls to getContext return null in such a case. For example, in Tomcat you can use a value of false for the crossContext attribute of the Context or DefaultContext element (specified in install_dir/conf/server.xml) to indicate that a Web application should run in a security-conscious environment and prohibit access to its ServletContext.
These two data-sharing approaches are illustrated by the SetSharedInfo and ShowSharedInfo servlets shown in Listings 4.6 and 4.7. The SetSharedInfo servlet creates custom entries in the session object and the servlet context. It also sets two cookies: one with the default path, indicating that the cookie should apply only to URLs with the same URL prefix as the original request, and one with a path of "/", indicating that the cookie should apply to all URLs on the host. Finally, the Set-SharedInfo servlet redirects the client to the ShowSharedInfo servlet, which displays the names of all session attributes, all attributes in the current servlet context, all attributes in the servlet context that applies to URLs with the prefix /shareTest1, and all cookies.
Listing 4.6 SetSharedInfo.java
package moreservlets; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; /** Puts some data into the session, the servlet context, and * two cookies. Then redirects the user to the servlet * that displays info on sessions, the servlet context, * and cookies. */ public class SetSharedInfo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(true); session.setAttribute("sessionTest", "Session Entry One"); ServletContext context = getServletContext(); context.setAttribute("servletContextTest", "Servlet Context Entry One"); Cookie c1 = new Cookie("cookieTest1", "Cookie One"); c1.setMaxAge(3600); // One hour response.addCookie(c1); // Default path Cookie c2 = new Cookie("cookieTest2", "Cookie Two"); c2.setMaxAge(3600); // One hour c2.setPath("/"); // Explicit path: all URLs response.addCookie(c2); String url = request.getContextPath() + "/servlet/moreservlets.ShowSharedInfo"; // In case session tracking is based on URL rewriting. url = response.encodeRedirectURL(url); response.sendRedirect(url); } }
Listing 4.7 ShowSharedInfo.java
package moreservlets; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.util.*; /** Summarizes information on sessions, the servlet * context and cookies. Illustrates that sessions * and the servlet context are separate for each Web app * but that cookies are shared as long as their path is * set appropriately. */ public class ShowSharedInfo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "Shared Info"; out.println(ServletUtilities.headWithTitle(title) + "<BODY BGCOLOR=\"#FDF5E6\">\n" + "<H1 ALIGN=\"CENTER\">" + title + "</H1>\n" + "<UL>\n" + " <LI>Session:"); HttpSession session = request.getSession(true); Enumeration attributes = session.getAttributeNames(); out.println(getAttributeList(attributes)); out.println(" <LI>Current Servlet Context:"); ServletContext application = getServletContext(); attributes = application.getAttributeNames(); out.println(getAttributeList(attributes)); out.println(" <LI>Servlet Context of /shareTest1:"); application = application.getContext("/shareTest1"); attributes = application.getAttributeNames(); out.println(getAttributeList(attributes)); out.println(" <LI>Cookies:<UL>"); Cookie[] cookies = request.getCookies(); if ((cookies == null) || (cookies.length == 0)) { out.println(" <LI>No cookies found."); } else { Cookie cookie; for(int i=0; i<cookies.length; i++) { cookie = cookies[i]; out.println(" <LI>" + cookie.getName()); } } out.println(" </UL>\n" + "</UL>\n" + "</BODY></HTML>"); } private String getAttributeList(Enumeration attributes) { StringBuffer list = new StringBuffer(" <UL>\n"); if (!attributes.hasMoreElements()) { list.append(" <LI>No attributes found."); } else { while(attributes.hasMoreElements()) { list.append(" <LI>"); list.append(attributes.nextElement()); list.append("\n"); } } list.append(" </UL>"); return(list.toString()); } }
Figure 410 shows the result after the user visits the SetSharedInfo and ShowSharedInfo servlets from within the Web application that is assigned /shareTest1 as a URL prefix. The ShowSharedInfoservlet sees:
The custom session attribute.
The custom (explicitly created by the SetSharedInfoservlet) and standard (automatically created by the server) attributes that are contained in the default servlet context.
The custom and standard attributes that are contained in the servlet context that is found by means of getContext("/shareTest1"), which in this case is the same as the default servlet context.
The two explicitly created cookies and the system-created cookie used behind the scenes by the session tracking API.
Figure 410 Result of visiting the SetSharedInfo and ShowSharedInfo servlets from within the same Web application.
Figure 411 shows the result when the user later visits an identical copy of the ShowSharedInfo servlet that is installed in a Web application that has /shareTest2 as the URL prefix. The servlet sees:
The standard attributes that are contained in the default servlet context.
The custom and standard attributes that are contained in the servlet context that is found by means of getContext("/shareTest1"), which in this case is different from the default servlet context.
Two cookies: the explicitly created one that has its path set to "/"and the system-created one used behind the scenes for session tracking (which also uses a custom path of "/").
The servlet does not see:
Any attributes in its session object.
Any custom attributes contained in the default servlet context.
The explicitly created cookie that uses the default path.
Figure 411 Result of visiting the SetSharedInfo servlet in one Web application and the ShowSharedInfo servlet in a different Web application.