views:

201

answers:

1

I'm trying to persuade a SiteMesh decorator to change the content-type of the response, but no joy. The content-type always ends up being the same as the decorated JSP, rather than that of the decorator.

For example, say I have a JSP with the header

<%@ page contentType="application/xhtml+xml" %>

I also have a SiteMesh decorator JSP which defines this:

<%@ page contentType="application/vnd.wap.xhtml+xml" %>

What I want is for the decorated response to have the mime type of the decorator (the actual MIME type used here are not important, this is just to illustrate the problem).

A dig through the SiteMesh 2.4.1 source suggests that the problem lies with the ContentBufferingResponse class, which is responsible for capturing the output of the target. This overrides the setContentType() method, recording the value for later use, but it also invokes super.setContentType(), effectively passing the content-type of the target JSP directly to the response. Once that's done, no amount of cajoling will persuade the response to do otherwise.

So is there a workaround for this? Can the content-type of the target JSP be suppressed, and taken from the decorator instead?

+1  A: 

The ContentBufferingResponse.setContentType will trigger a call to the HttpServletResponseWrapper.setContentType. Later, the decorator is included in the response using a RequestDispatcher.include which cannot change the status code or set headers (any attempt to make changes is ignored). So basically, once you set the content type, its game over, you can’t change it.

From what I see, the SiteMeshFilter.obtainContent method is the only place the ContentBufferingResponse class is instantiated, so SiteMeshFilter and ContentBufferingResponse would be the places to look for workarounds.

One possible workaround will be to overwrite obtainContent in a subclass of SiteMeshFilter and let the right method be called at runtime by use of polymorphism. There is just one problem with this: obtainContent is marked private so polymorphism won’t work. To make a call to a different obtainContent method you will have to overwrite a lot more than this method in the filter, and I’m afraid that will include the doFilter method itself.

Another workaround would be to somehow call another version of the setContentType method, one that does not call super.setContentType with the mime type of the decorated page. But you can’t change the call to another method since in obtainContent’s code we are instantiating a ContentBufferingResponse instance using “new”.

At this point you could create a copy of the ContentBufferingResponse class in your project (under the same package declaration) one in which the setContentType method calls super.setContentType with the mime type you want and not the mime type from the decorated page. You can then trick the server into loading your class instead of the original, by playing with the classpath and making sure your class is loaded before the one in SiteMesh’s jar. The main problem here will be managing between different mime types if you have more than one decorator (and I’m sure you have :D).

The third (also ugly) workaround will be to just hack away on SiteMesh’s code and have your way about it (not sure if you will hit issues with the license).

So, in my opinion, unless you are willing to resort to some ugly workarounds, you won’t be able to change the content type once it has been set.

dpb