Hello, My name is Mike, and I work for a fairly large company that uses Struts 2 for most of its web apps. Some of these web apps are quite large, and one in particular has grown into a monster. In my ongoing efforts to clean up this project, I have developed what I like to call a JspHelper. Before I explain exactly what that is, I'd like to provide you with some examples of the problems that I set out to fix.
*Problem 1 - Monster JSPs* During early development of this project, struts tags were highly leveraged. I was not involved with the early phases of this project, so I can't speak to why they were used so extensively.. but they were. Tons of nested if tags that read var tags that were set in the JSP. Some of these JSPs were very hard to follow. Even though I am a fan of struts tags, I've come to realize that they should not be overused. JSPs cannot be unit tested and JSPs are hard to debug. So cleaning up a JSP means shifting logic out, in most cases into the action. An easy clean up could be something like this: <s:if test="articles != null && articles.size > 0 && user.canViewArticles"> into <s:if test="showArticleSection()"> It's instantly more readable, and the logic is in a testable Java class. Following this approach creates a lot of methods in the action, which can be a problem if you already have... *Problem 2 - Bloated Action Classes* When the project was small, it made a lot of sense to create abstract/parent action classes to share code between actions. As time passed, we just continued shoving any shared code in one of two "abstract" classes... even if the code was only being shared between two out of the 20 actions. The fix for this trend is to moved shared code into services, so we began enforcing that practice. But this approach had its limitations. We use Spring injection for all of our services, and some times a particular piece of logic just required too much stateful input... it just made more sense to leave it on the action where it had easy access to everything it needed, as opposed to shoving it in a stateless service and having to pass in a ton of arguments. What we really needed was some best practices put in place for the creation of stateful, non-Spring services. *Enter JspHelperInceptor * It wasn't until after I created this inceptor that I realized it is almost identical to the ModelDrivenInceptor. At which point, I wondered if that is what I should actually be using.. but more on that later. All this interceptor does is push an object onto the value stack. That's it. Your action implements JspHelperAware which provides Object getJspHelper(), and the intercept code reads almost line for line with the ModelDrivenInterceptor. The idea behind this JspHelper, which is just a POJO that you define, is to provide a place where all (or most) of the data your JSP needs can be found. This means that OGNL can find any public members or operations faster, and also developers can trace back any OGNL expressions in the JSP to it's corresponding Java code. This approach places code in a shareable component. You can easily aggregate one JSPHelper into another one, and then either provide wrapper methods or simply push the aggregate helper onto the stack using the s:push tag for direct access to it's members. This approach makes the actions leaner, and provides a very clear cut view. I find at times that the action tends to be both controller and view, when really it should only act as the controller (IMO). This technique is functionally very similar to ModelDriven, but it's used slightly differently. I've used a JspHelper in combination with ModelDriven, and I've found the JspHelper to be a good place to store logic.. and leave the Model strictly as a business object. *Conclusion * I have personally found this technique incredibly useful in cleaning up code. You can shift all of the JSP exclusive logic and properties to the helper without touching the JSP (the OGNL expressions remain the same). Then after that initial step, you have a real handle on the exact input your JSP needs, and the actual clean up process progresses faster and with less errors. I'm still in the process of establishing some best practices, but so far this approach is working for us. I would like some honest feedback