Skip to main content

Comparing webapp frameworks : Stripes

Posted by simongbrown on March 10, 2006 at 6:06 AM PST

Stripes is a relatively new web application framework that's been built with a couple of things in mind - simplicity and the adoption of new technology. In Stripes, you won't find any complex XML configuration files and, refreshingly, the distribution only requires/includes a small number of dependent JAR files. Stripes is a genuinely lightweight framework and uses Java 5 annotations as it's configuration mechanism.

Like other webapp frameworks, Stripes sticks to the typical model 2 architecture, implemented using the command and controller strategy where the main front controller component delegates request processing responsibility to an action class. However, unlike most other frameworks, you aren't restricted to implementing a specific execute() method and each action class can provide implementations for more than a single request. Let's see how this looks with an example. I'm using Stripes 1.2.2.

Home page

To recap, the home page presents the user with a list of the most recent blog entries and it's the piece of code that locates those blog entries that is a candidate for moving into an action. We've seen this already with the Struts implementation, so how does this look in Stripes?

package action;

import domain.*;
import net.sourceforge.stripes.action.*;

/**
* A Stripes ActionBean that is responsible for allowing the
* user to view all recent blog entries.
*
* @author    Simon Brown
*/
@UrlBinding("/viewBlogEntries.action")
public class ViewBlogEntriesActionBean implements ActionBean {

  private ActionBeanContext context;

  public ActionBeanContext getContext() {
    return context;
  }

  public void setContext(ActionBeanContext context) {
    this.context = context;
  }

  @DefaultHandler
  @DontValidate
  public Resolution viewBlogEntries() {
    BlogService blogService = new BlogService();
    Blog blog = blogService.getBlog();
    context.getRequest().setAttribute("blog", blog);

    return new ForwardResolution("/viewBlogEntries.jsp");
  }

}

First up, we need to implement an interface called ActionBean. From the Javadoc...

Interface for all classes that respond to user interface events. Implementations receive information about the event (usually a form submission) in two ways. The first is through a ActionBeanContext object which will always be set on the ActionBean prior to any user implemented "handler" methods being invoked. The second is through the setting of JavaBean style properties on the object (and nested JavaBeans). Values submitted in an HTML Form will be bound to a property on the object if such a property is defined. The ActionBeanContext is used primarily to provide access to Servlet APIs such as the request and response, and other information generated during the pre-processing of the request.

In summary, it's a simple interface with a pair of methods to get and set an ActionBeanContext instance, which in turn provides access to other classes including the servlet request and response. This needs to be implemented by any class that would like to respond to web requests although, unlike most other webapp frameworks, this interface doesn't provide a contract for how the request will be serviced by this class. Instead, Stripes allows you to mark any arbitrary method as a request handler by using an annotation. Here, the viewBlogEntries method has been annotated with @DefaultHandler, signalling to Stripes that it's this method that should be called to process requests sent to this class. Something worth mentioning here is that more than a single method on any ActionBean implementation can be marked as a request handler, and this is achieved by using the @HandlesEvent("...") annotation (not shown here). The benefit of this approach is that it's easy to group all related request handling logic together in a single class, yet split the implementations. For example, given a form with three submit buttons, you can have one handler for all of them, or one for each. The choice is yours.

So how do requests get to this class? Well, in Stripes you map a URL to an ActionBean implementation using the @UrlBinding(...) annotation. The only other thing you need to do is declare the Stripes filter and dispatcher servlet in the web.xml file, as follows.

  <filter>
    <display-name>Stripes Filter</display-name>
    <filter-name>StripesFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>StripesFilter</filter-name>
    <url-pattern>*.jsp</url-pattern>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>

  <filter-mapping>
    <filter-name>StripesFilter</filter-name>
    <servlet-name>StripesDispatcher</servlet-name>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>

  <servlet>
    <servlet-name>StripesDispatcher</servlet-name>
    <servlet-class>net.sourceforge.stripes.controller.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

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

As for the actual processing, there's nothing really new here. We just find the list of blog entries and insert them into the request scope. Since we're using JSP for the view technology, we return a instance of ViewResolution that specifies the name of the JSP page that should be forwarded to. Other common Resolution implementations include RedirectResolution and StreamingResolution. As far as the JSP is concerned, it should come as no surprise that it is fairly similar to those shown before.

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="stripes" uri="http://stripes.sourceforge.net/stripes.tld" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>

<!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>${blog.name}</title>
    <link rel="stylesheet" href="screen.css" type="text/css" />
  </head>

  <body>
    <div id="container">
      <h1>${blog.name}</h1>
      <h2>${blog.description}</h2>

      <c:forEach var="blogEntry" items="${blog.blogEntries}">
        <div class="blogEntry">
          <h3>${blogEntry.title}</h3>

          <c:choose>
            <c:when test="${not empty blogEntry.excerpt}">
              ${blogEntry.excerpt}
              <p>
              <stripes:link href="/viewBlogEntry.action">
              Read more
              <stripes:link-param name="id" value="${blogEntry.id}" />
              </stripes:link>
              </p>
            </c:when>
            <c:otherwise>
              ${blogEntry.body}
            </c:otherwise>
          </c:choose>

          <p>
          Posted on <fmt:formatDate value="${blogEntry.date}"
            timeZone="${blog.timeZone}" type="both"
            dateStyle="long" timeStyle="long" />
          </p>
        </div>
      </c:forEach>
    </div>
  </body>

</html>

The only real difference between this and other JSP implementations is in the use of the stripes:link tag, which helps you build up hyperlinks containing parameters. Like other frameworks, this custom tag has lots of attributes mirroring those found on the HTML anchor tag, allowing you to pass through values. You either love these tags or hate them, but it's nice to have the choice.

Before we move on, I wanted to mention that ActionBean instances can also be used as view helpers. The example above shows an ActionBean implementation mapped to a specific URL that, when requested, causes the default handler to execute and forward the user onto the appropriate JSP. As an alternative, we could call the JSP directly and use the Stripes useActionBean tag to tell the framework that we would like some code to be executed. You can find more details about this tag in the taglib docs, but essentially it creates a new instance of an ActionBean (specified by its UrlBinding), binds any parameters and then optionally calls a named handler. Take a look at the best practices page for more information on this. Again, Stripes gives you choice.

Blog entry detail page

As you might expect, the blog entry page is similar. Here's the Java code that makes up the action.

package action;

import net.sourceforge.stripes.action.*;
import domain.BlogService;
import domain.Blog;
import domain.BlogEntry;

/**
* A Stripes ActionBean that is responsible for allowing the user
* to view a single blog entry.
*
* @author    Simon Brown
*/
@UrlBinding("/viewBlogEntry.action")
public class ViewBlogEntryActionBean implements ActionBean {

  private ActionBeanContext context;
  private String id;

  public ActionBeanContext getContext() {
    return context;
  }

  public void setContext(ActionBeanContext context) {
    this.context = context;
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  @DefaultHandler
  @DontValidate
  public Resolution viewBlogEntry() throws IOException {
    BlogService blogService = new BlogService();
    Blog blog = blogService.getBlog();

    context.getRequest().setAttribute("blog", blog);

    BlogEntry blogEntry = blog.getBlogEntry(id);
    if (blogEntry == null) {
      context.getResponse().sendError(HttpServletResponse.SC_NOT_FOUND);
      return null;
    } else {
      context.getRequest().setAttribute("blogEntry", blogEntry);
      return new ForwardResolution("/viewBlogEntry.jsp");
    }
  }

}

As with other implementations, there's a simple flow of code of that finds the blog and, if the specified blog entry is found, forwards the user to the blog entry page. However, the thing that that makes this different from many other frameworks is that any submitted request parameters are automatically mapped by Stripes onto JavaBean properties on your ActionBean implementation. In this case, an integer id property has been defined to represent the ID of the blog entry being requested, and this is available for use in the handler method.

So, moving on, you'd expect that the JSP implementation of the view blog entry page is also similar to those shown before. You'd be right, here it is.

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>

<!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>${blogEntry.title} : ${blog.name}</title>
    <link rel="stylesheet" href="screen.css" type="text/css" />
  </head>

  <body>
    <div id="container">
      <h1>${blog.name}</h1>
      <h2>${blog.description}</h2>

      <div class="blogEntry">
        <h3>${blogEntry.title}</h3>

        ${blogEntry.body}

        <p>
        Posted on <fmt:formatDate value="${blogEntry.date}"
          timeZone="${blog.timeZone}" type="both"
          dateStyle="long" timeStyle="long" />
        </p>
      </div>
    </div>
  </body>

</html>

Summary

Stripes is one of a new breed of frameworks that takes advantage of the new features provided by Java 5 and this really shows. Where other frameworks adopt verbose XML files to map HTTP requests onto classes that will process them, Stripes adopts a much simpler annotation based approach, which really seems to work. Likewise, its use of returning explicit Resolutions is a nice change from the complexities of defining return values/views in XML. In Stripes, the easy stuff is easy. I'm looking forward to revisiting it with some read/write behaviour in the web application.