XML and the Model-View-Controller Pattern






XML and the Model-View-Controller Pattern

When I refer to XML as a presentation technology, I am referring primarily to the view in an application using a Model-View-Controller (MVC) architecture. Model-View-Controller is a software architecture originally documented as a pattern for traditional client applications (like those created with Swing) but has been widely adopted as an architecture for web applications. In short, an MVC application separates an application into three main areas:


Model

The raw data and business rules of an application


View

The user-visible rendition of the model


Controller

Functionality that receives requests from users, interprets those requests, interacts with the model, and provides the view with any necessary model objects

In more concrete terms, an MVC web application written with Java servlets and JSPs could processes a request in four steps:

  1. A servlet (the controller) receives the request and parses it.

  2. The servlet calls some methods on a data access object (the model).

  3. The servlet passes model data objects to a JSP page for rendering.

  4. The JSP page outputs an HTML page including data from the model objects.

There are a number of Java web MVC frameworks available that provide much of the base code necessary in any web application. Popular examples include Apache Struts (http://struts.apache.org), Spring MVC (http://www.springframework.org), JavaServer Faces (http://java.sun.com/javaee/javaserverfaces), and Tapestry (http://tapestry.apache.org).

XML in MVC Web Applications

XML can be used in several places in an MVC web application. Most MVC frameworks make heavy use of XML for internal configuration. More interesting for our purposes is where XML is used to contain the data passed between the view and the controller. Instead of the controller passing one or more model objects to the view, the controller constructs an XML representation of the model objects and passes the XML document to the view. In some cases, your application is responsible for delivering XML; the "view" is simply serializing the XML document as the HTTP response. In others, the view is some form of server-side transformation from the controller-supplied XML to a different XML syntax or to HTML. In addition, the use of XML to transfer model data between the controller and the view allows us to move any necessary transformations from the server to users' client applications (usually web browsers).

To start, let's simply create a servlet that produces an XML document. Figure contains a simple servlet that creates an XML document containing a list of books. Our model is a List of Map objects. Once it has created a DOM Document object, the servlet outputs the document using the identity transformation technique discussed in Chapter 7.

Servlet generating XML document

package javaxml3.ch13;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

public class BookListXMLServlet extends HttpServlet {

    private DocumentBuilderFactory documentBuilderFactory;

    private TransformerFactory transformerFactory;

    public void init(  ) {
        documentBuilderFactory = DocumentBuilderFactory.newInstance(  );
        transformerFactory = TransformerFactory.newInstance(  );
    }

    private Element newElementFromMap(Map map, String key, Document doc) {
        String text = (String) map.get(key);
        Text textNode = doc.createTextNode(text);
        Element element = doc.createElement(key);
        element.appendChild(textNode);
        return element;
    }

    protected void renderDocument(Document doc, HttpServletRequest request,
            HttpServletResponse response) throws IOException, ServletException {

        // tell the browser we're sending XML
        response.setContentType("text/xml");

        // now do the identity transformation
        try {
            Transformer identity = transformerFactory.newTransformer(  );

            // our Result object is a StreamResult wrapping the
            // ServletOutputStream
            Result result = new StreamResult(response.getOutputStream(  ));
            identity.transform(new DOMSource(doc), result);
        } catch (TransformerException e) {
            throw new ServletException(
                    "Unable to perform identity transformation", e);
        }
    }

    protected void service(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        List bookList = BookListFactory.INSTANCE;

        DocumentBuilder docBuilder = null;
        try {
            docBuilder = documentBuilderFactory.newDocumentBuilder(  );
        } catch (ParserConfigurationException e) {
            throw new ServletException(
                    "Unable to create DocumentBuilderFactory", e);
        }

        // create the DOM document
        Document doc = docBuilder.newDocument(  );
        Element books = doc.createElement("books");
        doc.appendChild(books);
        for (Iterator it = bookList.iterator(  ); it.hasNext(  );) {
            Map bookMap = (Map) it.next(  );
            Element book = doc.createElement("book");
            books.appendChild(book);
            book.appendChild(newElementFromMap(bookMap,
                    BookListConstants.TITLE, doc));
            book.appendChild(newElementFromMap(bookMap,
                    BookListConstants.AUTHOR, doc));
            book.appendChild(newElementFromMap(bookMap,
                    BookListConstants.PUBDATE, doc));
        }

        renderDocument(doc, request, response);
    }
}

class BookListConstants {

    public static final String AUTHOR = "author";

    public static final String PUBDATE = "pubdate";

    public static final String TITLE = "title";
}
class BookListFactory {

    public static final List INSTANCE;

    static {
        List templist = new ArrayList(  );
        Map m = new HashMap(  );
        m.put(BookListConstants.TITLE, "Ajax Hacks");
        m.put(BookListConstants.AUTHOR, "Bruce W. Perry");
        m.put(BookListConstants.PUBDATE, "March 2006");
        templist.add(m);

        m = new HashMap(  );
        m.put(BookListConstants.TITLE, "LDAP System Administration");
        m.put(BookListConstants.AUTHOR, "Gerald Carter");
        m.put(BookListConstants.PUBDATE, "March 2003");
        templist.add(m);

        m = new HashMap(  );
        m.put(BookListConstants.TITLE, "Java Servlet Programming");
        m.put(BookListConstants.AUTHOR, "Jason Hunter");
        m.put(BookListConstants.PUBDATE, "April 2001");
        templist.add(m);

        INSTANCE = Collections.unmodifiableList(templist);
    }

    private BookListFactory(  ) {
    }

}

Figure contains a web.xml servlet configuration file that maps this servlet to the path /booklist-xml.

Simple web.xml file

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="ch13-servlet" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <display-name>ch13-servlet</display-name>
    <servlet>
        <servlet-name>BookListXMLServlet</servlet-name>
        <servlet-class>javaxml3.ch13.BookListXMLServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>BookListXMLServlet</servlet-name>
        <url-pattern>/booklist-xml</url-pattern>
    </servlet-mapping>
</web-app>

Once we've created a web application containing this servlet class and configuration file, we can view the results in a browser, as seen in Figure.

XML output from a servlet


We didn't have to use a servlet to build this document. A page using JSP or another template language (such as Velocity) could build the same document. Instead of the servlet from Figure, we could have a much simpler servlet such as Figure and then do the XML creation in a JSP such as the one seen in Figure.

Much simpler book list servlet

package javaxml3.ch13;

import java.io.IOException;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class BookListXMLJSPServlet extends HttpServlet {

    protected void service(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        List bookList = BookListFactory.INSTANCE;

        request.setAttribute("bookList", bookList);
        RequestDispatcher dispatcher = getServletContext(  )
                .getRequestDispatcher("/booklist1.jsp");
        dispatcher.include(request, response);
    }

}

Rendering XML with JSP tags

<?xml version="1.0" encoding="UTF-8" ?>
<%@ page contentType="text/xml"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<books>
<c:forEach items="${bookList}" var="book">
    <book>
        <title>${book.title}</title>
        <author>${book.author}</author>
        <pubdate>${book.pubdate}</pubdate>
    </book>
</c:forEach>
</books>

Note that the XML declaration is placed at the top of the JSP page, above the directives. This is because the XML declaration must be the first thing in an XML document.


However, when we generate XML with a JSP such as Figure, we are now able to produce an XML document that is not well-formed. Because the servlet in Figure creates its document with DOM, there's no way a document that's not well-formed would be produced (except in case of a bug in the XML libraries). But with this method of creating a document with JSP, there is nothing to prevent you from accidentally creating a document with mismatched end tags such as:

<?xml version="1.0" encoding="UTF-8" ?>
<books>
    <book>
        <title>Ajax Hacks</title>
        <author>Bruce W. Perry</pubdate>
        <pubdate>March 2006</author>
    </book>
</books>

Or any of a variety of other well-formedness issues. Nevertheless, creating the document this way with JSP is bound to be faster and consume far less memory, since intermediate DOM objects are not created.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows