Struts to Stripes—A Road Worth Traveling

orting an existing Java Web application to a new framework is probably not at the top of most developers’ fun-stuff-to-do lists. In addition to the time of learning a new Web framework, the tedious process of converting things like tags, internationalization systems, and validation can force even the most courageous among us to think twice. I recently faced such a challenge when considering a move from Struts.

The first question in any decision about porting an application should be “Why not stay with the framework I have?” In my case, Struts was a stable, well-documented framework with a large developer community, but configuration was cumbersome and the separation of forms, actions, application flow, and validation sometimes made following a thread through an application like trying to untangle a knotted fishing line. This tendency only got worse as my Struts applications grew. Eventually, from a maintenance perspective alone, migrating to a new framework made sense.

None of the frameworks I first considered (Java ServerFaces, Tapestry, WebWorks, Spring MVC) convinced me that their potential benefit outweighed the cost of porting from Struts. Some, like JSF, were not view friendly. Others, like Tapestry and WebWorks, had per-page internationalization systems that looked cumbersome. And Spring MVC didn’t look much better than Struts from a configuration perspective. The framework I selected needed to justify the time spent learning it and the effort of actually porting the code; it had to take me to a better place?a place where code was easier to write, troubleshoot, and maintain. From that perspective, these alternatives looked more like tradeoffs than saviors.

Stripes to the Rescue!

Then I happened upon the Stripes Framework. Like many in the Java community, I had been following the Ruby on Rails (RoR) phenomenon. For me, Stripes was the closest of the Java MVC frameworks to the RoR philosophy: simple, elegant, and requiring minimal configuration. In addition to its simplicity, Stripes looked familiar to a Struts veteran like me. The application flow and many of the naming conventions were similar. Stripes’ ActionBeans were like Strut’s Actions, and ForwardResolutions looked a lot like ActionForwards. With this framework, I would not have to throw away all of my hard-earned Struts knowledge.

Something else that appealed to me was the Stripes documentation. Like the framework itself, it was clear, clean, and concise. The tag library docs and API were well documented, and just about every feature of the framework had sample code. This excellent documentation, along with the ability for me to capitalize on my existing Struts knowledge made me confident I could quickly get up to speed with the Stripes framework.

Stripes also contained features that made it a good AJAX platform, including a streaming resolution that allows for improved error handling in AJAX implementations. For me, however, the deciding factor ultimately was that I could clearly see it making my life easier. I estimated that I could reduce the total lines of code in the action/configuration/validation areas of my application by about half. Less code meant fewer bugs, faster development time, and easier troubleshooting.

The Porting Process

I started my port from the view layer and worked my way back to actions. I had no real logic to this approach; I had to start somewhere and views were as good a starting point as any.

JavaServer Pages

Stripes, like Struts, uses JSPs for its view layer. I was pleasantly surprised to find that the Stripes tag library is similar to Struts’ HTML taglib. In fact, I was able to upgrade many of my tags using the universal replace.

Stripes relies on JSTL for logic in the JSP view. I was using a mix of Struts logic tags and JSTL in my application, so I was ahead of the game. By porting all of my logic tags to JSTL, I was also able to take advantage of JSTL’s superior handling of if/else and case statements, which are either rudimentary or non-existent in the Struts logic taglib.

Internationalization

The next surprise came when I was porting my Struts’ message resources. On the configuration side, all this operation required was renaming my Struts message resource files. Inside my JSPs, I was able to replace 100 percent of my Struts message tags (e.g., ) with JSTL format tags (e.g., ) using universal replace. The JSTL format tag also supports message resource bundling available in Struts.

Forms

The most rewarding part of my port was getting rid of my Struts Action Forms, which can require extensive XML markup and tedious conversions in the Action class as the following sample shows:

									public ActionForward executeAction(ActionMapping mapping, ActionForm form,     HttpServletRequest request, HttpServletResponse response) throws Exception {            Employee employee=new Employee();        DynaValidatorForm eaf = (DynaValidatorForm) form;        employee.setFirstname(eaf.getString("firstname"));        employee.setLastname(eaf.getString("lastname"));        employee.setPhone (eaf.getString("phone"));        employee.setEmail (eaf.getString("email"));        employee.setEmployeeid ((Integer)eaf.get("employeeid"));        employee.setSocialsecurity(Long.parseLong(eaf.getString("socialsecurity")));        employee.setBirthdate(MyUtils.convertStringToDate(eaf.getString("birthdate")));        employee.setSalary(MyUtils.convertStringToBigDecimal(eaf.getString("salary")));        EmployeeDAOService.updateEmployee(employee);        return new ActionForward(mapping.getForward());    }

Stripes form handling, on the other hand, allows you to use your domain object as a form:

public class UpdateEmployeeActionBean implements ActionBean {	private ActionBeanContext context;	private Employee employee;	public ActionBeanContext getContext() {		return context;	}	public void setContext(ActionBeanContext context) {		this.context = context;	}	public void setEmployee(Employee employee) {		this.employee = employee;	}	public Employee getEmployee() {		return this.employee;	}	@DefaultHandler	public Resolution update() {		EmployeeDAOService.updateEmployee(employee);		return new ForwardResolution("/employees/updateEmployee.jsp");	}}

In most cases, I was able to embed a copy of my domain object as a property of my Stripes ActionBean class and include only the code involved in moving that object to and from my persistence layer. I threw away all of the form handling in Struts Actions, including initial configuration, casting the form to the appropriate class, and copying and converting that data to and from the domain object?about 30 percent of the code in most action classes I’ve seen. It was not needed in Stripes.

In short, embed the domain object as a property of your ActionBean class, provide it getter and setter methods, and voilà! The whole enchilada?including lists?is exposed to the form in the HTML view.

What I could do with forms I could also do with query-string parameters. I simply made those parameters a property of my ActionBean and they were automatically copied into those fields if they were part of the request.

Validation

Porting Struts validation to Stripes required more work than forms or tags. For my application, I had to rewrite validation configuration in the validation.xml file with Java 5.0 annotations inside the Stripes ActionBean class. Stripes also gives you a nice type-based validation for free. With no user configuration whatsoever, Stripes will return HTML forms to the user when they enter the wrong value type (e.g., a character in a number or date field). Forms can be returned automatically with a user-friendly message and the offending field highlighted.

Application Flow

Converting the control flow of my Struts application was probably the one place that required a break from the Struts way of thinking. In Struts, control flow?the binding of URL requests, actions, and the resulting view?is rendered in XML markup and centralized in the struts-config.xml file. This rendering outside the action layer makes Struts bindings flexible. They are not hard-coded inside the action layer and a single action can easily be coupled with different input URLs and forwards. The downside of this approach is that Struts configurations can quickly grow large and cumbersome. The separation of control flow from the action layer can also make debugging all the way through the request cycle difficult.

Stripes offers three different ways to map requests to the action layer:

  1. Explicitly bind an ActionBean to a URL using annotations
  2. Allow Stripes during startup to guess the binding of its ActionBeans based on the similarity between the ActionBean class path and the application URLs
  3. Bind a JSP to any ActionBean, or call any method of a Java class in the application class path, using the Stripes useBean tag

While the first two approaches seem somewhat hard-coded compared with Struts configuration, the useBean tag provides a lot of flexibility. With it, JSPs can access multiple ActionBeans or classes to get just what they need.

Easy Journey to the Right Destination

When choosing a new framework, the ease of the migration?both in learning the new framework and porting your existing code?are factors to consider, but they should not be given too much weight. Yes, you have made a large investment in learning an existing framework and it would be nice to retain some of that investment in your next MVC platform. And yes, it would be nice if you could port your application in weeks, not months. But no matter how easy or pleasant the journey, you should decide first if the destination is a place you want to go. For me, the ability to nearly half the amount of code in my action layer and centralize forms, configuration, and validation in one place were the major factors in my decision. The quality of Stripes’ documentation and the other stuff was just icing on the cake.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: