/**
*
*/
package org.jboss.portletbridge.richfaces;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;
import javax.faces.FacesException;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.portlet.MimeResponse;
import javax.portlet.PortletContext;
import javax.portlet.PortletSession;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;
import javax.portlet.faces.BridgeException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.ajax4jsf.application.AjaxViewHandler;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.context.ViewResources;
import org.ajax4jsf.request.MultipartRequest;
import org.ajax4jsf.resource.FacesResourceContext;
import org.ajax4jsf.resource.InternetResource;
import org.ajax4jsf.resource.InternetResourceBuilder;
import org.ajax4jsf.resource.ResourceContext;
import org.ajax4jsf.resource.ResourceLifecycle;
import org.ajax4jsf.resource.ResourceNotFoundException;
import org.ajax4jsf.resource.ServletResourceContext;
import org.ajax4jsf.webapp.WebXml;
import org.ajax4jsf.xml.serializer.Method;
import org.ajax4jsf.xml.serializer.OutputPropertiesFactory;
import org.ajax4jsf.xml.serializer.Serializer;
import org.ajax4jsf.xml.serializer.SerializerFactory;
import org.ajax4jsf.xml.serializer.TreeWalker;
import org.jboss.portletbridge.BridgeConfig;
import org.jboss.portletbridge.BridgeStrategy;
import org.jboss.portletbridge.BufferedMimeResponseWrapper;
import org.jboss.portletbridge.RequestScopeManager;
import org.jboss.portletbridge.StateId;
import org.jboss.portletbridge.component.UIPortletAjaxViewRoot;
import org.jboss.portletbridge.context.AbstractExternalContext;
import org.jboss.portletbridge.context.PortletBridgeContext;
import org.jboss.portletbridge.renderkit.portlet.PortletAjaxViewRootRenderer;
import org.jboss.portletbridge.util.BridgeLogger;
import org.richfaces.component.FileUploadConstants;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
/**
* @author asmirnov
*
*/
public class RichFacesStrategy extends BridgeStrategy {
private static final String XML_CONTENT = "text/xml;charset=UTF-8";
private static final String HTML_EPILOG = "</body></html>";
private static final byte[] HTML_EPILOG_BYTES = HTML_EPILOG.getBytes();
private static final String HTML_BODY = "</head><body>";
private static final byte[] HTML_BODY_BYTES = HTML_BODY.getBytes();
private static final String HTML_PROLOG = "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head>";
private static final byte[] HTML_PROLOG_BYTES = HTML_PROLOG.getBytes();
private static final Logger log = BridgeLogger.BRIDGE.getLogger();
public static final String RICHFACES_RESOURCE = "org.jboss.portletbridge.richfaces.Resource";
public static final String AJAX4JSF_FILTER = "org.ajax4jsf.Filter";
public static final String SEAM_FILTER = "org.jboss.seam.servlet.SeamFilter";
public static final String PARAM_CREATE_TEMP_FILES = "createTempFiles";
public static final String PARAM_MAX_REQUEST_SIZE = "maxRequestSize";
private final ResourceLifecycle lifecycle;
private final PortletXMLFilter xmlFilter;
private InternetResourceBuilder resourceBuilder;
private boolean uploadToTempDir = false;
private int uploadLimit = 0;
public RichFacesStrategy(BridgeConfig config) {
super(config);
// Assert richfaces classes.
AjaxContext.class.getName();
lifecycle = new ResourceLifecycle();
xmlFilter = new PortletXMLFilter();
PortletContext portletContext = config.getPortletConfig()
.getPortletContext();
xmlFilter.init(portletContext);
resourceBuilder = InternetResourceBuilder.getInstance();
if (!(resourceBuilder instanceof PortletResourceBuilder)) {
resourceBuilder = new PortletResourceBuilder(resourceBuilder);
InternetResourceBuilder.setInstance(resourceBuilder);
}
WebXml webXml = (WebXml) portletContext
.getAttribute(WebXml.CONTEXT_ATTRIBUTE);
portletContext.setAttribute(WebXml.CONTEXT_ATTRIBUTE,
new PortletWebXml(webXml));
Map<String, String> filterInitParams = config
.getFilterInitParams(AJAX4JSF_FILTER);
if (filterInitParams == null) {
// then check to see if the init params are on the seam filter
// config
filterInitParams = config.getFilterInitParams(SEAM_FILTER);
}
if (filterInitParams != null) {
String uploadToTempDirStr = filterInitParams
.get(PARAM_CREATE_TEMP_FILES);
this.uploadToTempDir = uploadToTempDirStr != null
&& uploadToTempDirStr.toLowerCase().equals("true");
String maxRequestSizeStr = filterInitParams
.get(PARAM_MAX_REQUEST_SIZE);
if (maxRequestSizeStr != null) {
try {
uploadLimit = Integer.parseInt(maxRequestSizeStr);
} catch (NumberFormatException nfe) {
log.warning("invalid Filter init parameter value in web.xml: "
+ PARAM_MAX_REQUEST_SIZE
+ " - "
+ maxRequestSizeStr);
}
}
}
}
private void finishResponse(FacesContext facesContext,
BufferedMimeResponseWrapper wrappedResponse) {
try {
Map<String, Object> requestMap = facesContext.getExternalContext()
.getRequestMap();
if (!Boolean.TRUE.equals(requestMap
.get(AjaxViewHandler.RESOURCES_PROCESSED))) {
ViewResources viewResources = new ViewResources();
viewResources.initialize(facesContext);
viewResources.processHeadResources(facesContext);
requestMap.put(AjaxContext.HEAD_EVENTS_PARAMETER,
viewResources.getHeadEvents());
// Mark as processed.
requestMap.put(AjaxViewHandler.RESOURCES_PROCESSED,
Boolean.TRUE);
}
AjaxContext ajaxContext = AjaxContext
.getCurrentInstance(facesContext);
if (ajaxContext.isAjaxRequest(facesContext)) {
writeAjaxContent(facesContext, wrappedResponse);
} else {
writeContent(facesContext, wrappedResponse);
}
} catch (Exception e) {
throw new BridgeException(e);
}
}
private void writeAjaxContent(FacesContext facesContext,
BufferedMimeResponseWrapper wrappedResponse) throws IOException,
SAXException {
xmlFilter.writeContent(facesContext, wrappedResponse);
}
private void writeContent(FacesContext facesContext,
BufferedMimeResponseWrapper wrappedResponse) throws IOException,
SAXException {
writeHeadEvents(facesContext, wrappedResponse);
wrappedResponse.writeBufferedData();
}
private void writeHeadEvents(FacesContext facesContext,
BufferedMimeResponseWrapper wrappedResponse) throws IOException,
SAXException {
Object headEvents = facesContext.getExternalContext().getRequestMap()
.get(AjaxContext.HEAD_EVENTS_PARAMETER);
if (headEvents != null) {
Node[] nodes = (Node[]) headEvents;
Properties xhtmlProperties = OutputPropertiesFactory
.getDefaultMethodProperties(Method.XHTML);
Serializer serializer = SerializerFactory
.getSerializer(xhtmlProperties);
MimeResponse response = wrappedResponse.getResponse();
if (wrappedResponse.isUseWriter()) {
serializer.setWriter(response.getWriter());
} else {
serializer.setOutputStream(response.getPortletOutputStream());
}
ContentHandler contentHandler = serializer.asContentHandler();
TreeWalker treeWalker = new TreeWalker(contentHandler);
contentHandler.startDocument();
for (Node node : nodes) {
treeWalker.traverseFragment(node);
}
contentHandler.endDocument();
}
}
public void init(FacesContext context, RenderKitFactory renderKitFactory) {
PortletAjaxViewRootRenderer ajaxRenderer = new PortletAjaxViewRootRenderer();
Iterator<String> renderKitIds = renderKitFactory.getRenderKitIds();
while (renderKitIds.hasNext()) {
String renderKitId = renderKitIds.next();
RenderKit renderKit = renderKitFactory.getRenderKit(context,
renderKitId);
renderKit.addRenderer(UIViewRoot.COMPONENT_FAMILY,
UIViewRoot.COMPONENT_TYPE, ajaxRenderer);
}
context.getApplication().addComponent(UIViewRoot.COMPONENT_TYPE,
UIPortletAjaxViewRoot.class.getName());
}
public void beforeRenderRequest(FacesContext facesContext) {
AjaxContext ajaxContext = AjaxContext.getCurrentInstance(facesContext);
StateId stateId = PortletBridgeContext.getCurrentInstance(facesContext)
.getStateId();
ajaxContext.getCommonAjaxParameters().put(
RequestScopeManager.STATE_ID_PARAMETER, stateId.toString());
}
public RenderResponse createResponseWrapper(RenderResponse response) {
return new BufferedRenderResponseWrapper(response);
}
public ResourceResponse createResponseWrapper(ResourceResponse response) {
return new BufferedResourceResponseWrapper(response);
}
public void afterRenderRequest(FacesContext facesContext,
RenderResponse wrappedResponse) {
finishResponse(facesContext,
(BufferedMimeResponseWrapper) wrappedResponse);
}
public void afterResourceRequest(FacesContext facesContext,
ResourceResponse wrappedResponse) {
finishResponse(facesContext,
(BufferedMimeResponseWrapper) wrappedResponse);
}
@Override
public boolean serveResource(ResourceRequest request,
ResourceResponse response) throws BridgeException {
String resourceKey = request.getResourceID();
if (null != resourceKey) {
try {
InternetResource resource = resourceBuilder
.getResourceForKey(resourceKey);
ResourceContext resourceContext = getResourceContext(resource,
request, response);
Object resourceData = resourceBuilder
.getResourceDataForKey(resourceKey);
resourceContext.setResourceData(resourceData);
try {
// TODO - cache ?
request.setAttribute(RICHFACES_RESOURCE, resource);
lifecycle.send(resourceContext, resource);
request.removeAttribute(RICHFACES_RESOURCE);
} catch (IOException e) {
throw new BridgeException(e);
} finally {
resourceContext.release();
}
return true;
} catch (ResourceNotFoundException e) {
return false;
}
} else { // Handle File Upload
if(isMultipartContent(request)) {
String uid = request
.getParameter(FileUploadConstants.UPLOAD_FILES_ID);
MultipartRequest multipartRequest;
// RichFaces 3.3.1 & 3.3.3 uses different signatures in
// constructor, so use reflection to create instance.
try {
try {
Constructor<MultipartRequest> constructor = MultipartRequest.class
.getConstructor(HttpServletRequest.class,
boolean.class, int.class, String.class);
multipartRequest = constructor.newInstance(
new ResourceRequestWrapper(request),
uploadToTempDir, uploadLimit, uid);
} catch (NoSuchMethodException e) {
// 3.3.1, add temp dir parameter.
Constructor<MultipartRequest> constructor = MultipartRequest.class
.getConstructor(HttpServletRequest.class,
boolean.class, String.class, int.class,
String.class);
multipartRequest = constructor.newInstance(
new ResourceRequestWrapper(request),
uploadToTempDir, null, uploadLimit, uid);
}
} catch (Exception e) {
throw new BridgeException(
"Cannot create MultipartRequest instance", e);
}
request.setAttribute(
FileUploadConstants.FILE_UPLOAD_REQUEST_ATTRIBUTE_NAME,
multipartRequest);
return false;
}
}
return false;
}
public static final String MULTIPART = "multipart/";
public static final boolean isMultipartContent(ResourceRequest request) {
String contentType = request.getContentType();
if (contentType == null) {
return false;
}
if (contentType.toLowerCase().startsWith(MULTIPART)) {
return true;
}
return false;
}
/**
* @param resource
* @param request
* @param response
* @return
* @throws ServletException
* @throws FacesException
*/
protected ResourceContext getResourceContext(InternetResource resource,
ResourceRequest request, ResourceResponse response)
throws FacesException {
FacesContext facesContext = null;
ResourceContext resourceContext;
if (resource.requireFacesContext()) {
facesContext = config.createFacesContext(request, response);
resourceContext = new PortletFacesResourceContext(facesContext);
} else {
resourceContext = new PortletResourceContext(config
.getPortletConfig().getPortletContext(), request, response);
}
resourceContext.setCacheEnabled(true);
return resourceContext;
}
@Override
public int getPortletSessionScopeForName(String name) {
return PortletSession.PORTLET_SCOPE;
}
@Override
public void beforeActionRequest(FacesContext facesContext) {
// TODO Auto-generated method stub
}
@Override
public void afterActionRequestExecute(FacesContext facesContext) {
// TODO Auto-generated method stub
}
@Override
public void afterActionRequest(FacesContext facesContext) {
// TODO Auto-generated method stub
}
@Override
public void beforeEventRequest(FacesContext facesContext) {
// TODO Auto-generated method stub
}
@Override
public void afterEventRequest(FacesContext facesContext) {
// TODO Auto-generated method stub
}
@Override
public void beforeResourceRequest(FacesContext facesContext) {
AjaxContext ajaxContext = AjaxContext.getCurrentInstance(facesContext);
StateId stateId = PortletBridgeContext.getCurrentInstance(facesContext)
.getStateId();
ajaxContext.getCommonAjaxParameters().put(
RequestScopeManager.STATE_ID_PARAMETER, stateId.toString());
if (ajaxContext.isAjaxRequest(facesContext)) {
// Set XML response.
Object response = facesContext.getExternalContext().getResponse();
Object request = facesContext.getExternalContext().getRequest();
// PBR-134 see forum post for IE8 fix
if (!isMultipartContent((ResourceRequest) request)) {
if (response instanceof MimeResponse) {
MimeResponse mimeResponse = (MimeResponse) response;
mimeResponse.setContentType(XML_CONTENT);
}
}
}
}
@Override
public void afterResourceRequestExecute(FacesContext facesContext) {
// DO nothing.
}
}