SiteMesh: Changing the content-type of the response
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?
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.
Write a servlet filter that applies to your page, override setContentType()
to not call out to super, and then in the decorator you can set the content type to whatever you want.
You'll need to write one more servlet filter to do it, but it should be pretty simple.
精彩评论