Chapter 13. Creating More Powerful Servlets

Yesterday you took an introductory look at working with Java servlets. Today, you'll see more of the story, such as supporting Model/View/Controller (MVC) architecture, user authentication, and more. Here are today's topics:

  • Working with servlet sessions

  • Working with servlet contexts

  • Storing and retrieving data in sessions and servlet contexts

  • Forwarding to a new servlet

  • Including a servlet in a servlet's response

  • Invoking JSP pages from servlets

  • Model-View-Controller (MVC) architecture

  • JSP Model 1 versus JSP Model 2 architecture

  • Implementing user authentication (usernames and passwords)

There's a great deal of power in servlets, and today you'll get to see more of what they have to offer.

Tip

For more on servlets, you can take a look at the official Servlet 2.3 Specification, which you can download for free from http://jcp.org/aboutJava/communityprocess/first/jsr053/index.html—as of this writing. If the URL has changed by the time you read this, take a look at http://java.sun.com/products/servlet/index.html for the new URL.

Sessions and Servlet Contexts

You learned how to work with JSP pages and sessions in Day 7, “Tracking Users with Sessions and Cookies,” and you can also work with sessions in servlets in much the same way. As you know, sessions establish a connection with the user's browser with a specific timeout. Although a session is active, you can store data in the session and recover that data the next time the user accesses the servlet.

For example, in this case, the servlet will keep track of how many times the user has accessed it, and display that information. It'll also show more information about the session, such as the session ID. It all starts by getting an HTTPSession object (see Table 7.4):

public class ch13_01 extends HttpServlet 
{

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        HttpSession session = request.getSession(true);
        .
        .
        .

Next, as you've seen in Day 7, you can get data from a session object with methods such as getAttribute. This example will store the number of times the user has accessed this servlet in the current session in an attribute named counter:

public class ch13_01 extends HttpServlet 
{

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        HttpSession session = request.getSession(true);

        Integer counter = (Integer) session.getAttribute("counter");
        .
        .
        .

If the counter attribute doesn't exist, the code will create it—and whether or not it exists, this attribute is incremented for the current access:

public class ch13_01 extends HttpServlet 
{

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        HttpSession session = request.getSession(true);

        Integer counter = (Integer) session.getAttribute("counter");

        if (counter == null) {
            counter = new Integer(1);
        } else {
            counter = new Integer(counter.intValue() + 1);
        }

        session.setAttribute("counter", counter);
        .
        .
        .

You can also use session methods such as isNew to see whether the session is new, getID to get the current session's ID, getLastAccessedTime to get the last time the session was accessed, getCreationTime to get the creation time of the session, getMaxInactiveInterval to get the timeout, and so on. You can see all that at work in Listing 13.1.

Example 13.1. Using Servlet Sessions (ch13_01.java)

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

public class ch13_01 extends HttpServlet
{

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        HttpSession session = request.getSession(true);

        Integer counter = (Integer) session.getAttribute("counter");

        if (counter == null) {
            counter = new Integer(1);
        } else {
            counter = new Integer(counter.intValue() + 1);
        }

        session.setAttribute("counter", counter);

        out.println("<HTML>");
        out.println("<HEAD>");
        out.println("<TITLE>");
        out.println("Using Sessions");
        out.println("</TITLE>");
        out.println("</HEAD>");
        out.println("<BODY>");
        out.println("<H1>Using Sessions</H1>");

        out.println("Welcome! You have been here " + counter + " times.<BR>");

        if(session.isNew()){
            out.println("This is a new session.<BR>");
        } else {
            out.println("This is not a new session.<BR>");
        }

        out.println("The session ID: " + session.getId() + "<BR>");
        out.println("Last time accessed: " + new Date(session.getLastAccessedTime()) +
Using Servlet Sessions (ch13_01.java) "<BR>");
        out.println("Creation time: " + new Date(session.getCreationTime())  + "<BR>");
        out.println("Timeout length: " + session.getMaxInactiveInterval()  + " seconds<BR>");

        out.println("</BODY>");
        out.println("</HTML>");
    }
}

And that's it—you can see the results in Figure 13.1, where you see an active session at work using servlets.

Using servlets and sessions.

Figure 13.1. Using servlets and sessions.

Besides using sessions, you can also store data in the servlet context (see Table 12.4 for the ServletContext interface). As discussed yesterday, a servlet context stores data accessible by all servlets in a Web application. For example, multiple users can access the same servlet, and those servlets can share data in the servlet context (as opposed to individual servlet sessions, which just store data for a single user's session).

To see how this works, you can add a second counter, counter2, to the current servlet example, and store this counter as a context attribute. To retrieve the value of counter2, you first get a servlet context object with getServletContext, then retrieve the attribute you're interested in with the getAttribute method:

public class ch13_02 extends HttpServlet 
{

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        HttpSession session = request.getSession(true);

        Integer counter2 = (Integer) getServletContext().getAttribute("counter2");
        .
        .
        .

If the counter2 attribute does not exist, you can create it in code. And you can also increment counter2 and store it back in the servlet context, like this:

public class ch13_02 extends HttpServlet 
{

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        HttpSession session = request.getSession(true);

        Integer counter2 = (Integer) getServletContext().getAttribute("counter2");

        if (counter2 == null) {
            counter2 = new Integer(1);
        } else {
            counter2 = new Integer(counter2.intValue() + 1);
        }

        getServletContext().setAttribute("counter2", counter2);
        .
        .
        .

Then you can display the total number of accesses to this servlet in the current Web application, as you see in Listing 13.2.

Example 13.2. Using Servlet Contexts (ch13_02.java)

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

public class ch13_02 extends HttpServlet
{

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        HttpSession session = request.getSession(true);

        Integer counter2 = (Integer) getServletContext().getAttribute("counter2");

        if (counter2 == null) {
            counter2 = new Integer(1);
        } else {
            counter2 = new Integer(counter2.intValue() + 1);
        }

        getServletContext().setAttribute("counter2", counter2);

        Integer counter = (Integer) session.getAttribute("counter");

        if (counter == null) {
            counter = new Integer(1);
        } else {
            counter = new Integer(counter.intValue() + 1);
        }

        session.setAttribute("counter", counter);

        out.println("<HTML>");
        out.println("<HEAD>");
        out.println("<TITLE>");
        out.println("Using Contexts");
        out.println("</TITLE>");
        out.println("</HEAD>");
        out.println("<BODY>");
        out.println("<H1>Using Contexts</H1>");

        out.println("Welcome! You have been here " + counter + " times.<BR>");

        out.println("Total page accesses: " + counter2 + "<BR>");

        if(session.isNew()){
            out.println("This is a new session.<BR>");
        } else {
            out.println("This is not a new session.<BR>");
        }

        out.println("The session ID: " + session.getId() + "<BR>");
        out.println("Last time accessed: " + new Date(session.getLastAccessedTime()) +
Using Servlet Contexts (ch13_02.java) "<BR>");
        out.println("Creation time: " + new Date(session.getCreationTime())  + "<BR>");
        out.println("Timeout length: " + session.getMaxInactiveInterval()  + "
Using Servlet Contexts (ch13_02.java) seconds<BR>");

        out.println("</BODY>");
        out.println("</HTML>");
    }
}

You can see the results in Figure 13.2 using a second browser, Netscape Navigator instead of Internet Explorer, to start a new session in the same Web application. You can see that the current user has accessed the servlet three times, but there have been five total accesses from all users.

Using servlet contexts.

Figure 13.2. Using servlet contexts.

Forwarding Requests to Other Web Resources

Another powerful aspect of servlet programming is forwarding data to other servlets or JSP pages. The current request and response objects are also forwarded. You can also include the output of other Web resources such as servlets or JSP, as originally discussed back in Day 1, “Getting Started!”

Here's an example showing how forwarding works. To indicate where the forwarding request came from, you can add attribute values to the request object. For example, here's how you might set the attribute servletID to "1":

public class ch13_03 extends HttpServlet 
{

    String target = "ch13_05";

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        request.setAttribute("servletID", "1");
        .
        .
        .
    }
}

To actually forward the request and response objects to a new servlet, you can use an object of the RequestDispatcher class. You can create an object of that class with a HttpServletRequest object's getRequestDispatcher method. You pass the URL you want to forward to this method.

For example, if you want to forward the request and response objects to a servlet mapped to the name ch13_05 (whose URL is http://localhost:8080/ch13/ch13_05 because of the way it's been mapped using a <servlet-mapping> element in web.xml), you can use this code:

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

public class ch13_03 extends HttpServlet
{

    String target = "ch13_05";

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        request.setAttribute("servletID", "1");
        RequestDispatcher dispatcher =
            request.getRequestDispatcher(target);
        .
        .
        .
    }
}

To actually perform the forward operation, you can use the methods of the RequestDispatcher class:

  • void forward(ServletRequest request, ServletResponse response)—. Forwards a request from a servlet to another Web resource (servlet, JSP file, or HTML file).

  • void include(ServletRequest request, ServletResponse response)—. Includes the content of a Web resource (servlet, JSP page, HTML file) in the output from this servlet.

In this case, you use the forward method, as you see in Listing 13.3.

Example 13.3. Forwarding Requests (ch13_03.java)

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

public class ch13_03 extends HttpServlet
{

    String target = "ch13_05";

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        request.setAttribute("servletID", "1");
        RequestDispatcher dispatcher =
            request.getRequestDispatcher(target);
        dispatcher.forward(request, response);
    }
}

You'll see this servlet at work in a moment, after covering include operations, which is coming up next.

Note that you can also forward request and response objects with JSP. In particular, you saw the <jsp:forward> element in Day 5, “Reading Data from Web Pages: Check Boxes, Radio Buttons, and Select Controls,” which enables you to perform forwards. Here's a JSP page that works the same way as the Java code in Listing 13.3:

<HTML> 
    <HEAD>
        <TITLE>Forwarding</TITLE>
    </HEAD>

    <BODY>
        <H1>Forwarding</H1>
        <%
            request.setAttribute("servletID", "1");
        %>
        <jsp:forward page="ch13_05"/>
     </BODY>
</HTML>

Including Output from Other Web Resources

Besides forwarding requests, you can also include the output of other resources, such as servlets, JSP pages, or HTML pages, in a servlet's output. Like forwarding, you do that with the RequestDispatcher class.

For example, in this case, you can give this new servlet the attribute servletID, setting it to 2 to distinguish it from the previous example. Next, you get a RequestDispatcher object, set its target, and use the include method to include the output from another resource. Here's how that looks when you're including the output of the servlet ch13_05:

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

public class ch13_04 extends HttpServlet
{

    String target = "ch13_05";

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        request.setAttribute("servletID", "2");
        RequestDispatcher dispatcher =
            request.getRequestDispatcher(target);
        dispatcher.include(request, response);
        .
        .
        .

Unlike forwarding operations, control returns to you after the output from the other Web resource is included in the output. That means that after the output from servlet ch13_05 is included in the output from the current servlet, you can display additional text from the current servlet, as you see in Listing 13.4.

Example 13.4. Using Includes (ch13_04.java)

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

public class ch13_04 extends HttpServlet
{

    String target = "ch13_05";

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        request.setAttribute("servletID", "2");
        RequestDispatcher dispatcher =
            request.getRequestDispatcher(target);
        dispatcher.include(request, response);
        PrintWriter out = response.getWriter();
        out.println("Response was included.<BR>");
    }
}

In JSP, you can perform include operations with the <jsp:include> element:

<jsp:include page="{relativeURL | <%= expression %>}" 
   flush="true| false" />

You can also set request parameter values with the <jsp:param> element (just as you can use the <jsp:param> element with <jsp:forward>, as you saw in Day 5:

<jsp:include page="{relativeURL | <%= expression %>}" 
    flush="true| false" >
    <jsp:param name="parameterName"
        value="{parameterValue | <%= expression %>}" />+
</jsp:include>

Here are what the various attributes mean:

  • page=”{ relativeURL | <%= expression %> }”—. This is the relative URL that specifies the resource to be included, or an expression that evaluates to a String equivalent to the relative URL. (The relative URL looks like a pathname—it cannot contain a protocol name, port number, or domain name.)

  • flush="true | false"—. If the page output is buffered and the flush attribute set to true, the buffer is flushed before it includes output from other Web resources. This means that all current output from your code will be sent to the Web page before output from other Web resources is included. The default is false.

  • <jsp:param name="parameterName" value=”{parameterValue | <%= expression %>}” />+—. Using <jsp:param>, you can pass one or more parameter names and values to the included Web resource.

For example, here's how to convert the servlet to include the example to a JSP page using <jsp:include>:

<HTML> 
    <HEAD>
        <TITLE>Includes</TITLE>
    </HEAD>

    <BODY>
        <H1>Includes</H1>
        <%
            request.setAttribute("servletID", "2");
        %>
        <jsp:include page="ch13_05"/>
        Response was included.
        <BR>
    </BODY>
</HTML>

Now you've seen what it takes to forward requests and include the output of other resources using servlets in code—next, you'll see the page that handles the forwarding, or including, operation.

Handling Forwards and Includes

Both of the previous forwarding and including examples (ch13_03 and ch13_04) use the servlet in Listing 13.5 as their target. You can determine what servlet (ch13_03 or ch13_04) is at work by checking the servletID attribute—which both servlets set—as you see in Listing 13.5.

Example 13.5. A Servlet That Serves as a Target for Forwards and Includes (ch13_05.java)

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

public class ch13_05 extends HttpServlet
{

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String servletID = (String) request.getAttribute("servletID");
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        out.println("<HTML>");
        out.println("<HEAD>");
        out.println("<TITLE>");
        out.println("Using Sessions");
        out.println("</TITLE>");
        out.println("</HEAD>");
        out.println("<BODY>");
        out.println("<H1>Using Forwards and Includes</H1><BR>");

        if (servletID != null) {
            if (servletID.equals("1")) {
                out.println("Forwarding was successful!<BR>");
            }
            if (servletID.equals("2")) {
                out.println("Including was successful!<BR>");
            }
        } else {
            out.println("This example must be invoked with ch13_03 or ch13_04");
        }

        out.println("</BODY>");
        out.println("</HTML>");
    }
}

You can see the results when you use the forwarding servlet (ch13_03) in Figure 13.3. As you can see, the forwarding request was handled as it was supposed to be.

Forwarding a request.

Figure 13.3. Forwarding a request.

You can also see the results of the including servlet (ch13_04) in Figure 13.4. The output from the included servlet appears in the Web page, as well as the additional text the including servlet adds.

Handling an include operation.

Figure 13.4. Handling an include operation.

Now that you know about forwarding requests, it's time to take a look at another aspect of JSP programming—Model-View-Controller architecture. The separation of data presentation from the code, which actually manipulates the data, has become big news in JSP. And that's been the impetus behind two different models of JSP programming—Model 1 and Model 2.

JSP Model 1 Architecture

JSP Model 1 programming is the way smaller applications are typically constructed. You use a JSP page that calls a few JavaBeans that implement the application's logic. For example, you may want to present an online catalog to the user, use JSP pages to display items to the user, search for additional items, move to new items, and so on. All this is done while using beans to calculate prices with shipping and tax, and to check whether an item is in stock.

In programming architecture, JSP is responsible for everything but calling the internal logic implemented in the beans. However, as applications have gotten larger and larger, a new programming architecture, JSP Model 2, has evolved.

JSP Model 2 Architecture

JSP Model 2 Architecture
JSP Model 2 Architecture
JSP Model 2 Architecture
  • Controller—Oversees the model and the view by reacting to the data the user sends. Accepts input from the user, calls the appropriate bean methods in the model, and sends data to the view for presentation. Often implemented as a servlet.

  • Model—Implements the functionality, such as the “business rules” of the application. This is the core code that actually does the internal work, such as checking whether an item is in stock, and so on. The model doesn't know anything about the view of the controller. This is often implemented using JavaBeans.

  • View—Implements the presentation of data to the user. The view takes the data supplied to it (usually from the controller), and displays it. The designers of the view need to know HTML, but they don't need to know anything about the logic implemented in the model. The view is often implemented in JSP.

A JSP Model 2 application often uses all three programming techniques we've seen—a servlet controller, a JavaBean model, and a JSP view. An example showing how that might work, and showing how to communicate between these types of components, is coming up next. In the example, you'll see a servlet controller that calls a bean to get some data to display, and passes that data on to a JSP page for presentation.

The Controller

The user interacts with the controller, which is a servlet in this case, so that when the user accesses this servlet, its code creates an object named model using a Java bean. Then the servlet calls that object's msg method to get a message to send back to the user. To forward the message on to a JSP page, the servlet uses a request attribute named “message” this way:

public class ch13_06 extends HttpServlet 
{

    ch13_07 model1 = new ch13_07();

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        request.setAttribute("message", model1.msg());
        .
        .
        .

This is just a simple example, of course—usually, you would pass data you received from the user on to the model's methods, so that it can handle that data.

After getting the data it is supposed to display, the servlet simply forwards that data to a JSP page. To forward data from a servlet to a servlet, you use the name you've mapped the target servlet to; when you're forwarding data from a servlet to a JSP page, you use the relative URL of the JSP page itself, as you see in Listing 13.6.

Example 13.6. The Controller (ch13_06.java)

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import beans.ch13_07;

public class ch13_06 extends HttpServlet
{

    String target = "ch13_08.jsp";

    ch13_07 model1 = new ch13_07();

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        request.setAttribute("message", model1.msg());
        RequestDispatcher dispatcher =
            request.getRequestDispatcher(target);
        dispatcher.forward(request, response);
    }
}

The Model

The model in this case is just some simple Java code that supports the msg method, which returns the message "Hello from JSP!", as you see in Listing 13.7.

Example 13.7. The Model (ch13_07.java)

public class ch13_07
{
    public String msg()
    {
        return "Hello from JSP!";
    }

    public ch13_07()
    {
    }
}

In real applications, the model might be connected to a database to check data such as prices, number of items in stock, and so on.

The View

The view in this example is the JSP page you see in Listing 13.8. The code in this page simply retrieves the message attribute sent to it by the controller, and displays the results.

Example 13.8. The View (ch13_08.jsp)

<HTML>
  <HEAD>
    <TITLE>Using MVC Architecture</TITLE>
  </HEAD>

    <BODY>
        <H1>Using MVC Architecture</H1>
        The message is
        <% out.println(request.getAttribute("message")); %>
   </BODY>
</HTML>

To put this MVC example to work, compile the bean (ch13_07.java) into ch13_07.class, and the servlet (ch13_06.java) into ch13_06.class. The servlet imports the bean so that you'll have to set the classpath to include not only servlet.jar, but also the bean class, ch13_07.class. You can do that if you store ch13_07.class in a directory named WEB-INFclasseseans:

ch13                                 ch13_08.jsp 
|____WEB-INF
       |____classes                  ch13_6.class
              |____beans             ch13_07.class

To set the classpath so that Java can find the bean, include the current directory—what Java represents by a dot (.)—in the classpath variable. That might look similar to this in Windows (where spath is the path to servlet.jar):

C:	omcatjakarta-tomcat-4.0.3webappsch13WEB-INFclasses>set classpath=spathservlet.jar;. 

Now you can compile the servlet, ch13_06.java, using javac. Finally, map the ch13_06 servlet to the name ch13_6 using web.xml, as you've seen before. (Note that all this tedious preparation is already done in the code you can download for this book.)

Next you can navigate to the controller, ch13_06, which calls the bean, ch13_07, and displays the results by forwarding data to the JSP page, ch13_08. You can see the results in Figure 13.5. As you see, the message from the bean, “Hello from JSP!” appears in the JSP page—now you're using MVC programming architecture.

Using MVC architecture.

Figure 13.5. Using MVC architecture.

MVC architecture is great for separating data presentation from program logic, and that's a hot topic in JSP programming. As your Web applications become more complex, the separation is a good idea to make developing and maintaining your application easier.

Supporting User Authentication

Here's another topic that adds a great deal of power to your programming arsenal—user authentication. You've probably been to places on the Web where the server asks you to sign in with a dialog box asking for your username and password, and you can support that on Tomcat as well. This next example will do just that—create a servlet that you need a password to access.

You can set up user authentication with web.xml and another file named tomcat-users.xml. In web.xml, the <web-app> element can include two elements, <security-constraint> and <login-config>:

<web-app> 
    <icon>
    <display-name>
    <description>
    <distributable>
    <context-param>
    <servlet>
    <servlet-mapping>
    <session-config>
    <mime-mapping>
    <welcome-file-list>
    <error-page>
    <taglib>
    <resource-ref>
    <security-constraint>
    <login-config>
    <security-role>
    <env-entry>
    <ejb-ref>
</web-app>

Here are the subelements you can use in the <security-constraint> element:

<security-constraint> 
    <web-resource-collection>
        <web-resource-name>
        <description>
        <url-pattern>
        <http-method>
    </web-resource-collection>
    <auth-constraint>
        <description>
        <role-name>
    </auth-constraint>
    <user-data-constraint>
        <description>
        <transport-guarantee>
    </user-data-constraint>
</security-constraint>

And here are the subelements you can use in the <login-config> element:

<login-config> 
    <auth-method>
    <realm-name>
</login-config>

How do you actually set a password so that Tomcat knows about it? You use the tomcat-users.xml, which is stored in the jakarta-tomcat-4.0.3conf directory. For example, to give the user Steve the password “tomcat,” you can add a line in tomcat-users.xml such as this (as you might expect, you need to shut down and restart Tomcat to make these changes take effect):

<!-- 
  NOTE:  By default, no user is included in the "manager" role required
  to operate the "/manager" web application.  If you wish to use this app,
  you must define such a user - the username and password are arbitrary.
-->
<tomcat-users>
  <user name="tomcat" password="tomcat" roles="tomcat" />
  <user name="role1"  password="tomcat" roles="role1"  />
  <user name="both"   password="tomcat" roles="tomcat,role1" />
  <user name="steve"  password="tomcat" roles="jsp" />
</tomcat-users>
Supporting User Authentication

Here's an example putting this all to work. This example will use basic user authentication, which is the type that displays the customary dialog box asking for username and password. To make this happen, you can edit web.xml to add the <security-constraint> and <login-config> elements:

<?xml version="1.0" encoding="ISO-8859-1"?> 
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <display-name>Example Applications</display-name>

  <servlet>
    <servlet-name>ch13_01</servlet-name>
    <servlet-class>ch13_01</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>ch13_02</servlet-name>
    <servlet-class>ch13_02</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>ch13_03</servlet-name>
    <servlet-class>ch13_03</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>ch13_04</servlet-name>
    <servlet-class>ch13_04</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>ch13_05</servlet-name>
    <servlet-class>ch13_05</servlet-class>
  </servlet>
        .
        .
        .
  <session-config>
    <session-timeout>30</session-timeout>
  </session-config>

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Secure Area</web-resource-name>
      <url-pattern>/ch13_09</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>jsp</role-name>
    </auth-constraint>
  </security-constraint>

  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>JSP Area</realm-name>
  </login-config>

</web-app>

In this case, web.xml just sets a password for the ch13_09 servlet only, using the <url-pattern> element. Any URLs in the Web application that match the URL pattern will be subject to user authentication. Although this example only requires user authentication for the ch13_09 servlet itself, you can use URL patterns such as /secure/* to match any URLs in a subdirectory named secure, or just * to match any URL in the Web application.

You can get user authentication data from the request object—for example, the getAuthType method returns the type of user authentication employed (that's BASIC here), and the getUserPrincipal method to get the username (that's Steve here). You can see this at work in the servlet in Listing 13.9.

Example 13.9. A Password-Protected Servlet (ch13_09.java)

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

public class ch13_09 extends HttpServlet
{
    public void init(ServletConfig cfg) throws ServletException
    {
        super.init(cfg);
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        out.println("<HTML>");
        out.println("<HEAD>");
        out.println("<TITLE>");
        out.println("User Authentication");
        out.println("</TITLE>");
        out.println("</HEAD>");
        out.println("<BODY>");
        out.println("<H1>User Authentication</H1>");

        String type = request.getAuthType();
        out.println("Welcome to this secure page.<BR>");
        out.println("Authentication mechanism: " + type + "<BR>");
        Principal principal = request.getUserPrincipal();
        out.println("Your username is: " + principal.getName() + "<BR>");

        out.println("</BODY>");
        out.println("</HTML>");
    }
}

Navigate to this servlet now (note that even if you're using the downloadable code, you still must edit tomcat-users.xml to create a username and password that Tomcat can understand).

You can see the results in Figure 13.6, where the user is being asked to log in.

Logging in.

Figure 13.6. Logging in.

After the user enters his username and password and clicks the OK button, you can see the results in Figure 13.7. The user has been authenticated, and the servlet displays the authentication mechanism used, and the username.

Authenticating a user.

Figure 13.7. Authenticating a user.

Tip

See also “Using Filters for User Authentication” in Day 14, “Using Filters.”

Using Cookies in Servlets

Here's another thing you can do with servlets—work with cookies. In fact, working with cookies in servlets is much like working with cookies in JSP, which you saw in Day 7.

Take a look at Listing 13.10. This example puts to work the same example you saw in Day 7, which used a cookie to set the background color of a Web page.

Example 13.10. Using Cookies in Servlets (ch13_10.java)

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

public class ch13_10 extends HttpServlet
{

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        out.println("<HTML>");
        out.println("<HEAD>");
        out.println("<TITLE>");
        out.println("A Web Page");
        out.println("</TITLE>");
        out.println("</HEAD>");
        out.println("<BODY");

        Cookie[] cookies = request.getCookies();
        boolean foundCookie = false;

        for(int loopIndex = 0; loopIndex < cookies.length; loopIndex++) {
            Cookie cookie1 = cookies[loopIndex];
            if (cookie1.getName().equals("color")) {
                out.println("bgcolor = " + cookie1.getValue());
                foundCookie = true;
            }
        }

        if (!foundCookie) {
            Cookie cookie1 = new Cookie("color", "cyan");
            cookie1.setMaxAge(24*60*60);
            response.addCookie(cookie1);
        }

        out.println(">");
        out.println("<H1>Setting and Reading Cookies</H1>");
        out.println("This page will set its background color using a cookie when reloaded.");
        out.println("</BODY>");
        out.println("</HTML>");
    }
}

The first time you load this servlet, it sets a cookie named color to "cyan". The next time you load this servlet, it reads that cookie and uses it to set the background of the Web page it creates to cyan, as you see (in glorious black and white) in Figure 13.8.

Using a cookie with a servlet.

Figure 13.8. Using a cookie with a servlet.

Thread Safety

Thread Safety

There's a way to ensure thread safety in servlets—you can implement the SingleThreadModel interface (which has no methods). If a servlet implements this interface, you are guaranteed that no two threads will execute concurrently in the servlet's service method (the server will either make other threads wait, or create new copies of the code):

public class servlet1 extends HttpServlet implements SingleThreadModel 
{
        .
        .
        .
}

In JSP pages, the page directive has an attribute named isThreadSafe, which indicates whether a JSP page is thread-safe. The default value is true, which means that multiple threads might execute the same code. Although the server is supposed to handle the case where JSP pages are not thread-safe if you set this attribute to false, don't count on that—the best choice is to make the JSP code thread-safe in the first place. This means you must be aware that if you're using session or application variables, for example, other threads may be changing that data.

In Java, you can handle multithread issues by using the synchronized keyword, which enables you to block access to an object until you're done with it. For example, say that shared is an application-level object, and that you want to block access to it by other threads until you're done with it. You can do that with the synchronized keyword, which makes other threads wait until you're done:

class CustomThread 
{
    Shared shared;

    public CustomThread(Shared shared, String string)
    {
        super(string);
        this.shared = shared;
        start();
    }

    public void run() {
        synchronized(shared) {
            shared.doWork();
        }
    }
}

You can also use this keyword to restrict access to methods—see the Java documentation for more details.

Summary

Today you learned a great deal about creating and using servlets. Today's work started with using servlets in sessions and storing data in sessions. As you saw, you can store data from a single servlet in a single session. You also learned that you could use servlet contexts to store data across servlets.

You learned that you can perform forwarding and including operations with servlets. A forwarding operation forwards both the request and response objects to another Web resource, such as a servlet or JSP page. An including operation includes the output from another Web resource.

You learned that JSP Model 2 programming uses Model-View-Controller programming architecture, or MVC architecture. In that architecture, the user interacts with a controller, which in turn calls the model to handle the application's internal logic, and the view presents data to the user.

You also took a look at user authentication today. With user authentication, you can ask the user for a username and password, and restrict access to Web resources.

Finally, you learned that working with cookies in servlets is much like working with cookies in JSP—you use the getCookies and addCookie methods of a request object.

Tomorrow, we'll dig into more details of the Tomcat server itself, as we see how to create filters that let you examine the input and output for Web resources before those resources gain access to it.

Q&A

Q1:

Can I forward a request multiple times?

A1:

Yes, that's no problem—a copy of the same request and response objects are forwarded on each time you perform a forwarding operation.

Q2:

Can I invoke a servlet from a JSP page?

A2:

Yes, just forward on to the appropriate URL. If the servlet has been mapped to a particular name, you can forward it to that name.

Workshop

This workshop tests whether you understand all the concepts you learned today. It's a good idea to master today's concepts by honing your knowledge here before starting tomorrow's material. You can find the answers to the quiz questions in Appendix A.

Quiz

1:

If you want to share data between three servlets in a Web application, would you use servlet sessions or servlet contexts?

2:

What Java class do you use to implement forward and include operations in servlets?

3:

What do the various components of the MVC architecture do?

4:

What's the name of the file in which you store usernames and passwords in Tomcat?

5:

What elements do you use in web.xml to implement password access?

Exercises

1:

Alter the MVC example from today (Listings 13.6, 13.7 and 13.8) to let the user enter a message. Pass that message on to the model, which will change it to uppercase with the String method toUpper and pass it back to the controller. Then have the view display the new uppercase message.

2:

Give implementing user authentication a try. This time, implement usernames and passwords for not just one, but three servlets in a Web application at the same time (that is, put the three servlets in a single Web application, and use * in the <url-pattern> element).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.60.158