My current client has been running their main webapplication since 2001. As with all applications, it is a product of it’s time. It was built with the best webframework then available (Struts) and with the best web development practices of the day. Since then, a team of around 50 developers has been working to maintain the site and adapt it to meet changing requirements. In a team that big, no one can keep an overview of the entire system and take responsibility for it’s architectural integrity. The pressure of deadlines did the rest.
Looking through the code now, there are a lot of problems:
- no good separation of concerns (presentation logic is mixed with business logic)
- the Action classes contain basically all code in the system, except where delegated to a Helper or Util class
- lots of duplication of functionality, across JSP pages as well as Actions
- the JSP pages are 1000+ line beasts, equal parts markup and JavaScript code.
With a major functional change coming down the line, the time has come to re-imagine this application with today’s technology. I was asked to see what the application would look like when rebuilt using JSF and Seam. This blog post describes my experiences so far.
The application allows users to browse information in a highly configurable way — users can add widgets, place them where they like, configure which information is shown and how. In the current application, the pages with user-specific, highly variable data were built using standard JSP pages containing the framework of the page and JavaScript calls to fetch the user-specific parts. Seam and JSF support these concerns very well. Using JSF, the user-specific parts look like this:
<c:forEach var="widget" items="#{userPreferences.getWidgets()}">
<ui:include src="#{widget.pageName}"/>
</c:forEach>
By being able to use ui:include and provide it with an EL expression that resolves to the view to be included, the page becomes extremely dynamic.
The above piece of code does contain one tricky bit: the use of c:forEach instead of ui:repeat. It took me a while to figure this out, but there is a good explanation in this blog post.
Even knowing that I needed to use the JSTL core tag library instead of the JSF one was a puzzle. JSF apparently comes with support for this, but you have to be aware that you need to use a special namespace declaration in the view page to make it work. Instead of the normal xmlns:c="http://java.sun.com/jstl/jsp/core" you need to use xmlns:c="http://java.sun.com/jstl/core".
The above code neatly includes the proper widgets in a page. Ideally these widgets would be reusable on other pages. In the Struts application this is not necessarily the case — before a JSP is rendered, the Action class is responsible for putting all the required data on the request. If I move a widget to another page, the data to display will not be there and I have to move the code to fetch the data as well. This is where a lot of the code duplication begins.
In Seam, the widget is responsible for fetching it’s own data by invoking Seam components using EL. For instance, the included widget might contain this code:
The company's annual sales total #{metricService.calculateAnnualSalesTotal(company)}.
If the company is available somewhere in the Seam contexts, the above fragment will invoke a service using the company to fetch the required data, independent of the request context and makes the widget reusable on any page.
Working with Seam gives you a lot of flexibility, but that also has it’s downsides. What is needed is, to quote a well-known movie, is structure and discipline. Even though Seam doesn’t require a lot of layering, it still makes sense to separate the different concerns in the application. For the prototype, I chose to implement the traditional view / action / service / DAO layers to give the application more structure. Similarly, with all the options Seam provides for configuration and state management, it is important to pick one and stick with it.
One thing I did find surprising when developing with Seam is how lenient it is when it comes to errors. I’ve had pages referring to non-existent components or ui:include statements trying to include a missing view page, but Seam didn’t complain, just rendered an empty piece of HTML. If Seam would have thrown an error, at least I would have known what was wrong, saving me some time.
That said, so far I am impressed with the framework and the style of building websites with Seam. I have the feeling I have yet to tap into the true power of the framework.