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

Reply via email to