Friday, November 16, 2007

Stripes TypeConverters: Populating Domain Objects

Most web frameworks today have type converters. Type converters are chunks of code that takes query parameters as Strings and convert them into their appropriate data type. So numbers become Integers, dates become Dates, etc. Most frameworks also allow you to create custom type converters. For example, you might want to convert a currency form field into a Money object. That's all well and good. But we can take it a bit further with Stripes.

A common scenario in the web world is requesting data from the server and displaying it on the page. I mean, really, all web applications do is send data and get data and display the data they got. Nothing too complex. How we populate domain objects based on query parameters is where everyone's code can differ. Most common is for an action to process the query parameters and we get the one(s) we need and use them to lookup information in a database, shove those into some domain objects or properties of some sort, throw them in the request and forward to our view. Again, nothing too complex.

There are many times when we need to populate the same domain object(s) based on the same parameter on many different pages. I'll set up a scenario that we can use throughout the rest of this article. You created a User management screen. Displaying a list of users you want to edit one. The query parameter you pass in is the user.id. So we might have some code in our Stripes action bean that looks something like this:


private User user;
private UserDao userDao;

public User getUser()
{
return this.user;
}

public void setUser(User user)
{
this.user = user;
}

@SpringBean("userDao")
public void setUserDao(UserDao userDao)
{
this.userDao = userDao;
}

public Resolution edit()
{
user = UserDao.getUser(user.getId());
return new ForwardResolution(EDIT_USER_PAGE);
}


We could of course also discuss the pros and cons of doing the same thing in a method annotated with @After/@Before but for the sake of simplicity we'll say that's possible as well and move on.

So the client comes back and decides that they need an additional page where the administrator can click on a product and see all the users that purchased this product. And further more he can click on a user and view some details about that user. We begin to build the new action and view and notice that we've got to duplicate some code we just wrote. We need the UserDao again and we need to do a lookup so we copy and paste, test it, it works. Yea! And then we need another page, and another page, and we copy and paste and copy and paste. At this point we've got the same code written 10 times. That's bad. So we decide hey, we are using Stripes, let's use a TypeConverter to do this.

Stripes has a TypeConverter interface that requires two methods:

convert(String input, Class targetType, Collection errors)
setLocale(Locale locale)

We are only going to worry about convert right now. So we begin our custom type converter:


public class UserTypeConverter implements TypeConverter
{
private UserDao userDAo;

@SpringBean("userDao")
public void setUserDao(UserDao userDao)
{
this.userDao = userDao;
}

public User convert(String id, Class user, Collection error)
{
if (id != null)
{
User user = userDao.getUser(id);
return user;
}
return null;
}
}


This is pretty straightforward. We are just looking the user up from our UserDao using an id. But how does that id get there? Pretty simple as well. Let's look at our action again, only this time using the converter.


User user;

public void setUser(User user)
{
this.user = user;
}

public User getUser()
{
return this.user;
}

public Resolution edit()
{
return new ForwardResolution(EDIT_USER_PAGE);
}


And the URL might looking something like this:

http://localhost:8080/app/User.action?edit=&user=1234

Anytime the id is not null, user gets populated. If it is null, for example when adding a new user, user will only be populated from the normal form to object binding that Stripes normally does. But how does our action know that User is supposed to use the UserTypeConverter? Well, if we weren't needing the Spring Bean injection magic would could have simply done this in our action.

@Validate(converter=UserTypeConverter.class)
User user;

But since we need the Spring Bean magic we need to create our own TypeConverterFactory. Again, another interface. Stripes uses a default to init all the built in type converters. Here is what one might look like:


public class CustomTypeConverterFactory extends DefaultTypeConverterFactory
{
public TypeConverter getInstance(Class clazz, Locale locale) throws Exception
{
TypeConverter tc = super.getInstance(clazz, locale);
ServletContext sc = getConfiguration().getServletContext();

SpringHelper.injectBeans(tc, sc);

return tc;
}

@Override
public void init(Configuration configuration) {
super.init(configuration);
add(User.class, UserTypeConverter.class);
}
}


And then we need to tell Stripes to use our CustomTypeConverterFactory. In the web.xml in the StripesFilter init-params add the following param-name:

TypeConverterFactory.Class

and param-value:

com.app.package.foo.CustomTypeConverterFactory

And we're done. Comments welcome.

Thursday, November 15, 2007

Spring's DAO Support..Other choices?

I've gotten really spoiled to Spring's DAO support classes, Transaction management, etc. But as any good developer should I begin to wonder what alternatives are out there? Do java developers have another choice aside from rolling our own?

Just looking for comments.

Monday, November 12, 2007

Stripes Interceptor Tutorial

Note: I still can't get code to display very well on blogger. Sorry for the formatting issues. If you need to the source code is linked at the bottom of this article if it makes it easier to follow along.

I've been using Stripes for both large and small applications for about a year. While I am of the camp that there is no golden hammer I've not run across a project I am involved with where Stripes doesn't make sense. I've been wanting to write more articles on Stripes. While the documentation on the site is great for folks to get started it is lacking in the area of best practices and general tutorials on different aspects of the framework. One of my favaorite things about Stripes (and there are many) is how easy it is to extend the framework to suit my needs and most of the time I don't even need to dig into the source. A lot of tiimes that power comes in the form of an Interceptor.

Interceptors are not a new concept. I believe my first experince with them in web applications was when working with WebWork several years ago. Stripes has a couple of Interceptors already:

  • SpringInterceptor - Allows you to inject Spring Beans into controllers using the @SpringBean method level annotation.
  • BeforeAfterMethodInterceptor - Allows you to execute methods before and/or after specific Lifecycle Stages using @Before and @After method level annotations (AOP like behavior)

Note: Both the Interceptors mentioned above work in combination with annotations but not all have to.

In this article I want to show how to create an interceptor and we'll also create an annotation that will be used with the interceptor. I'll identify a problem that Stripes by itself doesn't solve and show how to implement the solution with a simple Interceptor. At times you may see mention of concepts that you won't be familiar with if not familiar with Stripes. While I'll try and explain these a bit as I go I may link to the Stripes documentation on the subject so as not to repeat information. I also want to keep this article as concise and to the point as possible.

The Problem

Ajax is everywhere and one common problem I've run into in the past is dealing with the browser caching the response from an ajax request. This seems to happen more often than not in Internet Explorer. What we want is to prevent this as much as possible when needed.

Typical Solutions

Probably the most common talked about solution is appending a random number on the end of the request. This tricks the browser into thinking it is a different response. Wikipedia's entry on XmlHttpRequest offers a different and slightly more complex solution which you can read about here: http://en.wikipedia.org/wiki/Xmlhttprequest#Caching. The problem with that is most of us are using a JavaScript library like jQuery or Prototype and that would cause us to go hacking inside their code. Not ideal. And another approache is to add header information to the response. In java it goes something like this:

HttpServletResponse response = context.getResponse();

response.setDateHeader("Expires", 0);

response.setHeader("Cache-control", "no-cache");

response.setHeader("Pragma", "no-cache");

Finding a centralized place for that code is the key. One solution on the Stripes mailing list was having a disableCaching() method in an ActionBean base class. When you wanted this to execute you would do something like this:

@Before(stages = LifecycleStage.ResolutionExecution)

@Override

protected void disableCaching() {

super.disableCaching();

}

And while that works it is a bit more verbose than need be.

The Interceptor Solution

What we want to do is take the above code and centralize it in an Interceptor that executes on the LifecycleStage.ResolutionExecution stage however we want to be able to control when the code gets executed. To do this we need two things:

  1. @NoCache annotation - We'll use this on methods to denote we want no caching. We'll also allow a boolean argument to indicate whether it should be on or off. This is important if we want to override a class level annotation on one or more methods.
  2. NoCacheInterceptor - We'll use this to check for the @NoCache annotation and shove the no cache header directives into the response.

The @NoCache annotation is pretty straightfoward:

@Retention(RetentionPolicy.RUNTIME)

@Target( {ElementType.METHOD, ElementType.TYPE})

@Documented

public @interface NoCache

{

boolean value() default true;

}

Save that in a file called NoCache.java in whatever package you desire.

Next is the interceptor which requires a bit more discussion. A Stripes interceptor implements an interface called Interceptor. Interceptor requires one method be implemented; public Resolution intercept(ExecutionContext ctx) throws Exception. Stripes will call this method on any configured interceptor during the specified LifecycleStage. Whoa, how do we specify the LifecycleStage? Whoa!!! What's with all this LifecycleStage talk?

Segway..Stripes has five Lifecycle Stages. Instead of repeating what is already well documented I'll let you read about them at your lesiure here.

And we're back...So we need to create this Interceptor and tell it when to execute the intercept method. We do that by specifying an annotation at the class level:

@Intercepts(LifecycleStage.ResolutionExecution)

public class NoCacheInterceptor implements Interceptor {

...

}

Now we should define our intercept method:

public Resolution intercept(ExecutionContext ctx) throws Exception {
...
}

Inside this method we need to define several objects we are going to use:

(1) final Configuration config = StripesFilter.getConfiguration();
(2) final ActionResolver resolver = config.getActionResolver();
(3) final ActionBeanContext context = ctx.getActionBeanContext();
(4) final ActionBean actionBean = resolver.getActionBean(context);
(5) final Class beanClass = actionBean.getClass();
(6) final String eventName = resolver.getEventName(beanClass, context);

  1. The Stripes Configuration class holds all the configuration information passed into Stripes from the web.xml. This includes all configured Interceptors.
  2. The ActionResolver will allows us to get which Method was requested from the ActionBean during the request.
  3. The ActionBeanContext is used to help us get an instance of the requested ActionBean
  4. The requested ActionBean.
  5. We need the actual Class.
  6. The eventName or requested method to call.

The next thing we need to do is get the Method or handler if the eventName doesn't exist. The reason for this is because if you request a URL and there is no eventName specified Stripes will look for a default handler which is a method denoted by the @DefaultHandler annotation. And then if this doesn't exist we need to throw an error and inform the user.

Good Practice: All ActionBeans should have a default handler in case an eventName was not given.

final Method handler;
if (eventName != null) {
handler = resolver.getHandler(beanClass, eventName);
} else {
handler = resolver.getDefaultHandler(beanClass);
if (handler != null) {
context.setEventName(resolver.getHandledEvent(handler));
}
}

// Insist that we have a handler
if (handler == null) {
throw new StripesServletException(
"No handler method found for request with ActionBean ["
+ beanClass.getName() + "] and eventName [ "
+ eventName + "]");
}

Don't worry too much about understanding every bit of that. Just know that we need the handler and we need to throw an error if one doesn't exist. The next thing we need to do is actually look for the @NoCache annotation. If we find it we set the no cache header junk and tell the ExecutionContext to proceed with the request.

if (isCachingDisabled(handler, beanClass)) {
HttpServletResponse response = context.getResponse();
response.setDateHeader("Expires", 0);
response.setHeader("Cache-control", "no-cache");
response.setHeader("Pragma", "no-cache");
}
return ctx.proceed();

Now we'll look at the isCachingDisabled method. We pass this method the handler and also the beanClass.

protected boolean isCachingDisabled(Method method, Class beanClass) {
....
}

To make sure that we are as performant as possible Stripes caches interceptor instances. Because of this we can also cache data within the interceptor. In this case once a @NoCache annotation has been found we want to cache this fact so the next time this interceptor is run with this beanClass we can just check the cache and not have to go through the annotation reflection checks. We first want to check the handler (Method) being called because it override class level annotations. If no annotation is found then we check for a class annotation.

So first, lets check the cache:

CacheKey cacheKey = new CacheKey(method, beanClass);
Boolean disabled = cache.get(cacheKey);
if (disabled != null) {
return disabled;
}

Don't worry about CacheKey yet. It's a simple class and we'll discuss it last. So if we found cache we return the value we found. Otherwise, we need to check for the annotation:

NoCache annotation = method.getAnnotation(NoCache.class);
if (annotation != null) {
disabled = annotation.value();
} else {
// search the method's class and its superclasses
Class clazz = beanClass;
do {
annotation = clazz.getAnnotation(NoCache.class);
clazz = clazz.getSuperclass();
} while (clazz != null && annotation == null);

if (annotation != null) {
disabled = annotation.value();
} else {
disabled = false;
}
}

So basically we look for an annotation on the method. If we find it we return its value. Otherwise, we check the class. We also want to check all super classes incase the user is extending a Base ActionBean of sorts.

Good Practice: Creating a BaseActionBean and having all your ActionBeans extend that base class will save a lot of boilerplace code and make developing with Stripes a lot simpler.

So once we either find an annotation or not we want to cache it and then return what we found:

cache.put(cacheKey, disabled);
return disabled;

The last thing for code is the CacheKey class. We want to make sure that we store unique keys when checking for cache. That way we always know we have the correct cache for the correct beanClass that was requested. Here's the class. I just make it an inner class of the interceptor.

private static final class CacheKey {
private Method method;
private Class beanClass;
private int hashCode;

public CacheKey(Method method, Class beanClass) {
super();
this.method = method;
this.beanClass = beanClass;
this.hashCode = method.hashCode() * 37 + beanClass.hashCode();
}

@Override
public boolean equals(Object obj) {
CacheKey that = (CacheKey) obj;
return this.method.equals(that.method)
&& this.beanClass.equals(that.beanClass);
}

@Override
public int hashCode() {
return hashCode;
}

@Override
public String toString() {
return beanClass.getName() + "." + method.getName();
}
}

Note: In Stripes 1.5, yet to be released, there will be a core set of interceptors on by default. This NoCache interceptor will be one of them along with BeforeAfterMethodInterceptor.

And that's it. Now to use it you might do something like this:

public class SomeActionBean extends BaseActionBean {

@NoCache
public Resolution ajaxEvent() {
// return some ajaxy stuff here
}
}

If you needed all but one method to turn caching off you might do something like this:

@NoCache
public class SomeActionBean extends BaseActionBean {

@NoCache(false)
public Resolution ajaxEvent() {
// return some ajaxy stuff here
}
}

I've included the full source for the interceptor and annotation at the following URL.

NoCache.tar.gz