The Hidden Gem of Web Frameworks

Tutorial – Stripes: a lean, mean Java web framework

FredDaoud
stripes

Fred Daoud introduces us to the Java Stripes Web Framework. Low-key but packs a punch.

Stripes is a real hidden gem of a web framework. It is relatively low-key but those who use it, swear by it. And for good reason–it is simple, effective, and tightly focused on doing a few things and doing them really, really well. This gives you a framework that does a lot more for you than you have to do for it, while staying out of your way and letting you do things the way you want to do them. It also lets you pick your favorite tools for the other layers involved in developing a complete web application, such as persistence, security, and a jazzy user interface.

If you’ve never heard of Stripes, you might wonder if it is being used in real-world applications. It certainly is. Just one example is Modernizing Medicine where my colleagues and I use Stripes to develop a powerful Electronic Medical Assistant for Dermatologists. Daniel Cane, President and CEO of Modernizing Medicine, states: “HOW I heard about Stripes, I have no idea. But, once I did, I was immediately hooked: the ease of binding and validation, and elegance of manipulation of forms, the power of localization, and a solid community of people willing to help. When looking at the Stripes book, the tag line “…and Java Web Development is Fun Again” sold me on the platform. I have been so happy with this framework, and each week I learn something new.”

Show me some code

Intrigued yet? Let’s look at some code. Say you are showing a list of products from your inventory and you want to create a link that goes to the detail page for each product. The code in your page might look something like this:  

 

 <%-- sample01.jsp --%>
  <ul>
    <c:forEach var="${products}" item="product">
      <li>
       <stripes:link beanclass="com.example.ProductActionBean" event="showDetail">
          <stripes:param name="productId" value="${product.id}"/>
          ${product.name}
       </stripes:link>
      </li>
    </c:forEach>
  </ul>

 

Things to take away from this example:

  •  Stripes uses JSP for templating. It is easy to use, has great support in your IDE, has an enormous user base, copious amounts of third-party libraries and support, and is documented by a ton of reference pages, articles, books, and blog posts.
  • Stripes does not reinvent anything that is already available in standard JSP, such as the forEach tag.
  • Stripes tags are straightforward. The link clearly shows that it targets the com.jaxenter.stripes.action.ProductActionBean class and the showDetail event. You know exactly where to look next. Also, if you rename ProductActionBean or change its package, it’s a plain search to find where it is referenced in JSPs. Finally, adding a productId parameter to the link is easily (and cleanly) done as well.

Let’s take a peek at the Java side of things:

 

  /* ProductActionBean.java */
  package com.jaxenter.stripes.action;
  
  import net.sourceforge.stripes.action.ForwardResolution;
  import net.sourceforge.stripes.action.Resolution;
  import com.jaxenter.stripes.model.Product;
  
  public class ProductActionBean extends BaseActionBean {
    private Integer productId;
    private Product product;
  
    public Resolution showDetail() {
      product = loadProduct(productId);
      return new ForwardResolution("/WEB-INF/jsp/productDetails.jsp");
    }
  
    public Product getProduct() {
      return product;
    }
  
    public Integer getProductId() {
      return productId;
    }
    public void setProductId(Integer productId) {
      this.productId = productId;
    }
  }

 

Now we have a few more takeaways:

  • The class includes a method named showDetail, which matches the value of the event=”showDetail” attribute in the JSP. This method will automatically be called when the user clicks on the link.
  • The class also includes a productId property. Its value will be set to the product ID associated with the link, because we have <stripes:param name=”productId”> tag in the body of the link.
  • Although all parameter values arriving over HTTP are Strings, we can use Integer for our productId property. Stripes will automatically do the type conversion, and also supports Date, BigDecimal, BigInteger, and all primitive and wrapper types, out-of-the-box. It’s easy to write type conversions for your own types, as well.
  • The result returned by the showDetail method is a Resolution, which defines what happens next. Here, we are using ForwardResolution which clearly shows that we are forwarding to productDetail.jsp. The information is all there, no need to search in a separate file to figure out what the “success” string means!
  • Stripes uses Action Beans, which are so named because they combine actions with JavaBean properties. Here, the action is showDetail, and the bean is the inclusion of the productId and product properties.

In productDetail.jsp, we can easily show the details of the selected product by taking advantage of the automatic presence of the action bean within the JSP, under actionBean, courtesy of Stripes: 

 <%-- productDetails.jsp --%>
  <h1>Product details</h1>
  <ul>
    <li>Name: ${actionBean.product.name}</li>
    <li>Description: ${actionBean.product.description}</li>
    <li>Price: ${actionBean.product.price}</li>
  </ul>
 

Knowing that the current action bean is always available in the JSP under {actionBean} makes it easy to transfer data to the view: just add a getter method in the action bean class for the information that you want to access.

More about action beans

Action beans are the workhorses of a Stripes application. These classes are where you receive input and handle events triggered by the user clicking on links or submitting forms. One feature that makes things easy for you, the developer, is, that action beans are thread-safe. A fresh instance is created to handle each request, so you are free to use instance variables without worry. The next thing to know about action beans is that you handle events with methods of the following signature:

 

 public Resolution eventName()

 

The method name determines the name of the event that it handles. We already saw how to indicate the action bean class and event name in a link:  

 

 <stripes:link beanclass="..." event="...">

 

We can do the same for a form just as easily:  

 

 <stripes:form beanclass="...">
    <stripes:submit name="save" value="Save Data"/>
    <stripes:submit name="cancel" value="Cancel everything"/>
  </stripes:form>

 

The name in each <stripes:submit> tag indicates which event handler method to call in the action bean (the value is the button label to show in the browser). This makes it really easy to handle multiple submit buttons in a form! Just write an event handler method for each button:  

 

 public Resolution save() {
    // ... 
  }
  public Resolution cancel() {
    // ... 
  }

 

 

You can have a default event handler in an action bean in two ways: first, if it is the only event handler method in the action bean, it is automatically the default. Second, you can designate an event handler method as the default by annotating it:  

 

@DefaultHandler
  public Resolution view() {
    // ... 
  }
  public Resolution cancel() {
    // ... 
  }

 

 

After you have handled an event, you need to tell Stripes where to go next. You do this with a Resolution. Let’s talk about those now.

Where to next? Resolutions

In Stripes, Resolution is a simple interface to tell Stripes how to respond to the browser after handling a request. The interface has just one method:  

 

void execute(HttpServletRequest request, HttpServletResponse response);

 

As you can see, it is simple and lets you do just about anything in order to send a response to the browser. Of course, Stripes has some built-in implementations for common responses:

  • ForwardResolution forwards to a template, typically a JSP
  • RedirectResolution sends a redirect to the browser, typically to another action bean
  • StreamingResolution makes it easy to send a binary file as a response, such as a PDF
  • JavaScriptResolution sends a Java object in JavaScript, which is very useful when using Ajax
  • ErrorResolution sends an HTTP error, with error code and descriptive message

One of these will suit your needs most of the time, but you can extend them or write your own from scratch to get any customized behavior that you require. The two most commonly used resolutions are ForwardResolution and RedirectResolution:

 

  // Forwards to a JSP
  return new ForwardResolution("/WEB-INF/jsp/productDetails.jsp");
  
  // Redirects to another action bean and its default event handler
  return new RedirectResolution(AnotherActionBean.class);
  
  // Redirects to another action bean and a specific event handler
  return new RedirectResolution(AnotherActionBean.class, "showDetails");
  
  // Redirects to another action bean and a specific event handler, 
  // with request parameters
  return new RedirectResolution(AnotherActionBean.class, "showDetails")
    .addParameter("productId", 42).addParameter("showDescription", true);

 

Resolutions are expressive and let you know right then and there, exactly what the next step is in responding to the browser.

   

Binding: the superfeature

Many a Stripes user, including myself, feel that the most powerful feature in Stripes is its binding of request parameters to action bean properties. We’ve looked at that feature briefly; now let’s dig deeper. Suppose you have a model object that has properties which are, in turn, other model objects, such as a Person with an Address

 

  /* Person.java */ 
  package com.jaxenter.stripes.model;
  
  public class Person {
    private String firstName;
    private Address address;
  
    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
      this.firstName = firstName;
    }
    public Address getAddress() {
      return address;
    }
    public void setAddress(Address address) {
      this.address = address;
    }
  }
  /* Address.java */
  package com.jaxenter.stripes.model;
  
  public class Address {
    private String streetName;
  
    public String getStreetName() {
      return streetName;
    }
    public void setStreetName(String streetName) {
      this.streetName = streetName;
    }
  }

 

You’d like to enable the user to create a person with an address using an HTML form. With Stripes, it would look something like this – first, you’d have an action bean with a Person property:  

 

  /* PersonActionBean.java */
  package com.jaxenter.stripes.action;
  
  import net.sourceforge.stripes.action.DefaultHandler;
  import net.sourceforge.stripes.action.ForwardResolution;
  import net.sourceforge.stripes.action.Resolution;
  import com.jaxenter.stripes.model.Person;
  
  public class PersonActionBean extends BaseActionBean {
    private Person person;
  
    @DefaultHandler
    public Resolution view() {
      return new ForwardResolution("/WEB-INF/jsp/personForm.jsp");
    }
  
    public Person getPerson() {
      return person;
    }
    public void setPerson(Person person) {
      this.person = person;
    }
  }

Notice that we also have a default event handler that forwards to personForm.jsp; this is where we create the HTML form: 

 <%-- personForm.jsp --%>
  <stripes:form beanclass="com.jaxenter.stripes.action.PersonActionBean">
    <div>
      First name:
      <stripes:text name="person.firstName"/>
    </div>
    <div>
      Street name:
      <stripes:text name="person.address.streetName"/>
    </div>
    <div>
      <stripes:submit name="save" value="Save"/>
    </div>
  </stripes:form>

See how simple and straightforward that is? We use the <stripes:form> tag and indicate the target action bean class name. Then we create text inputs with the name attribute that corresponds to the target property: person.firstName and person.address.streetName. When the user submits the form, Stripes will automatically call getPerson().setFirstName(<value>) and getPerson().getAddress().setStreetName(<value>) on the action bean to populate the person object and its nested properties.

It gets better: you might think that you’ll get a NullPointerException because we never created a new Person() object. Never fear! Stripes does it for you, even on nested properties. When it gets null from a getter when binding request parameters to properties, Stripes creates a new object and sets it. So you can just add properties and let Stripes take care of the housekeeping. Finally, we just need an event handler for the submit button, which is named save:

 

 public Resolution save() {
    // save the person object... 
  }

 

By the time the save() method is called by Stripes, the parameters have been bound to the action bean properties, so we can count on the person object to be created, populated, and ready to be saved. Well, that is not entirely true: for that to work, the user has to fill in the form, rather than submit a blank form. We can easily address this with validation.

Validation: easy and awesome

Stripes offers a simple way to perform validation on user input: annotations on action bean properties, and custom validation methods. Continuing our Person form example; say we want to make sure that the user enters values in both the person.firstName and person.address.streetName fields. We use annotations on the person property in the action bean:  

 

 /* PersonValidatedActionBean.java */
  package com.jaxenter.stripes.action;
  
  import net.sourceforge.stripes.action.DefaultHandler;
  import net.sourceforge.stripes.action.DontValidate;
  import net.sourceforge.stripes.action.ForwardResolution;
  import net.sourceforge.stripes.action.Resolution;
  import net.sourceforge.stripes.validation.Validate;
  import net.sourceforge.stripes.validation.ValidateNestedProperties;
  import com.jaxenter.stripes.model.Person;
  
  public class PersonValidatedActionBean extends BaseActionBean {
    @ValidateNestedProperties({
      @Validate(field="firstName", required=true),
      @Validate(field="address.streetName", required=true)
    })
    private Person person;
  
    @DefaultHandler
    @DontValidate
    public Resolution view() {
      return new ForwardResolution("/WEB-INF/jsp/personValidatedForm.jsp");
    }
  
    public Resolution save() {
      // save the person object... 
      return new ForwardResolution("/WEB-INF/jsp/personSaved.jsp");
    }
  
    public Person getPerson() {
      return person;
    }
    public void setPerson(Person person) {
      this.person = person;
    }
  }

 

The @Validate(required=true) annotation marks a property as a required field. We can place this annotation directly on an action bean property if we are submitting a value directly to that property. In our example, we’re not doing that; we’re submitting values to nested properties of the person property. In that case, we need to place our @Validate annotations inside a @ValidateNestedProperties annotation, and use the field attribute to indicate which nested property we are validating. Notice that we also added @DontValidate to the view() method so that the validations are not enforced when the user first arrives to the action bean–they haven’t had a chance to fill in the form at that point.

Now, with these validations, a blank form will not cause our save event handler to be called. Instead, Stripes will catch the validation errors and redisplay the form instead, with error messages at the location where we add the <stripes:errors/> tag:

 

  <%-- personValidatedForm.jsp --%>
  <stripes:form beanclass="com.jaxenter.stripes.action.PersonValidatedActionBean">
    <stripes:errors/>
    <div>
      First name:
      <stripes:text name="person.firstName"/>
    </div>
    <div>
      Street name:
      <stripes:text name="person.address.streetName"/>
    </div>
    <div>
      <stripes:submit name="save" value="Save"/>
    </div>
  </stripes:form>

 

Submitting a blank form will result in a page as shown in the figure below (Fig. 1).

 

What’s really nice is that if the user fills in one of the fields, but not the other, the page will be redisplayed with the error message concerning the required field that was left blank, but the field that was filled in will automatically be repopulated. Notice that we don’t have to do anything in the <stripes:text> tag for that to happen. This keeps your JSP code clean and tight. Stripes comes with the following built-in validations:  

 

  • required field
  • minimum and maximum length
  • minimum and maximum value, for numerical input
  • matching a regular expression mask
  • evaluation of a JSP expression

Writing a custom validation method is a breeze, too. Just write a method annotated with @ValidationMethod. For example:

 

@ValidationMethod
  public void validateSomething(ValidationErrors errors) {
    if (!password.equals(confirmPassword)) {
      errors.add(new SimpleError("The passwords do not match."));
    }
  }

 

By adding an error to the errors list, we cause Stripes to return to the form instead of calling the event handler, just like when a built-in validation fails.

Finally, note that Stripes only calls the validation method if the built-in validations have passed. Again, this keeps your code clean and tight, because for example you don’t have to do null checks on the password and confirmPassword if you made them required fields. If the required validation fails, the custom validation method will not be called. The error messages concerning the fields being required will be shown instead.

   

More about forms and binding

I said that parameter binding was one of Stripes’ strongest features. Combined with Stripes’ form tags, it’s a powerful one-two punch. So let’s explore that further. Suppose you have an enum, Gender, as follows:

 

package com.jaxenter.stripes.model;
  
  public enum Gender {
    FEMALE("Female"),
    MALE("Male");
  
    private String description;
  
    private Gender(String description) {
      this.description = description;
    }
  
    public String getDescription() {
      return description;
    }
  }

 

 

Here is how you could create a form to select one gender with radio buttons, any number of genders with checkboxes, and one gender with a select box:  

 

  <%-- formControls.jsp --%>
  <stripes:form beanclass="com.jaxenter.stripes.action.FormControlsActionBean">
    <div>
      Radio buttons:
      <c:forEach var="gender"
        items="<%= com.jaxenter.stripes.model.Gender.values() %>">
        <stripes:radio name="radioChoice" value="${gender}"/>${gender.description}
      </c:forEach>
    </div>
    <div>
      Checkboxes:
      <c:forEach var="gender"
        items="<%= com.jaxenter.stripes.model.Gender.values() %>">
        <stripes:checkbox name="checkboxChoices" value="${gender}"/>
        ${gender.description}
      </c:forEach>
    </div>
    <div>
      Select box:
      <stripes:select name="selectChoice">
        <stripes:option value="">Select...</stripes:option>
        <stripes:options-enumeration enum="com.jaxenter.stripes.model.Gender"
          label="description"/>
      </stripes:select>
    </div>
    <div><stripes:submit name="view" value="Send"/></div>
  </stripes:form>

 

As you can see, the Stripes tag library makes it easy to create forms and use form controls. Remember that the form controls automatically repopulate from previous values. Also, Stripes will automatically handle the conversion between String and our Gender enum type. Finally, notice the <stripes:options-enumeration> tag that takes the class name of an enum and automatically creates a list of options from its values. It renders the enumeration’s constant by default, but we can also use another property as we have done here by specifying the label attribute. Stripes also has a <stripes:options-collection> and a <stripes:options-map> tag to render a list of options from any Collection or Map, respectively.

One more very nice feature is that we can include a “Select…” option as the first in the list to prompt the user to select an option, instead of selecting one by default. By doing this, we can make sure that the user makes a selection. Indeed, because the value of the “Select…” option is a blank string, the corresponding property on the action bean will be null if the user does not make a selection. Using @Validate(required=true), we can enforce the requirement of selecting an option. This is much cleaner than using -1 or some other magic value and then manually checking for that value as meaning “no selection was made”! We are ready to look at the action bean:  

 

  package com.jaxenter.stripes.action;
  
  import java.util.List;
  import net.sourceforge.stripes.action.ForwardResolution;
  import net.sourceforge.stripes.action.Resolution;
  import com.jaxenter.stripes.model.Gender;
  
  public class FormControlsActionBean extends BaseActionBean {
    private Gender radioChoice;
    private List<Gender> checkboxChoices;
    private Gender selectChoice;
  
    public Resolution view() {
      return new ForwardResolution("/WEB-INF/jsp/formControls.jsp");
    }
  
    public Gender getRadioChoice() {
      return radioChoice;
    }
    public void setRadioChoice(Gender radioChoice) {
      this.radioChoice = radioChoice;
    }
    public List<Gender> getCheckboxChoices() {
      return checkboxChoices;
    }
    public void setCheckboxChoices(List<Gender> checkboxChoices) {
      this.checkboxChoices = checkboxChoices;
    }
    public Gender getSelectChoice() {
      return selectChoice;
    }
    public void setSelectChoice(Gender selectChoice) {
      this.selectChoice = selectChoice;
    }
  }

 

The properties correspond to the name attributes that we have in the JSP. As I mentioned, Stripes converts from the incoming String to the corresponding Gender. What’s more, for the checkboxes, we have a List<Gender> property. This list will automatically contain the genders that the user has selected. We don’t even have to create a new ArrayList; Stripes will do that for us, too!

Feature-packed, but without excess

Stripes offers many useful features while staying focused on its raison d’être: it is a server-side web framework, not a full-stack framework nor a client-side graphical user interface. Its philosophy is that there are many excellent solutions for those tiers of an application, so it does not reinvent the wheel and does not prevent you from choosing your favorite solution for things like persistence, JavaScript widgets, and so on. Here is a summary of more features offered by Stripes:

  • Automatic discovery of your action beans simply by mentioning, once and for all, the root package of your action beans. No need to adjust configuration every time you add, modify, or remove an action bean.
  • Similarly, automatic discovery of your extensions: these are custom type converters, formatters, and other Stripes artifacts that you extend or implement to suit your needs.
  • A layout system that is both simple and powerful. The recurring theme of not requiring configuration holds here as well.
  • Annotations to easily create wizards, forms that span multiple pages.
  • Interceptors, which allow you to tap into the Stripes lifecycle and do virtually anything to customize the framework to meet your requirements.
  • Exception handling to define, in one place, how to handle exceptions, from specific to general.
  • Testing facilities so that you can write automated tests for your Stripes web application.
  • Localization to easily make your application available in multiple languages.

Conclusion

Stripes strikes a perfect balance of powerful features, simplicity, and tight focus to give you a framework that does a lot for you without getting in your way nor suffering from feature bloat. It is easy to learn and intuitive, so you quickly become productive. It is customizable and extendable, so you can tweak it to suit your more advanced or particular requirements. Last but not least, it has one of the friendliest communities in the open source framework space, so if you need help, write a message to the mailing list.

Author Bio

Fred Daoud is the author of Stripes and Java Web Development is Fun Again and currently uses Stripes at Modernizing Medicine, Inc. He has been using Java since 1997 and loves web frameworks. He also likes using other JVM-based languages such as Clojure, JRuby, and Groovy.

 

This article previously appears in Java Tech Journal – Java Web Frameworks. Find more articles on web frameworks here.

Author
FredDaoud
Fred Daoud is the author of Stripes and Java Web Development is Fun Again and currently uses Stripes at Modernizing Medicine, Inc. He has been using Java since 1997 and loves web frameworks. He also likes using other JVM-based languages such as Clojure, JRuby, and Groovy.
Comments
comments powered by Disqus