Best Practices
While Stripes was designed to provide the flexibility often needed in complex situations, it was also developed to make a number of things extremely easy. This document describes some of the best practices that fit in with the model the author intended for Stripes applications.
Don't duplicate domain objects in ActionBeans
Stripes' binding and validation system was designed from the ground up to make it easy to work with existing domain/value/JavaBean objects because you shouldn't have to duplicate properties from your domain objects in to a presentation specific class just to get things done. Stripes will happily set nested properties within your existing domain objects, many levels deep, and instantiate objects along the way as necessary. So, just embed your domain objects (or Lists, Arrays of Maps of them) in your ActionBean and provide a simple getter/setter pair for the object (or List or Array or Map).
Prefer pre-actions over <stripes:useActionBean ... />
Even if Stripes provides an elegant way to grab a View Helper Action from inside a JSP (or other view), the recommended request/response flow is to use a pre-action. View Helpers can and should be used (via <stripes:useActionBean/>
) in order to create reusable JSP fragments, for example, but HTTP requests should generally not be sent to JSPs directly.
Using an ActionBean to handle every incoming request has several advantages:
- URL Scheme Abstraction
- Full control over your URLs. Instead of URLs representing the path to your JSP, you can define the URLs either via convention or by specifying them directly using
@UrlBinding
- The ActionBean provides an indirection to the resource : internal JSP refactorings don't impact anything
- No hard-coded links to resources : you can use
<stripes:link beanclass="..."/>
, which provides "type-safe" links inside your JSPs - Out of the box support for clean, search engine friendly URLs
- Full control over your URLs. Instead of URLs representing the path to your JSP, you can define the URLs either via convention or by specifying them directly using
- Unified use of Interceptors (Interceptors execute around ActionBeans, not views)
- Page/action level security
- HTTP resource cache control
- Your custom interceptors
Previous experience with other frameworks may have led you to dislike pre-actions due to the amount of work involved constructing them. In Stripes it's actually pretty trivial! In most cases there will need to be an ActionBean to process events (form submission) from a page, in which case it's as simple as adding a one line method to that class which forwards to the page. If the page is read-only it is a little more work (requiring it's own ActionBean) but is still pretty minimal:
public class MyPreAction extends MyBaseActionBean { public Resolution view() { return new ForwardResolution("/my/view_only_page.jsp"); } }
Give the low cost of implementing pre-actions and all the benefits it brings we think it's really worth it!
Implement your own ActionBeanContext subclass
One area where projects often get into trouble, or have unwieldy code, is in accessing attributes store in HttpSession. One goal of Stripes is to ensure that ActionBeans remain independent of the Http* classes, so that they can be easily unit tested. By subclassing ActionBeanContext and configuring Stripes to use your subclass you can provide type safe centralized access to session. And since all access is in one class, it's easy to swap out the implementation with a mock implementation for unit testing. Your class might contain methods like:
public UserProfile getUserProfile() { return (UserProfile) getRequest().getSession().getAttribute("user_profile"); } public void setUserProfile(UserProfile userProfile) { getRequest().getSession().setAttribute("user_profile", userProfile); }
Use @Before methods to pre-populate domain objects
When creating screens to edit existing persistent objects it's nice to be able to avoid copying all the properties from the nested domain object to the persistent one that you just looked up from the database. If you pre-populate the object from the database, Stripes will just bind the values from the request like normal, leaving you with an updated object ready to save. Such an implementation might look like:
@Before(stages = LifecycleStage.BindingAndValidation) public void rehydrate() { this.domainObject = getHibernateSession().load(DomainObject.class, context.getRequest().getParameter("id")); }
Use the <stripes:layout*> tags to create componentized layouts
While Sitemesh is great for applying sitewide decorators and such, sometimes you just want a simpler solution. The layout tags allow you to define simple re-usable layouts using JSPs and then use them to drive the layout of your sites pages. By centralizing your layouts (instead of say, including the header and footer on each page) you make it much easier to change your layout later, perhaps from a standard c-clamp layout to something a little different. And since it's all done with JSP and a couple of custom tags, there's no external configuration or new syntax to learn. See the Tag Library Documentation for more information.
Make sensible use of Stripes' logging facilities
Most classes in Stripes log information that is very useful in figuring out what is going wrong when you are having problems. Stripes logs a lot of information at the DEBUG
and TRACE
levels, and quite sparse information at INFO
and below. In development it is recommended that you either always have Stripes logging at DEBUG
level, or be able to switch this on easily. In most logging implementations this can be done by configuring loggers under net.sourceforge.stripes
to output at DEBUG
level.
However, it should be noted that leaving logging turned up seriously impacts performance! For example, reducing the logging level from DEBUG
to INFO
on the Bugzooky example app cuts processing time in five in some cases! For this reason you should probably set the log level at INFO
or WARNING
for production usage.