Skip to main content

Comparing webapp frameworks : Struts

Posted by simongbrown on January 26, 2006 at 9:48 AM PST

Struts is the grandaddy of Java webapp frameworks so it's fitting that we start our tour here. I think it's probably safe to say that Struts was the first model 2 (web MVC) framework to gain widespread adoption in the Java arena and to this day it's still used by many people.

Just to ensure everybody is up to speed, model 2/web MVC is an architectural pattern that promotes separation of concerns between the model, the view and the controller in a web environment. As we saw in the previous model 1 implementations of the sample application, each of the JSP pages contains Java code to lookup data and display it to the user. With an MVC approach, each of the components has a strict responsibility as follows.

  • Model : represents the data being viewed/manipulated.
  • View : responsible for rendering the model back to the user.
  • Controller : responsible for taking the request/user input and initialising the model, manipulating it, etc.

In reality, different people have different views as to whether the controller represents only the web specific parts of the process flow and whether real business logic in fact resides in the model (it being a model of the underlying business). What's important here is that web MVC promotes a separation of concerns through reusable and testable components, regardless of how you cut it. For more information about the model 2 architecture, take a look at Designing Web Applications and Servlet Patterns.

Home page
So what's different between the Struts version of the example application and the original model 1 version? Remember that Java code at the top of the page? Basically, that's been moved into a Java class.

package action;

import domain.Blog;
import domain.BlogService;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ViewBlogEntriesAction extends Action {

  public ActionForward execute(ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response) throws Exception {

    BlogService blogService = new BlogService();
    Blog blog = blogService.getBlog();
    request.setAttribute("blog", blog);

    return mapping.findForward("success");
  }
}

In Struts terminology, this class is called an action although you'll also see this general pattern referred to as the command pattern. At runtime, the Struts controller (a Java servlet) maps the incoming request onto an action class and calls the execute() method, which contains the (business) logic required to service the request. In the example, this logic is responsible for locating the Blog instance and making it available (via the request) so that the recent list of blog entries can be presented back to the user. As with all of the others, this example uses JSP as the view technology, although you'll notice from the code above that we don't explicitly say, "please now show JSP page X". We'll cover this shortly but for now, here's the JSP page itself.

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

  <head>
    <title><bean:write name="blog" property="name" /></title>
    <link rel="stylesheet" href="screen.css" type="text/css" />
  </head>

  <body>
    <div id="container">
      <h1><bean:write name="blog" property="name" /></h1>
      <h2><bean:write name="blog" property="description" /></h2>

      <logic:iterate id="blogEntry" name="blog" property="blogEntries" >
        <div class="blogEntry">
          <h3><bean:write name="blogEntry" property="title" /></h3>
        </div>

        <logic:notEmpty name="blogEntry" property="excerpt">
          <bean:write name="blogEntry" property="excerpt" filter="false" />
          <p>
          <html:link action="viewBlogEntry" paramId="id" paramName="blogEntry" paramProperty="id">Read more</html:link>
          </p>
        </logic:notEmpty>

        <logic:empty name="blogEntry" property="excerpt">
          <bean:write name="blogEntry" property="body" filter="false" />
        </logic:empty>

        <p>
        Posted on <bean:write name="blogEntry" property="date" format="dd MMM yyyy HH:mm:ss" />
        </p>
      </logic:iterate>
    </div>
  </body>

</html>

As you can see, moving the Java code out of the page makes for a shorter JSP that looks just like regular XHTML, albeit with a few custom tags here and there. On that note, I've chosen to use Struts tags in this version of the page purely to show something a little different to last time, but you have other alternatives including the (more feature rich) JSTL tags.

So how does a request for the home page make its way through to the ViewBlogEntriesAction class? Well, the main index.jsp page contains a simple JSP forward to /viewBlogEntries.do, which is subsequently mapped to the ViewBlogEntriesAction Java class. With Struts, there are two parts to this. First of all, the mapping between *.do and the Struts front controller servlet is defined in the web.xml file, as follows.

  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>
        org.apache.struts.action.ActionServlet
    </servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>
         /WEB-INF/struts-config.xml
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

Next, the mapping between a URL and the Java class responsible for processing that request is specified in a file called struts-config.xml that typically resides in /WEB-INF (but is configurable, as you see above). Here's the relevant part of the file that illustrates these mappings (path -> type) for the sample application.

    <action-mappings>

      <action path="/viewBlogEntries" type="action.ViewBlogEntriesAction" name="viewBlogEntries">
        <forward name="success" path="/viewBlogEntries.jsp" />
      </action>

      <action path="/viewBlogEntry" type="action.ViewBlogEntryAction" name="viewBlogEntry">
        <forward name="success" path="/viewBlogEntry.jsp" />
        <forward name="notfound" path="/404.jsp" />
      </action>

    </action-mappings>

Within each of the action mappings, you'll also notice one or forward elements and these are essentially an abstraction over the JSP that will be responsible for rendering the response. It's probably worth noting that this is just a URI, so you don't have to reference a JSP page here. If you look back to the ViewBlogEntriesAction class above, you'll see that the last line in the execute method performs mapping.findForward("success"). This says, please find the mapping called success that's defined for this action mapping and forward control to it. In this case, the viewBlogEntries.jsp page is used. This completes the picture and is the basic way that most requests in Struts are handled.

Blog entry detail page
Likewise, for the blog entry page we have an action class and a JSP page. Here's the action.

package action;

import domain.Blog;
import domain.BlogEntry;
import domain.BlogService;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ViewBlogEntryAction extends Action {

  public ActionForward execute(ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response) throws Exception {

    BlogService blogService = new BlogService();
    Blog blog = blogService.getBlog();
    request.setAttribute("blog", blog);

    BlogEntry blogEntry = blog.getBlogEntry(request.getParameter("id"));
    if (blogEntry == null) {
      return mapping.findForward("notfound");
    } else {
      request.setAttribute("blogEntry", blogEntry);
      return mapping.findForward("success");
    }
  }

}

No big surprises here except that there are two possible routes through the action depending on whether the specified blog entry is found or not. And here's the JSP page used to render the successful response.

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

  <head>
    <title><bean:write name="blogEntry" property="title" /> : <bean:write name="blog" property="name" /></title>
    <link rel="stylesheet" href="screen.css" type="text/css" />
  </head>

  <body>
    <div id="container">
      <h1><bean:write name="blog" property="name" /></h1>
      <h2><bean:write name="blog" property="description" /></h2>

      <div class="blogEntry">
        <h3><bean:write name="blogEntry" property="title" /></h3>

        <bean:write name="blogEntry" property="body" filter="false" />

        <p>
        Posted on <bean:write name="blogEntry" property="date" format="dd MMM yyyy HH:mm:ss" />
        </p>
      </div>
    </div>
  </body>

</html>

Summary
Despite its age, Struts is still one of the mostly widely used web application frameworks and one of the top choices for teams building Java EE systems. Additionally, the overall implementation strategy (called the Command and controller strategy in Java EE pattern documentation) is probably the most pervasive in both bespoke build and, as we'll be seeing, other open source alternatives to Struts.

Related Topics >>