The Source for Java Technology Collaboration
User: Password:



Simon Brown's Blog

March 2006 Archives


Comparing webapp frameworks : WebWork

Posted by simongbrown on March 24, 2006 at 03:02 PM | Permalink | Comments (5)

Like Struts, WebWork is a framework that is fairly established within the J2EE webapp space although it's interesting that I've only ever come across two types of WebWork users - those that have never heard of it and those that love it. WebWork, like most other frameworks, is designed around the web MVC pattern and uses the command and controller implementation strategy. What's slightly different about WebWork is that it's built on top of XWork, a separate framework that provides an implementation of the command pattern that is independent of the Servlet API.

In XWork, actions (or commands, depending on your terminology) implement the com.opensymphony.xwork.Action interface, which specifies a single zero argument method called execute(). Since this same interface is used in WebWork, it's possible to write actions without knowledge that they are even running within the context of a web application. From a testability perspective this is very desirable since it allows you to simply instantiate actions and execute them. Of course, most actions typically tend to operate upon data provided as parameters in the HTTP request and, like Stripes, WebWork actions declare their dependency on those parameters by providing JavaBeans style properties with the same names. At runtime, WebWork automatically extracts parameters from the HTTP request and injects them into your actions. While you can get access to the Servlet API inside of a WebWork action, WebWork doesn't force this upon you, leaving that choice to you as the developer.

Right, let's get on with seeing how a WebWork implementation of the sample application compares to the others. I'm using WebWork 2.1.7 and installing it is a simple matter of copying a few JAR files into the /WEB-INF/lib directory and adding the front controller component and WebWork tag library into the web.xml file as follows.

<servlet>
  <servlet-name>webwork</servlet-name>
  <servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

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

<taglib>
  <taglib-uri>webwork</taglib-uri>
  <taglib-location>/WEB-INF/lib/webwork-2.1.7.jar</taglib-location>
</taglib>

Two additional configuration files that we need are called validators.xml and xwork.xml. The first of these defines a number of validator components that can be used by the framework to validate incoming requests through an interceptor. Each action can define it's own set of specific validation rules based upon the validators configured in the validators.xml file. Since this is a readonly webapp, we don't really have any need for validation, but we'll be coming back to this when we add some user interactivity. For now, we don't need to declare any validators so the file looks as follows.

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN"
    "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd"> 

<validators> 
</validators>

The other file (xwork.xml) defines the actions that our web application will use, along with their outcomes/results and the type of view component that is responsible for rendering those outcomes. If you've read Comparing Webapp Frameworks : Struts or are familiar with Struts, you can think of this as being similar to the struts-config.xml file. Let's see this in action by diving into the code.

Home page
The home page presents the user a list of recent blog entries and with WebWork, the code that finds these blog entries get wrappeds up inside an XWork action as follows.

package action;

import com.opensymphony.xwork.ActionSupport;
import domain.Blog;
import domain.BlogService;

/**
 * Action responsible for finding blog entries, ready to be displayed.
 *
 * @author    Simon Brown
 */
public class ViewBlogEntriesAction extends ActionSupport {

  /** the blog that owns the blog entries */
  private Blog blog;

  /**
   * Performs the processing associated with this action.
   *
   * @return  a String defining the outcome of this action
   */
  public String execute() throws Exception {

    BlogService blogService = new BlogService();
    this.blog = blogService.getBlog();

    return SUCCESS;
  }

  /**
   * Gets the blog that this action is operating upon.
   *
   * @return  a Blog instance
   */
  public Blog getBlog() {
    return blog;
  }

}

This implementation is very straightforward, just looking up the Blog instance and returning the predefined SUCCESS outcome. Notice here that the Blog instance isn't inserted into the HTTP request. Instead it's assigned to an instance variable and we'll be seeing shortly how exactly this is used in the JSP page representing the view. Speaking of which, how does the SUCCESS outcome get mapped to that JSP? Here's where the xwork.xml file fits in.

<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" 
  "http://www.opensymphony.com/xwork/xwork-1.0.dtd">

<xwork>
  <include file="webwork-default.xml" />

  <package name="default" extends="webwork-default">

    <default-interceptor-ref name="defaultStack" />

    <action name="viewBlogEntries" class="action.ViewBlogEntriesAction">
        <result name="success" type="dispatcher">viewBlogEntries.jsp</result>
    </action>

  </package>
</xwork>

Here, an action called viewBlogEntries is defined (which maps onto a URI of /viewBlogEntries.action) and given the fully qualified name of the class that implements the action. In addition to this, the possible outcomes of the action are defined, mapping a simple string value onto the name of the JSP page that is responsible for rendering that particular outcome. The value of the type attribute instructs WebWork to use the Servlet request dispatcher and, as we'll see in the next blog entry, there are some other possible values that allow you to use alternative view technologies. So, what does the JSP page look like? Well it's pretty similar to the previous versions although I've chosen to show the WebWork tag library in use. It is also possible to use a pure JSP/JSTL combination.

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="webwork" prefix="ww" %>
<%@ 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><ww:property value="blog.name" /></title>
    <link rel="stylesheet" href="screen.css" type="text/css" />
  </head>

  <body>
    <div id="container">
      <h1><ww:property value="blog.name" /></h1>
      <h2><ww:property value="blog.description" /></h2>

      <ww:iterator id="blogEntry" value="blog.blogEntries">
        <div class="blogEntry">
          <h3><ww:property value="title"/></h3>

          <ww:if test="excerpt != null">
            <ww:property value="excerpt"/>
            <p>
            <a href="<ww:url value="'viewBlogEntry.action'">
              <ww:param name="'id'" value="id"/>
            </ww:url>">Read more</a>
            </p>
          </ww:if>
          <ww:else>
            <ww:property value="body"/>
          </ww:else>

          <p>
            Posted on <ww:property value="date" />
          </p>
        </div>
      </ww:iterator>
    </div>
  </body>

</html>

If you're familiar with something like the Struts bean/logic tags or JSTL, you'll probably pick up the WebWork tags pretty quickly. ww:property outputs the value of the named property, ww:if lets you perform conditional logic and ww:iterator provides a simple looping mechanism. You may be wondering exactly where the blog object comes from since we never injected it into the HTTP request or the JSP page. Behind the scenes, WebWork uses something called a Value Stack to maintain references to objects and make them available within the various supported view technologies. You can think of the value stack as a collection of objects, and one of those objects is the action that was responsible for dispatching us to the page. Accessing objects within the stack is achieved using a language called OGNL (the Object Graph Navigation Language). During the dispatching process, the action becomes the "root" node, meaning you can easily access any properties of the action with a simple object.property syntax. Something worth pointing out is that when you use the ww:iterate tag, the "current object" in the value stack changes to reflect the current object exposed during iteration, but you can get back to any object in the stack by prefixing the expression with the # character.

Blog entry detail page
At a high level, that's pretty much it so it won't surprise you that the blog entry detail page is very similar. First the action class.

package action;

import com.opensymphony.xwork.ActionSupport;
import domain.Blog;
import domain.BlogEntry;
import domain.BlogService;

/**
 * Responsible for finding a specific blog entry.
 *
 * @author    Simon Brown
 */
public class ViewBlogEntryAction extends ActionSupport {

  /** the ID of the blog entry to display */
  private String id;

  /** the blog that owns the blog entries */
  private Blog blog;

  /** the blog entry to be displayed */
  private BlogEntry blogEntry;

  /**
   * Performs the processing associated with this action.
   *
   * @return  a String defining the outcome of this action
   */
  public String execute() throws Exception {
    BlogService blogService = new BlogService();
    blog = blogService.getBlog();
    blogEntry = blog.getBlogEntry(id);

    if (blogEntry == null) {
      return "notfound";
    } else {
      return SUCCESS;
    }
  }

  /**
   * Setter for the id property.
   *
   * @param id    a String
   */
  public void setId(String id) {
    this.id = id;
  }

  /**
   * Gets the blog that this action is operating upon.
   *
   * @return  a Blog instance
   */
  public Blog getBlog() {
    return blog;
  }

  /**
   * Gets the blog entry to be displayed.
   *
   * @return  a BlogEntry instance
   */
  public BlogEntry getBlogEntry() {
    return blogEntry;
  }

}

Next up is the fragment of XML that needs to be inserted into the xwork.xml file to register the action and define its outcomes.

    <action name="viewBlogEntry" class="action.ViewBlogEntryAction">
      <result name="success" type="dispatcher">viewBlogEntry.jsp</result>
      <result name="notfound" type="dispatcher">404.jsp</result>
    </action>

And finally is the JSP page itself.

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib uri="webwork" prefix="ww" %>
<%@ 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><ww:property value="blogEntry.title" /> : <ww:property value="blog.name" /></title>
    <link rel="stylesheet" href="screen.css" type="text/css" />
  </head>

  <body>
    <div id="container">
      <h1><ww:property value="blog.name" /></h1>
      <h2><ww:property value="blog.description" /></h2>

      <div class="blogEntry">
        <h3><ww:property value="blogEntry.title" /></h3>

        <ww:property value="blogEntry.body" />

        <p>
        Posted on <ww:property value="blogEntry.date" />
        </p>
      </div>
    </div>
  </body>

</html>

Summary
Where actions in frameworks like Struts only have responsibility for the business logic associated with processing a request, in WebWork (and Stripes) those actions also act as custodians for the data used in processing the request. Incoming data on the request gets automatically mapped onto JavaBean properties on the action class, which can subsequently be accessed and displayed in the various view technologies. If you're not used to this approach, it can seem a little odd at first. However, it doesn't take very long to get used to and having both the logic and data in the same place proves very useful to support a hierarchy of similar actions. All in all, WebWork is a very sophisticated web application framework that lets you build easily testable actions and provides a way for you to use multiple view technologies. In the next comparison, we'll take a break from JSP to look at another WebWork implementation that uses Velocity.



Comparing webapp frameworks : Stripes

Posted by simongbrown on March 10, 2006 at 06:06 AM | Permalink | Comments (8)

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.



Comparing webapp frameworks : Wicket

Posted by simongbrown on March 09, 2006 at 01:53 AM | Permalink | Comments (3)

Guillermo Castro has posted a Wicket implementation of the webapp comparison that I started a while ago. It's an interesting read and the contrast with most page/request based webapp frameworks is amazing. In summary, Guillermo says:

Wicket, in my opinion, focuses the development efforts in the right place, inside plain Java code, and leaves the graphical presentation where it should be, inside html. At first you might find a little hard to grasp this paradigm shift, as so many developers are being 'forced' to rely on jstl and jsp scriptlets to accomplish logic programming for a page with all the other frameworks. But once you get used to this, I'm sure wicket will provide really fast development times.

From my perspective, the thing that I really like about Wicket is that way that you can build common components, as illustrated by the BasePage class. This is a really neat way of building in commality across many pages in terms of their content and their layout. The thing I find most interesting about the Wicket implementation is summed up by the following comment that was left on Guillermo's blog.

I've never seen a finer explanation of why Java web development takes forever.

One of the reasons that I wanted to compare frameworks with a simple read-only app is because I wanted to see whether the easy stuff was actually easy, and I've taken a lot of flak about this. From a newcomers perspective, Wicket *looks* complicated. Once I (eventually!) get a few more frameworks written up, I'll add some read/write behaviour into the application and hopefully we'll start to see some of Wicket's strengths shine through.

My thanks go to Guillermo for volunteering to do this. I have the WebWork and Stripes versions half written up, so I'll make an effort to getting these out as soon as possible. If anybody would like to volunteer to provide a similar implementation in other webapp frameworks, please get in touch and I'll e-mail you the source. If you don't have a website/blog, I'm more than happy to publish it on my blog for you.





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds