Sunday, December 13, 2009

Simple Layouts with JSP in Spring MVC

Every web application has elements common to all its pages which are good candidates for re-use. While Spring MVC provides integration with Tiles, it can be an overkill for simple applications and needs using Spring's client side JS library for AJAX (correct me if I'm wrong).

For most applications, standard JSPs can serve as good layout engine. This post shows how Spring's handler interceptor can be used to specify a normal JSP file as the layout for a Spring MVC web application.

This interceptor allows you to specify a JSP file containing the layout for the application into which the actual views get plugged. The interceptor takes the actual model and view and replaces the actual view with the layout view's name and puts the actual view name into the model map to be used by the layout file. If you do not want to include the layout for certain requests (AJAX requests) you can prefix the view name with "noLayout:".

The Interceptor
public class LayoutInterceptor extends HandlerInterceptorAdapter {
private static final String NO_LAYOUT = "noLayout:";

private String layoutView;

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);

String originalView = modelAndView.getViewName();

if (originalView != null && !originalView.startsWith("redirect:")) {
includeLayout(modelAndView, originalView);
}
}

private void includeLayout(ModelAndView modelAndView, String originalView) {
boolean noLayout = originalView.startsWith(NO_LAYOUT);

String realViewName = (noLayout) ? originalView.substring(NO_LAYOUT.length()) : originalView;

if (noLayout) {
modelAndView.setViewName(realViewName);
} else {
modelAndView.addObject("view", realViewName);
modelAndView.setViewName(layoutView);
}
}

public void setLayoutView(String layoutView) {
this.layoutView = layoutView;
}
}

Sample Usage
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<bean id="layoutInterceptor" class="LayoutInterceptor">
<property name="layoutView" value="layout"></property>
</bean>
</list>
</property>
</bean>

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
With that all the views returned by controllers can be included within the layout JSP by including the following line in WEB-INF/jsp/layout.jsp.
<jsp:include page="/WEB-INF/jsp/${view}.jsp"></jsp:include>

2 comments:

chenrik said...

Have you ever heard of Sitemesh?

It does pretty much what you are describing plus a little more.

It is implemented as a servlet filter.

Chandru said...

Sitemesh sounds very close indeed though I do not know it well enough. But how would it handle AJAX requests?