com.liferay.faces.bridge.renderkit.bridge
Class ResponseWriterBridgeImpl

java.lang.Object
  extended by java.io.Writer
      extended by javax.faces.context.ResponseWriter
          extended by javax.faces.context.ResponseWriterWrapper
              extended by com.liferay.faces.bridge.renderkit.bridge.ResponseWriterBridgeImpl
All Implemented Interfaces:
Closeable, Flushable, Appendable, javax.faces.FacesWrapper<javax.faces.context.ResponseWriter>

public class ResponseWriterBridgeImpl
extends javax.faces.context.ResponseWriterWrapper

This class acts as a portlet filter (in a sense), in that it decorates/wraps the Faces implementation ResponseWriter so that it can transform what is written to the response. The response needs to be filtered because of three limitations in the JSF 2.0 and JSF 2.1 jsf.js JavaScript code). The goal is to fix these limitations in JSF 2.2 so that this class will become unnecessary.

The three limitations in the jsf.js JavaScript code are:

1. The <?xml version="1.0" encoding="UTF-8"?> and <!DOCTYPE> markers is assumed to be valid to keep in the response because jsf.js assumes a servlet environment in which the rendered JSF view takes up the entire DOM in the userAgent/browser.

The workaround for #1 is to simply strip out the offending markers.

2. During "partial" updates in which the javax.faces.ViewRoot is being replaced in the DOM (which is basically a full update of the view -- not really partial), jsf.js attempts to replace everything inside the <body>...</body> elements, which of course is a servlet environment assumption. Instead, it should be the outermost <div> tag rendered by the bridge's BodyRendererBridgeImpl that should be replaced in the DOM.

The workaround for #2 is to substitute the id value of "javax.faces.ViewRoot" with the id of the outermost <div> tag rendered by the bridge's BodyRendererBridgeImpl.

3. Also in the the case of a "partial" update of javax.faces.ViewRoot, jsf.js attempts to dynamically create the javax.faces.ViewState hidden input field if it is not found in the form. The JavaScript code will successfully do this provided it is permitted to replace everything inside the <body>...</body> elements, but since we can't let that happen in a portlet environment, the hidden field does not get created.

The workaround for #3 is to inject the javax.faces.ViewState hidden field into the response if it is not already there.

Author:
Neil Griffin

Field Summary
 
Fields inherited from class java.io.Writer
lock
 
Constructor Summary
ResponseWriterBridgeImpl(javax.faces.context.ResponseWriter wrappedResponseWriter)
           
 
Method Summary
 javax.faces.context.ResponseWriter cloneWithWriter(Writer writer)
           
 void endCDATA()
           
 void endElement(String elementName)
           
 javax.faces.context.ResponseWriter getWrapped()
           
 void startCDATA()
           
 void startElement(String elementName, javax.faces.component.UIComponent uiComponent)
           
 void write(char[] cbuf, int off, int len)
          The main purpose of this method is to solve the jsf.js limitation #1 as described in the class header comments.
 void writeAttribute(String attributeName, Object attributeValue, String property)
          If the implementation (Mojarra in particular) is trying to do a DOM replacement using markup like the following:
 void writeURIAttribute(String name, Object value, String property)
           
 
Methods inherited from class javax.faces.context.ResponseWriterWrapper
close, endDocument, flush, getCharacterEncoding, getContentType, startDocument, writeComment, writeText, writeText, writeText
 
Methods inherited from class java.io.Writer
append, append, append, write, write, write, write
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

ResponseWriterBridgeImpl

public ResponseWriterBridgeImpl(javax.faces.context.ResponseWriter wrappedResponseWriter)
Method Detail

cloneWithWriter

public javax.faces.context.ResponseWriter cloneWithWriter(Writer writer)
Overrides:
cloneWithWriter in class javax.faces.context.ResponseWriterWrapper

endCDATA

public void endCDATA()
              throws IOException
Overrides:
endCDATA in class javax.faces.context.ResponseWriterWrapper
Throws:
IOException

endElement

public void endElement(String elementName)
                throws IOException
Overrides:
endElement in class javax.faces.context.ResponseWriterWrapper
Throws:
IOException

startCDATA

public void startCDATA()
                throws IOException
Overrides:
startCDATA in class javax.faces.context.ResponseWriterWrapper
Throws:
IOException

startElement

public void startElement(String elementName,
                         javax.faces.component.UIComponent uiComponent)
                  throws IOException
Overrides:
startElement in class javax.faces.context.ResponseWriterWrapper
Throws:
IOException

write

public void write(char[] cbuf,
                  int off,
                  int len)
           throws IOException

The main purpose of this method is to solve the jsf.js limitation #1 as described in the class header comments.

The Mojarra JSF implementation has a vendor-specific com.sun.faces.facelets.compiler.UIInstructions class that will render the following markers:

This method will ensure that such markers are not rendered to the response, as they should not be rendered as part of a portlet, since portlets are simply HTML fragment that are aggregated together into a single HTML document by the portlet container.

Overrides:
write in class javax.faces.context.ResponseWriterWrapper
Throws:
IOException

writeAttribute

public void writeAttribute(String attributeName,
                           Object attributeValue,
                           String property)
                    throws IOException

If the implementation (Mojarra in particular) is trying to do a DOM replacement using markup like the following:

               <partial-response>
                 <changes>
                   <update id="javax.faces.ViewRoot"><![CDATA[...]]]></update>
                   ...
                 </changes>
                 </partial-response>
 

... then this will cause the jsf.js JavaScript library to replace the entire <body> element! In order to avoid this, need to change the id attribute from "javax.faces.ViewRoot" to the clientId of the outer-most div of the portlet, rendered by the bridge's BodyRendererBridgeImpl.

Overrides:
writeAttribute in class javax.faces.context.ResponseWriterWrapper
Throws:
IOException

writeURIAttribute

public void writeURIAttribute(String name,
                              Object value,
                              String property)
                       throws IOException
Overrides:
writeURIAttribute in class javax.faces.context.ResponseWriterWrapper
Throws:
IOException

getWrapped

public javax.faces.context.ResponseWriter getWrapped()
Specified by:
getWrapped in interface javax.faces.FacesWrapper<javax.faces.context.ResponseWriter>
Overrides:
getWrapped in class javax.faces.context.ResponseWriterWrapper


Copyright © 2013 Liferay, Inc.. All Rights Reserved.