SiteMesh 2 Undecorated AJAX requests

Note: Only after testing and writing this did I realize this is not a good idea. I will leave this up in the case it helps someone do something else, but this is a poor way to do this. I will update it later with a beter technique.

Recently I had the problem where I wanted my JavaScript requests to return undecorated content. The solutions I found online weren’t quite what I wanted so I constructed my own method using a custom mapper. The first two solutions below are the once I came across in my searches, the third is mine.

API Path

One simple solution is to keep all requests which you want undecorated in a secondary path, such as /api, and set your decorators.xml to exclude them all from decoration.


Parameter Patterns

The request posted at made the suggestion to add a key-value parameter pair such as ajax=true to every request you want undecorated. Inelegant and not transparent at all, but effective. See for a little more detail.


Instead of doing excludes on URL pattern matching, I wanted every javascript request to be undecorated by examining the X-Requested-With header. To implement this, ensure you have a custom sitemesh.xml.

I create a blank decorator, this is my /WEB-INF/decorators/api.jsp

<%@ taglib prefix="decorator" uri=""%><decorator:body />

And ensure that you declare this as a decorator within your /WEB-INF/decorators.xml (or wherever you might have put it)

<decorators defaultdir="/WEB-INF/decorators">
    <decorator name="api" page="api.jsp" >
        <pattern>/api*</pattern> <!-- This line is OPTIONAL -->

and the last XML modification is to insert another custom mapper into the sitemesh configuration. If you don’t have one, simply create /WEB-INF/sitemesh.xml populated with the default content from sitemesh-default.xml.

      <mapper class="com.your.package.structure.mapper.HeaderDecoratorMapper">
         <param name="decorator" value="api" />
         <param name="X-Requested-With" value="XMLHttpRequest" />

Notice the custom class we map to. This is the class that will inspect the headers for the correct values and respond accordingly. Here is

package com.your.package.structure.mapper;

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;

import com.opensymphony.module.sitemesh.Config;
import com.opensymphony.module.sitemesh.Decorator;
import com.opensymphony.module.sitemesh.DecoratorMapper;
import com.opensymphony.module.sitemesh.Page;
import com.opensymphony.module.sitemesh.mapper.AbstractDecoratorMapper;

public class HeaderDecoratorMapper extends AbstractDecoratorMapper
	private final Logger logger = Logger.getLogger(HeaderDecoratorMapper.class);

	private Map<String, String> headerMap = null;

	public void init(Config config, Properties properties, DecoratorMapper parent) throws InstantiationException
		super.init(config, properties, parent);
		headerMap = new HashMap<String, String>();

	public Decorator getDecorator(HttpServletRequest request, Page page)
			Decorator result = null;

			final List<String> headers = Collections.list(request.getHeaderNames());
			for(final String header : headerMap.keySet())
				if(headers.contains(header) && request.getHeader(header).matches(headerMap.get(header)))
					// We know we want to differ decorators
					if(logger.isDebugEnabled()) {
						logger.debug("Decorating header request with the decorator: " + headerMap.get("decorator"));
					result = super.getNamedDecorator(request, headerMap.get("decorator"));

			return result == null ? super.getDecorator(request, page) : result;
		catch (NullPointerException e)
			return super.getDecorator(request, page);

	/** Initialize the header mappings. */
	private void initMap(final Properties props)
		final Iterator<Entry<Object, Object>> it = props.entrySet().iterator();
		while (it.hasNext())
			final Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>);
			final String key = (String) entry.getKey();
			final String ext = (String) entry.getValue();
			headerMap.put(key, ext);
			if(logger.isDebugEnabled()) {
				logger.debug("Header mapping '"+key+"' with value '"+ext+"' ");

With all of these in place you should be able to redirect any header key:value combination, with the value of the value being regex matched, to any view you want.

Thanks! Hopefully this helps.


This entry was posted in uncategorized. Bookmark the permalink.

2 Responses to SiteMesh 2 Undecorated AJAX requests

  1. Tribul says:

    “Only after testing and writing this did I realize this is not a good idea”

    You currently have three ideas on this page – which are you referring to?

  2. Tribul says:

    and what was the downside that you discovered?

Leave a Reply

Your email address will not be published.