Hi,
As is well known, using JSF1.1/JSP2.0 and jsp:include operation causes
problems. When the included page has a mix of JSF and non-JSF generated
output, all the JSF generated output appears first in the response
stream. This is particularly annoying when using Struts Tiles.
I have implemented a solution for this. It works "stand-alone" (ie
without modifying the MyFaces code) but would be even nicer if
integrated into the MyFaces release.
I'll describe the cause and solution first. I can't post the code just
yet, as I would need permission from my employer. However it's easy
enough to implement from the description. I hope to get permission to
post the code under APL 2.0 soon.
Note that the JSF1.2/JSP2.1 spec has supposedly resolved this problem by
changing a few APIs and definitions. I don't know exactly how they
solved the problem, but anyway that won't be available for a while as
the specs are only in public draft.
Issue:
JSF components generate output by calling
writer = FacesContext.getCurrentInstance().getResponseWriter().
// call methods on writer
However the FacesContext's response writer is initialised when it is
first created, and never changes. When a jsp:include occurs, jsp creates
a temporary buffered response stream and associates it with the
pageContext. After page processing is complete the buffer is appended to
the original response stream. So JSF content goes immediately to the
original output while other content is only sent after the page completes.
Solution:
Have the FacesContext keep a stack of ResponseWriters instead of just
one, and add a tag to each included page to tell the FacesContext when
to create&push a new ResponseWriter onto the stack and when to pop it off.
This requires creating a custom FacesContextFactory class which creates
custom FacesContext classes. The FacesContext keeps a stack of
ResponseWriter objects and overrides getResponseWriter. All other calls
can be delegated to a normal FacesContext implementation. A custom tag does:
doStartTag:
get FacesContext
cast it to custom type
call startPage(PageContext)
doEndTag:
get FacesContext
call endPage
Each included page then just needs:
<z:stackPage>
...
</z:stackPage>
The faces-config.xml needs to point to the custom FacesContextFactory
and that's all. Tiles includes (and other includes) then work as expected.
Can anyone see a problem with this approach? I don't see any issues and
performance is fine.
I think merging this functionality into the current
FacesContextFactoryImpl and ServletFacesContextImpl would be easy to do
and backwards-compatible. And the new tag's functionality could be
merged into f:subView, so that as long as the subView tag was *within*
the included page everything would work as expected. Or it could be kept
as a separate tag.
Regards,
Simon