The Grand Tour: Kickin’ It MVC Style

January is turning out to be a pretty light month for Java news. Oh well, more time to continue my grand tour of Java Web development! Still following along?

Last time, I showed you how to convert a “to-do list” servlet into a JSP. Unfortunately, what we got wasn’t much better in terms of readable code–Java code embedded in an HTML document isn’t a whole lot more readable than HTML code embedded in a Java class.

What to do? Well, so far we have two tools at our disposal: servlets and JSPs. Servlets are great for containing Java code, JSPs are great for containing HTML code. The answer is to split our application apart so that the Java code goes into servlets, and the HTML code goes into JSPs.

A standard approach for this called Model 2 Architecture has been around for a long time, but lately it has become known by the more descriptive name: Model-View-Controller (MVC).

The idea is to break the application into three parts:

  • The model: a collection of Java classes that perform the actual work of the application (often called the business logic), independant of the Web interface.
  • The view, a collection of JSPs, or some other technology, that produce the actual Web pages that the users see.
  • The controller, one or more servlets that process browser requests by telling the model what to do and then handing over to the view to respond.

Now I could blab on and on about this all day, but it’s easier to see through an example, so let’s get to work on modifying our to-do list application.

The model is usually the best place to start, as you can usually write it without worrying much about the Web interface. In the case of our to-do list application, we already have our model: it’s made up of the classes ToDoList and ToDoItem. These two classes do all the work of managing the to-do list.

While we’re looking at these classes, let’s make one minor tweak to the getToDoItems method: we’ll make it return a List rather than an Iterator. This allows for a bit more flexibility in how the to-do list items may be used when they are retrieved through this method.

  public List getToDoItems() {
    refreshList();
    return (List)list.clone();
  }

Next, let’s work on our controller. We know we’ll have three different kinds of requests coming from the browser: simple requests to view the to-do list, form posts for adding new items to the list, and form posts for deleting existing items from the list.

We now have a choice: do we write one servlet that knows how to handle all these different request types, or do we write three simple, specialized servlets that each know how to handle one type of request? There is no right answer, as there are advantages to each approach, but let’s try to keep our classes simple and go for multiple servlets.

We’ll start with ToDoServlet, which will handle requests to view the to-do list. The first thing this servlet needs to do is set up the model–an instance of ToDoList. Back when we first looked at servlets, we did this with the init() method of the servlet, which stored the ToDoList in an instance variable of the servlet.

This time, that won’t quite do the trick, because we want all three of our servlets to be able to access the model. We can’t create the ToDoList in one servlet’s initialization procedure because one of the other servlets might need it first, and we can’t store it in an instance variable because the other servlets wouldn’t be able to see it at all. What we need is a way to create the model when the Web application first starts up, and a place to store it where any servlet can get at it.

To perform initialization tasks for the entire application, we must use a ServletContextListener. Rather than a class to extend, this is an interface, which any class can implement. Since our “main” servlet is a good place to do this stuff, we’ll make it implement that interface:

public class ToDoServlet extends HttpServlet
    implements ServletContextListener {

When a class implements ServletContextListener, it must contain two methods: contextInitialized, which performs application initialization tasks, and contextDestroyed, which performs application cleanup:

  public void contextInitialized(ServletContextEvent sce) {
    ServletContext sc = sce.getServletContext();
    sc.setAttribute("toDoList",
        new ToDoList(sc.getInitParameter("jdbcDriver"),
                     sc.getInitParameter("jdbcConnectionString")));
  }

  public void contextDestroyed(ServletContextEvent sce) {
  }

All the action here is in contextInitialized, since we don’t need to do anything special when the application shuts down. As you can see, both of these methods receive a ServletContextEvent when they are called, from which we can get a reference to the ServletContext. The ServletContext is an object that represents the Web application as a whole–it is the context in which the servlet is running. All servlets and JSPs have access to the ServletContext; therefore, it provides the perfect place to store our model!

The ServletContext lets you set and get attributes, which are simply application-wide values associated with string labels. We will store our model, a new ToDoList object, in an attribute labelled "toDoList".

Before we move on, have a close look at the code that creates the ToDoList:

        new ToDoList(sc.getInitParameter("jdbcDriver"),
                     sc.getInitParameter("jdbcConnectionString")));

As we have done previously, we’re getting the JDBC driver and connection string values required by this class from initialization parameters, but because we are performing initialization for our entire application, the initialization parameters for the ToDoServlet are not yet available! That’s why we’re calling the getInitParameter method of the ServletContext object, not the servlet itself as we are used to doing. These are known as context initialization parameters, as opposed to servlet initialization parameters.

So we’ve got a ServletContextListener that’s ready to initialize our application, as long as it gets the context initialization parameters it needs. As you may have guessed, we need to add some things into our web.xml file to make this work:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
  <display-name>ToDoMVC</display-name>
  <listener>
    <listener-class>com.sitepoint.ToDoServlet</listener-class>
  </listener>
  <context-param>
    <description>The JDBC driver class.</description>
    <param-name>jdbcDriver</param-name>
    <param-value>com.mysql.jdbc.Driver</param-value>
  </context-param>
  <context-param>
    <description>The JDBC connection string.</description>
    <param-name>jdbcConnectionString</param-name>
    <param-value>jdbc:mysql://localhost/todo?user=root&amp;password=password</param-value>
  </context-param>
</web-app>

The <listener> tag lets the application know about the context listener we have provided, while the <context-param> tags set up our application-wide context initialization parameters.

Now, before we got caught up in all this application initialization stuff, I seem to remember we were writing a servlet. So let’s take a look at the doGet and doPost methods:

  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    ToDoList toDoList = (ToDoList)getServletContext().getAttribute("toDoList");
    List toDoItems = toDoList.getToDoItems();
    request.setAttribute("toDoItems", toDoItems);
  
    RequestDispatcher view = request.getRequestDispatcher("/todo.jsp");
    view.forward(request, response);
  }

  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    doGet(request, response);
  }

Look at that! A servlet with no embedded HTML code! How is such a thing possible? Well remember, this servlet is merely the controller in our MVC design–after getting things done with the model, it hands the request to the view to display the actual Web page. Looking closely at the doGet method, you’ll see this is exactly what it does:

    ToDoList toDoList = (ToDoList)getServletContext().getAttribute("toDoList");
    List toDoItems = toDoList.getToDoItems();
    request.setAttribute("toDoItems", toDoItems);

Here it fetches the model (the ToDoList stored in a servlet context attribute), uses it to get a List of the to-do list items, and then stores that List someplace where the view will be able to find it–namely, in the request object. It turns out that the request object supports attributes too, and this is the perfect way to pass values to the view that it will need to build the page.

With the heavy lifting done, we’re ready to pass the request to the view (in this case, a JSP called todo.jsp in the root of our application):

    RequestDispatcher view = request.getRequestDispatcher("/todo.jsp");
    view.forward(request, response);

At a glance, this may seem a bit bamboozling, but this is the formula we will always use to forward requests from a servlet to a JSP. We get a RequestDispatcher object for the location we want to forward to (in this case, "/todo.jsp" specifies the todo.jsp file in the root of the application), then we call its forward method, passing it the request and response objects it will need to do the job.

Okay, so I’ve had to yammer on a bit to explain all the new stuff that’s going on in this servlet, but if you step back and look at the code, you’ll find it’s actually pretty neat and tidy. And since they aren’t burdened with all the application setup stuff, our other two servlets are even neater and tidier!

package com.sitepoint;

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

public class AddToDoServlet extends HttpServlet {
  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    String newToDo = request.getParameter("newtodo");
    if (newToDo != null) {
      ToDoList toDoList = (ToDoList)getServletContext().getAttribute("toDoList");
      toDoList.addItem(newToDo);
    }
    response.sendRedirect("index.html");
  }
}
package com.sitepoint;

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

public class DeleteToDoServlet extends HttpServlet {
  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    String deleteid = request.getParameter("deleteid");
    if (deleteid != null) {
      try {
        ToDoList toDoList = (ToDoList)getServletContext().getAttribute("toDoList");
        toDoList.deleteItem(Integer.parseInt(deleteid));
      }
      catch (NumberFormatException e) {
        throw new ServletException("Bad deleteid value submitted.", e);
      }
    }
    response.sendRedirect("index.html");
  }
}

You might be surprised to note that these two servlets don’t actually forward to a JSP after they’ve done their work. Instead, they redirect the browser to "index.html", which we’ll shortly configure to point to our main ToDoServlet.

Because we want to show the user the updated to-do list after adding or deleting an item, we could forward the request to /todo.jsp, but, as I have explained in the past, this would lead to problems if the user were to refresh the browser afterward. The browser would re-submit the item addition/deletion request, causing errors or duplicate list entries. Redirecting instead of forwarding gets around this by having the browser make a new request, which is safe to repeat, after it has submitted the form.

To polish off our servlets, we need to assign URLs to each of them in our web.xml file. We’ll use /index.html for the ToDoServlet, since we want it to come up by default when a user loads our application directory. AddToDoServlet and DeleteToDoServlet will get /AddItem.do and /DeleteItem.do respectively, following the convention of giving “action” URLs .do extensions in Java Web apps.

  <servlet>
    <servlet-name>todoservlet</servlet-name>
    <servlet-class>com.sitepoint.ToDoServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>todoservlet</servlet-name>
    <url-pattern>/index.html</url-pattern>
  </servlet-mapping>
  <servlet>
    <servlet-name>addtodoservlet</servlet-name>
    <servlet-class>com.sitepoint.AddToDoServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>addtodoservlet</servlet-name>
    <url-pattern>/AddItem.do</url-pattern>
  </servlet-mapping>
  <servlet>
    <servlet-name>deletetodoservlet</servlet-name>
    <servlet-class>com.sitepoint.DeleteToDoServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>deletetodoservlet</servlet-name>
    <url-pattern>/DeleteItem.do</url-pattern>
  </servlet-mapping>

With our servlets done, all that’s left is to build the view for our application. For this example, all we need is a single JSP file: todo.jsp.

Now that we’ve pulled all our initialization and request processing code out into servlets, our JSP file looks a lot more reasonable:

<%@ page import="java.util.*,com.sitepoint.*,uk.co.anyware.html.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>To-Do List</title>
  <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
  <link rel="stylesheet" type="text/css" href="<%= request.getContextPath() %>/styles.css" />
</head>
<body>
  <%
  List toDoItems = (List)request.getAttribute("toDoItems");
  Iterator it = toDoItems.iterator();
  if (it.hasNext()) {
    %>
    <form action="DeleteItem.do" method="post">
      <select name="deleteid" size="<%= Math.max(2, toDoItems.size()) %>">
        <%
        while (it.hasNext()) {
          ToDoItem toDoItem = (ToDoItem) it.next();
          %>
          <option value="<%= toDoItem.getId() %>"><%= HTMLEscaper.escape(toDoItem.toString()) %></option>
          <%
        }
        %>
      </select>
      <input type="submit" value="Delete Selected Item" />
    </form>
    <%
  }
  %>
  <form action="AddItem.do" method="post">
    <input type="text" name="newtodo" />
    <input type="submit" value="Add New Item" />
  </form>
</body>
</html>

If you understood the JSP we built last time, this one should be a piece of cake! The scriptlet code simply pulls the List out of the request‘s "toDoItems" attribute to produce the list of to-do items, and our two forms now submit to the .do URLs we assigned to the corresponding servlets. Beyond those changes, it’s the same old JSP with a lot less Java code.

And there we have it: an MVC Java Web application! Here’s the directory structure:

/todo.jsp
/styles.css
/WEB-INF/web.xml
/WEB-INF/classes/com/sitepoint/AddToDoServlet.class
/WEB-INF/classes/com/sitepoint/AddToDoServlet.java
/WEB-INF/classes/com/sitepoint/DeleteToDoServlet.class
/WEB-INF/classes/com/sitepoint/DeleteToDoServlet.java
/WEB-INF/classes/com/sitepoint/ToDoItem.class
/WEB-INF/classes/com/sitepoint/ToDoItem.java
/WEB-INF/classes/com/sitepoint/ToDoList.class
/WEB-INF/classes/com/sitepoint/ToDoList.java
/WEB-INF/classes/com/sitepoint/ToDoServlet.class
/WEB-INF/classes/com/sitepoint/ToDoServlet.java
/WEB-INF/classes/uk/co/anyware/html/HTMLEscaper.class
/WEB-INF/classes/uk/co/anyware/html/HTMLEscaper.java
/WEB-INF/lib/mysql-connector-java-version-bin.jar

Download the code (250KB)

This application is starting to look pretty darned good, even if I do say so myself. Some of the code can be a bit of a pain to type by hand, but with a good Java IDE to write the boilerplate code for you (with a major new version out just last month, NetBeans is a popular free choice), a lot of the drudgery goes away.

The major weakness of the application at this stage continues to be the Java scriptlet code in the JSP. We’ve gotten rid of most of the Java code from this file, but what’s in there would still be pretty confusing for a Web designer. The next step in the grand tour will be to look at some of the features of modern JSP that allow us to dispense with these scriptlets in favour of tags and expressions.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.hostingheadlines.com hostingheadlines

    You might want to use a MVC framework like Struts (http://struts.apache.org/) or Spring (http://www.springframework.org/) rather than write all this code yourself. Personally I prefer Spring, but Struts is older and probably used more often.

  • andre

    hmm i think the Struts tutorial thing is coming up and this article was just the precursor to it :)

  • http://www.sitepoint.com/ Kevin Yank

    Getting there, hostingheadlines, getting there… This is the “Grand Tour”, which means we start with the simple DIY solutions and eventually end up at the powerful frameworks that do it all for you. :)

  • Steve

    Very nice.

  • http://www.functionflow.com Geof Harries

    This hurts my web designer brain :)

    I look forward to your next post; JSF I assume?

    geof

  • Tom

    I really like the fact that you included redirect-after-post. A very understandable article (as we are used to).

  • Daryl

    I have been doing J2EE development for some time now, and to be honest I’ve never fully understood it until reading these current Grand tour articles… Fantastic! I can’t wait to read the next installment.

  • William Lee

    Great! I’ve learned something here!

  • Anonymous

    ToDoList.java:54: clone() has protected access in java.lang.Object
    return (List)list.clone();
    ^
    1 error

  • http://www.sitepoint.com/ Kevin Yank

    Sorry about that. You need to redeclare list as an ArrayList:

    public class ToDoList {
    private String jdbcConnectionString;
    private ArrayList list = new ArrayList(); // *** Not just a List
    private boolean staleList = true;
    private Connection conn;

    Once it’s declared as a full ArrayList (not just an ArrayList declared as a List), you can call its clone() method.