站内搜索: 请输入搜索关键词
当前页面: 图书首页 > XML and Java: Developing Web Applications, Second Edition

XML and Java: Developing Web Applications, Second Edition

[ directory ] Previous Section Next Section

10.3 JavaServer Pages

A servlet writing an XML document tends to be hard to maintain and reuse because XML fragments, which are typically placed using the println() method, are embedded in the program. If we can use a template for an XML document in which some values can be inserted, the program becomes maintainable and reusable. JavaServer Pages (JSP) is the best fit for this purpose.

In this section, we introduce JSP and then show example JSPs that process XML documents. We use both the informational service and the transactional service, as described in Section 10.2.

10.3.1 What Is JSP?

In this section, we provide a brief introduction to JSP.

A Simple Example of JSP

Listing 10.12 shows a simple example of JSP called hello.jsp.

Listing 10.12 A simple example of JSP, hello.jsp
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML lang="en">
  <HEAD>
    <TITLE>The first sample of JavaServer Pages</TITLE>
    <META http-equiv="Content-Type"
      content="text/html; charset=us-ascii">
  </HEAD>
  <BODY>
    <H1>Hello!</H1>
    <P>It's <%=new java.util.Date()%> now.</P>
  </BODY>
</HTML>

The JSP hello.jsp outputs an HTML document. Open the following URL with a Web browser to see the Web page shown in Figure 10.5.

Figure 10.5. The result of hello.jsp

graphics/10fig05.gif

http://demohost:8080/xmlbook2/chap10/hello.jsp

The JSP hello.jsp looks like an HTML file, but it has the following line, which is to be replaced by the string representing the current date, as shown in Figure 10.5.

<P>It's <%=new java.util.Date()%> now.</P>

JSP outputs a string as the result of evaluating Java expressions enclosed by <%= and %>. As you can see, JSP allows you to embed dynamic contents in a document template.

Syntax of JSP

Table 10.1 shows some well-known JSP expressions. There are other useful JSP expressions; refer to the JSP specification for the details. As for how JSP works on an XML application server, see The Mechanism of a JSP Container.

The Mechanism of a JSP Container

A JSP runtime is called a JSP container. Figure 10.6 illustrates how a JSP is executed in a JSP container. (The following numbers in parentheses refer to the numbered sections of Figure 10.6.) Once the HTTP server receives an HTTP request to JSP, it requests that a JSP container process the request (1). The JSP container reads a JSP file associated with the URL from the server's local disk (2).

Figure 10.6. The mechanism of a JSP container

graphics/10fig06.gif

Table 10.1. Well-Known JSP Expressions

SYNTAX/EXAMPLE/RESULT

DESCRIPTION

<% Java program fragment %>

 
<%for (int i = 0; i < 3; i++) {%>
Hello!
<%{%>

Hello!Hello!Hello!

This is called a scriptlet. You can write any Java program between the symbols <% and %>. Note that a Java statement must not be completed in one scriptlet, as shown in the example.

<%=Expression%>

 
<%=new java.util.Date()%>

Sat Sep 15 19:00:13 JST 2001

The expression is evaluated as a Java string, and the result is written to the output.

<%@ page
  [import="{Class Name}, ..."]
  [contentType="MediaType[; charset=Charset]"]
%>

<%@ page
  import='java.util.Vector, java.io.*"%>
<%@ page
  charset="application/xml; charset=UTF-8"%>

You can specify attributes of the entire page that JSP outputs. The classes specified in the import attribute are imported. The contentType attribute specifies the value of the Content-Type header in the HTTP response to be sent back to the client.

N/A

 

The JSP file is eventually converted into a servlet instance. First, the JSP container converts the JSP file into Java source code that contains a servlet class definition (3). Second, it compiles the source code and generates a .class file (4). Third, it loads the .class file onto a Java VM (5) and creates a servlet instance for the JSP (6). Finally, the instance handles an HTTP request, just like the other servlets (7).

We recommend that you compare some JSP files with the generated source code to understand how JSP works. With Tomcat, the generated source code files are placed under the <jakart-tomcat>\work directory.

10.3.2 An Informational JSP Returning XML Documents

The JSP hello.jsp generates an HTML document. JSP, however, can generate not only HTML documents but also XML documents.[5] In this section, we describe JSPs that return XML documents.

[5] You will understand the reason if you recall the process in which a JSP is converted into a servlet.

Stock Quote Service

In Section 10.2.1, we described the stock quote service servlet. In such kinds of applications, typically, a template for the result XML document is finalized in advance and we only need to embed values in the template later. JSP is the best fit for such applications.

StockQuote.jsp, shown in Listing 10.13, is the JSP implementation of a stock quote service.

Listing 10.13 A JSP for a stock quote service, StockQuote.jsp
<%@ page contentType="application/xml; charset=UTF-8"%>
<jsp:useBean
  id="stockQuote"
  scope="request"
  class="chap10.stockquote.StockQuoteBean" />
<jsp:setProperty name="stockQuote" property="*" />
<?xml version="1.0" encoding="UTF-8"?>
<StockQuote
  company="<jsp:getProperty name="stockQuote" property="company"/>"
  xmlns="http://www.example.com/xmlbook2/chap10/stockquote">
  <price>
    <jsp:getProperty name="stockQuote" property="price"/>
  </price>
</StockQuote>

With JSP, it is easy to implement a one-way service that only returns XML documents.

Countermeasure for Cross-Site Scripting

Before we go into the details of StockQuote.jsp, we emphasize that this JSP also has a cross-site scripting vulnerability, as we discussed for the servlet in Section 10.2.1.

More specifically, the following portion is vulnerable. The jsp:setProperty tag sets the parameter value of company in the HTTP request to the property that has the same name as the stockQuote bean as is. The value of company is written to the output as is by jsp:getProperty. This is the cross-site scripting vulnerability.

<jsp:setProperty name="stockQuote" property="*" />
...
<?xml version="1.0" encoding="UTF-8"?>
<StockQuote
  company="<jsp:getProperty name="stockQuote" property="company"/>"
...

Therefore, even the typical combination of jsp:setProperty and jsp:getProperty has the cross-site scripting vulnerability. You should remember to escape the string to prevent cross-site scripting attacks, although it makes JSP programs slightly complicated.

StockQuote.jsp should be replaced by StockQuote2.jsp, as shown in Listing 10.14. It writes the value of company after explicitly escaping the string.

Listing 10.14 A revised JSP for a stock quote service, StockQuote2.jsp
<%@ page import="chap10.EscapeString"
         contentType="application/xml; charset=UTF-8"%>
<jsp:useBean
  id="stockQuote"
  scope="request"
  class="chap10.stockquote.StockQuoteBean" />

<jsp:setProperty name="stockQuote" property="*" />
<%
String company = EscapeString.escape(stockQuote.getCompany());
%>

<?xml version="1.0" encoding="UTF-8"?>
<StockQuote
  company="<%=company%>"
  xmlns="http://www.example.com/xmlbook2/chap10/stockquote">
  <price>
    <jsp:getProperty name="stockQuote" property="price"/>
  </price>
</StockQuote>

We access the following URL by running StockQuoteClient. The command is almost the same. The only difference is the URL. The result is the same as in Listing 10.2.

http://demohost:8080/xmlbook2/chap10/StockQuote2.jsp?company=IBM

A Consideration for Serializing XML Documents

Serializing an XML document in JSP is not a good idea based on the points we described in Section 3.4.2 because it does not check the well-formedness of XML documents with an XML processor.

However, we can narrow the variety of the XML documents to be generated if we escape all the strings to be embedded in the template in the way we described in the cross-site scripting attack section. The reason is that at least we can exclude the possibility that XML tags will be contained in the parameterized strings. For JSP, we recommend that you check the well-formedness of the XML documents generated by the JSP at development time. By doing so, you can avoid generating XML documents that are not well-formed.

Therefore, we recommend that you escape the strings to be output in advance.

Program Details

Next, we describe the details of StockQuote2.jsp step by step.

The following specifies the attributes (import class and Content-Type header) about the whole page to be generated by StockQuote2.jsp.

<%@ page import="chap10.EscapeString"
         contentType="application/xml; charset=UTF-8"%>

The following code fragment is a declaration of a bean named stockQuote. The bean is instantiated from the chap10.stockquote.StockQuoteBean class with a scope of the HTTP request.

<jsp:useBean
  id="stockQuote"
  scope="request"
  class="chap10.stockquote.StockQuoteBean" />

Listing 10.15 shows the source code for the bean used by StockQuote.jsp. This bean has two properties: company and price. The stock price is calculated the same way as in StockQuoteServlet in Section 10.2.1.

Listing 10.15 A bean class that holds the information for a stock quote service, chap10/stockquote/StockQuoteBean.java
package chap10.stockquote;

public class StockQuoteBean {
    private String company = null;
    private int price = 0;
    public StockQuoteBean() { }
    public void setCompany(String company) {
        this.company = company;
    }
    public String getCompany() { return company; }
    public void setPrice(int price) { this.price = price; }
    public int getPrice() {
        // Pseudo implementation of getPrice
        byte[] data = (company == null ? "" : company).getBytes();
        price = 0;
        for (int i = 0; i < data.length; i++)
            price += data[i] & 0xff;
        price = 150 - price % 100;
        return price;
    }
}

The following code fragment sets the property of the stockQuote bean to "*", which means that all properties of the bean are set from the corresponding parameters in an HTTP request. Then the value is escaped to prevent the cross-site scripting attack.

<jsp:setProperty name="stockQuote" property="*" />
<%
String company = EscapeString.escape(stockQuote.getCompany());
%>

The following code fragment generates a response XML document. There are JSP expressions, company and price, embedded in the template. Even though we use jsp:getProperty to embed a price as is, there is no problem because the type of price is integer. Therefore, we do not need to care about cross-site scripting and well-formedness.

<?xml version="1.0" encoding="UTF-8"?>
<StockQuote
  company="<%=company%>"
  xmlns="http://www.example.com/xmlbook2/chap10/stockquote">
  <price><jsp:getProperty name="stockQuote" property="price"/></price>
</StockQuote>

10.3.3 The Combination of Servlet and JSP

In this section, we describe how to combine Servlet and JSP so that we can take advantage of both technologies.

A Component Model Combining Servlet and JSP

Servlet complements the weak point of JSP and vice versa. Servlet has a programming language朿entric model and document fragments are embedded in a program, while JSP has a document-centric model and program code fragments are embedded in a document.

Does this imply that we have to tolerate their weak points in developing applications with Servlet or JSP? The answer is no. We can combine Servlet and JSP梩hat is, use Servlet to implement complicated application logic and use JSP to generate an output document.

Figure 10.7 shows a chaining model of Servlet and JSP as one of the application development models that combine Servlet and JSP.

Figure 10.7. A chaining model of Servlet and JSP

graphics/10fig07.gif

A servlet processes an input XML document and stores the result as a bean's property values so that JSP can use them easily. Then, JSP embeds the property values in the template of an XML document. The servlet uses the HttpSession object, described in Section 10.2, to pass the bean to the JSP. Finally, the servlet forwards the client to the JSP to delegate the output generation.

Figure 10.8 shows another combination of Servlet and JSP, called a cascading model. In this model, a servlet includes the output of a JSP within the servlet's output and sends it back to a Web client as if all the content were generated by the servlet. In this sense, this model is very similar to the Server-Side Include. We typically use beans to pass some values to the JSP like the chaining model. Note that the servlet needs to set HTTP headers such as Content-Type by itself in this model. In other words, the headers in the HTTP response from the JSP do not affect the servlet's HTTP response.

Figure 10.8. A cascading model of Servlet and JSP

graphics/10fig08.gif

You can take advantages of both Servlet and JSP by combining them. For example, if you want to modify an output XML document, you can just modify the JSP file.

This development model can be clearly mapped to the Model-View-Controller (MVC) Model: Bean as Model, JSP as View, and Servlet as Controller. This indicates the possibility that we can provide different views for different clients by providing customized JSPs for each client.

Improving BookStoreServlet Using JSP

BookStoreServlet2 is an example application that implements the chaining model shown in Figure 10.7. It is an improved version of BookStoreServlet, shown in Section 10.2. We use a servlet to process an input XML document contained in an HTTP request and generate an output XML document in an HTTP response by using JSP.

You can run BookStoreServlet2 by using the following command. The result is the same as in Listing 10.7. This is almost the same as for BookStoreServlet. The only difference is to change the URL to BookStoreServlet2.

R:\samples>java chap10.bookstore.BookStoreClient
 http://demohost:8080/xmlbook2/chap10/bookstore/BookStoreServlet2
 data\addItem.xml data\order.xml
Program Details

Listing 10.16 shows the source code for BookStoreServlet2.java.

Listing 10.16 Improved servlet class for the bookstore service, chap10/bookstore/BookStoreServlet2.java
package chap10.bookstore;

import java.io.IOException;
import java.util.Iterator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import chap10.GenericDOMServlet2;

public final class BookStoreServlet2 extends GenericDOMServlet2
    implements BookStoreApplication
{
    public void doProcess2(HttpServletRequest req,
                           HttpServletResponse res,
                           Document reqDoc)
        throws ServletException,
               IOException
    {
        // Gets a shopping cart from a session if it already exists.
        // If it does not exist, a shopping cart is newly created.
        ShoppingCartBean shoppingCart;
        HttpSession session = req.getSession();
        synchronized (session) {
            shoppingCart = (ShoppingCartBean)session
                .getAttribute(SHOPPING_CART_BEAN);
            if (shoppingCart == null) {
                shoppingCart = new ShoppingCartBean();
                session.setAttribute(SHOPPING_CART_BEAN,
                                     shoppingCart);
            }
        }

        // Gets the root element of reqDoc
        Element reqRoot = reqDoc.getDocumentElement();
        // Gets the local name of the root element
        String localName = reqRoot.getLocalName();

        // If the input XML document is "addItem"
        if ("addItem".equals(localName)) {
            // Creates an instance of BeansArrayBean
            // so that this servlet can store objects
            BeansArrayBean array = new BeansArrayBean();

            // For each "item" element in the request
            NodeList list = reqRoot
                .getElementsByTagNameNS(NAMESPACE_URI, "item");
            int length = list.getLength();
            for (int i = 0; i < length; i++) {
                ItemBean item = new ItemBean((Element)list.item(i));
                // Adds the item into the shopping cart
                shoppingCart.addItem(item);
                array.addBean(item);
            }
            session.setAttribute("result", array);
            String url = "/bookstore/" + localName + "Response.jsp";
            req.getRequestDispatcher(url).forward(req, res);
            return;
        }
        // If the input XML document is "order"
        if ("order".equals(localName)) {
            // Creates an instance of BeansArrayBean
            // so that this servlet can store objects
            BeansArrayBean array = new BeansArrayBean();

            // For each item in the shopping cart
            Iterator iterator = shoppingCart.getItems();
            while (iterator.hasNext()) {
                ItemBean item = (ItemBean)iterator.next();
                //
                // Orders the items here
                //
                array.addBean(item);
            }
            session.setAttribute("result", array);
            String url = "/bookstore/" + localName + "Response.jsp";
            req.getRequestDispatcher(url).forward(req, res);
            return;
        }
        throw new ServletException("Unknown request: " + localName);
    }
}

The major change from BookStoreServlet.java is that BookStoreDOM Handler2 does not return the response DOM tree, unlike BookStoreDOM Handler. In BookStoreDOMHandler2, a JSP generates the response XML documents instead.

BookStoreDOMHandler2 is almost same as BookStoreDOMHandler except for the following three points.

  1. It adds ItemBean, which represents the item to be processed by the addItem or order function, into BeansArrayBean instead of returning a DOM tree. For example, the following code is a part of the addItem function.

    // If the input XML document is "addItem"
    if ("addItem".equals(localName)) {
        // Creates an instance of BeansArrayBean
        // so that this servlet can store objects
        BeansArrayBean array = new BeansArrayBean();
    
         // For each "item" element in the request
         NodeList list =
             reqRoot.getElementsByTagNameNS(NAMESPACE_URI, "item");
         int length = list.getLength();
         for (int i = 0; i < length; i++) {
             ItemBean item = new ItemBean((Element)list.item(i));
             // Adds the item into the shopping cart
             shoppingCart.addItem(item);
             array.addBean(item);
        }
    ...
    }
    
  2. It stores BeanArrayBean in theHttpSession object with the name result as follows:

    session.setAttribute("result", array);
    
  3. It delegates the output of XML documents to the JSP by calling the RequestDispatcher#forward() method. The RequestDispatcher object can be obtained by calling the HttpServletRequest#getRequestDispatcher() method. The following code corresponds to that process.

    String url = "/bookstore/" + localName + "Response.jsp";
    req.getRequestDispatcher(url).forward(req, res);
    

    For a cascading model, a servlet calls the RequestDispatcher#include() method instead of the forward() method.

BookStoreDOMHandler2 stores the result as a bean in step 2, and then it forwards the client to the JSP to return the output XML documents in step 3.

Listing 10.17 shows BeansArrayBean.java, which is a bean to store the results. This bean can contain any number of beans.

Listing 10.17 A bean class that holds an array of beans, chap10/bookstore/BeansArrayBean.java
package chap10.bookstore;

import java.util.ArrayList;

public class BeansArrayBean {
    private ArrayList list = new ArrayList();
    public int getCount() { return list.size(); }
    public Object getBean(int i) { return list.get(i); }
    public void addBean(Object object) { list.add(object); }
}

Listing 10.18 shows the JSP addItemResponse.jsp, which generates the output XML documents for the addItem request.

Listing 10.18 A JSP that creates a response for the addItem request, addItemResponse.jsp
<%@ page import="chap10.bookstore.ItemBean, chap10.EscapeString"
         contentType="application/xml; charset=utf-8"%>
<jsp:useBean id="result"
             class="chap10.bookstore.BeansArrayBean"
             scope="session"/>
<addItemResponse
  xmlns="http://www.example.com/xmlbook2/chap10/bookstore/BookStore"
>
<%
for (int i = 0; i < result.getCount(); i++) {
    ItemBean item = (ItemBean)result.getBean(i);
    String id = EscapeString.escape(item.getId());
%>
<item id="<%=id%>" count="<%=item.getCount()%>"/>
<%
}
%>
</addItemResponse>

The following code fragment specifies the classes to be imported and the Content-Type header of this page.

<%@ page import="chap10.bookstore.ItemBean, chap10.EscapeString"
         contentType="application/xml; charset=utf-8"%>

Then, the following code declares the result bean to be passed by the servlet. Note that the scope of this bean is the HTTP session (session). This corresponds to the servlet used by an HttpSession object to carry the bean.

<jsp:useBean id="result"
             class="chap10.bookstore.BeansArrayBean"
             scope="session"/>

The following code outputs the start tag of the addItemResponse element.

<addItemResponse
  xmlns="http://www.example.com/xmlbook2/chap10/bookstore/BookStore"
>

Then, the following code fragment outputs all the item elements that correspond to the books to be added to the shopping cart. Each book item is obtained from the corresponding ItemBean in the result bean, declared earlier by jsp:useBean. Furthermore, it escapes the value of id to prevent cross-site scripting attacks.

<%
for (int i = 0; i < result.getCount(); i++) {
    ItemBean item = (ItemBean)result.getBean(i);
    String id = EscapeString.escape(item.getId());
%>
<item id="<%=id%>" count="<%=item.getCount()%>"/>
<%
}
%>

Finally, the following code outputs the end tag of the addItemResponse element.

</addItemResponse>

The JSP orderResponse.jsp is almost the same as addItemResponse.jsp. The full source code for both is available on the CD-ROM.

We hope you understand that the combination of Servlet and JSP makes an application more flexible for changes to the output XML documents.

In this section, we described JSP, which allows you to easily generate an XML document. In addition, the combination of Servlet and JSP gives great flexibility to Web applications for changes to the output documents.

We describe Apache Cocoon in the next section.

    [ directory ] Previous Section Next Section