/*
* JBoss, Home of Professional Open Source.
* Copyright 2013, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.portletbridge;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.FactoryFinder;
import javax.faces.application.ApplicationFactory;
import javax.faces.application.ViewHandler;
import javax.faces.context.FacesContext;
import javax.faces.event.SystemEvent;
import javax.faces.webapp.FacesServlet;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.PortletConfig;
import javax.portlet.PortletContext;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;
import javax.portlet.faces.Bridge;
import javax.portlet.faces.BridgeEventHandler;
import javax.portlet.faces.BridgeException;
import javax.portlet.faces.BridgePublicRenderParameterHandler;
import javax.portlet.faces.BridgeUninitializedException;
import javax.portlet.faces.BridgeWriteBehindResponse;
import org.jboss.portletbridge.bridge.config.BridgeConfig;
import org.jboss.portletbridge.bridge.context.BridgeContext;
import org.jboss.portletbridge.bridge.controller.BridgeController;
import org.jboss.portletbridge.bridge.event.BridgeDestroyRequestSystemEvent;
import org.jboss.portletbridge.bridge.event.BridgeInitializeRequestSystemEvent;
import org.jboss.portletbridge.bridge.factory.BridgeConfigFactory;
import org.jboss.portletbridge.bridge.factory.BridgeContextFactory;
import org.jboss.portletbridge.bridge.factory.BridgeControllerFactory;
import org.jboss.portletbridge.bridge.factory.BridgeFactoryFinder;
import org.jboss.portletbridge.bridge.logger.BridgeLogger;
import org.jboss.portletbridge.config.FacesConfigProcessor;
import org.jboss.portletbridge.config.WebXmlProcessor;
import org.jboss.portletbridge.context.InitFacesContext;
/**
* @author <a href="http://community.jboss.org/people/kenfinni">Ken Finnigan</a>
*/
public class PortletBridgeImpl implements Bridge {
private static final Logger logger = Logger.getLogger(PortletBridgeImpl.class.getName(), BridgeLogger.LOGGING_BUNDLE);
private BridgeConfig bridgeConfig;
private BridgeController bridgeController;
private boolean initialized = false;
/**
* @see javax.portlet.faces.Bridge#init(javax.portlet.PortletConfig)
*/
public void init(PortletConfig portletConfig) throws BridgeException {
if (null == portletConfig) {
throw new IllegalArgumentException("PortletConfig null when initializing Portlet Bridge");
}
if (this.initialized) {
throw new BridgeException("Portlet Bridge already initialized");
}
String portletName = portletConfig.getPortletName();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Commenced Portlet Bridge initialization for " + portletName);
}
this.bridgeConfig = getBridgeConfig(portletConfig);
this.bridgeController = ((BridgeControllerFactory) BridgeFactoryFinder
.getFactoryInstance(BridgeControllerFactory.class)).getBridgeController(bridgeConfig);
this.initialized = true;
if (logger.isLoggable(Level.FINE)) {
logger.fine("Completed Portlet Bridge initialization for " + portletName);
}
}
@SuppressWarnings("unchecked")
private BridgeConfig getBridgeConfig(PortletConfig portletConfig) {
BridgeConfig bridgeConfig = ((BridgeConfigFactory) BridgeFactoryFinder.getFactoryInstance(BridgeConfigFactory.class))
.getBridgeConfig();
bridgeConfig.setPortletConfig(portletConfig);
PortletContext portletContext = portletConfig.getPortletContext();
String bridgeParametersPrefix = Bridge.BRIDGE_PACKAGE_PREFIX + portletConfig.getPortletName() + ".";
// Check if Bridge should log messages
Boolean enableLogging = (Boolean) portletContext.getAttribute(bridgeParametersPrefix
+ BridgeLogger.LOGGING_ENABLED_PORTLET_INIT_PARAM);
bridgeConfig.getLogger().setEnabled(null != enableLogging ? enableLogging : Boolean.FALSE);
// Bridge Event Handler
bridgeConfig.setEventHandler((BridgeEventHandler) portletContext.getAttribute(bridgeParametersPrefix
+ Bridge.BRIDGE_EVENT_HANDLER));
// Public Render Parameter Handler
bridgeConfig.setPublicRenderParameterHandler((BridgePublicRenderParameterHandler) portletContext
.getAttribute(bridgeParametersPrefix + Bridge.BRIDGE_PUBLIC_RENDER_PARAMETER_HANDLER));
// Preserve Action Parameters
bridgeConfig.setPreserveActionParameters((Boolean) portletContext.getAttribute(bridgeParametersPrefix
+ Bridge.PRESERVE_ACTION_PARAMS));
// Excluded Request Attributes from Portlet definition
bridgeConfig.setExcludedRequestAttributes((List<String>) portletContext.getAttribute(bridgeParametersPrefix
+ Bridge.EXCLUDED_REQUEST_ATTRIBUTES));
// Lifecycle Id
String lifecycleId = portletContext.getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
if (null != lifecycleId && lifecycleId.trim().length() != 0) {
bridgeConfig.setLifecyleId(lifecycleId);
}
// Faces Suffixes. JSF 2.0 allows multiple
String suffixString = portletContext.getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
if (null == suffixString) {
suffixString = ViewHandler.DEFAULT_SUFFIX;
}
bridgeConfig.setFacesSuffixes(Arrays.asList(suffixString.split(" ")));
// Process web.xml content
WebXmlProcessor webXmlProc = new WebXmlProcessor(portletConfig.getPortletContext());
// Check if we have what we need to proceed
if (null == webXmlProc.getFacesServlet()) {
throw new BridgeException("No Faces Servlet defined in web.xml");
}
if (null == webXmlProc.getFacesServlet().getMappings()) {
throw new BridgeException("No Faces Servlet Mapping defined in web.xml");
}
// Retrieve Faces Servlet Mapping
bridgeConfig.setFacesServletMappings(webXmlProc.getFacesServlet().getMappings());
// Retrieve Error Page Mapping
bridgeConfig.setFacesErrorViewMappings(webXmlProc.getErrorViews());
// Retrieve faces-config.xml settings
// Excluded Request Attributes from Faces Config
List<String> excludedAttrs = FacesConfigProcessor.getExcludedAttributes();
if (null != excludedAttrs) {
bridgeConfig.getExcludedRequestAttributes().addAll(excludedAttrs);
}
// Public Parameter Mapping
Map<String, String> publicParams = FacesConfigProcessor.getPublicParameterMappings();
if (null != publicParams) {
bridgeConfig.getPublicRenderParameterMappings().putAll(publicParams);
}
// Write Behind Response Wrappers
bridgeConfig.setWriteBehindRenderResponseWrapper(createWrapper(FacesConfigProcessor
.getWriteBehindRenderResponseWrapperClassName()));
bridgeConfig.setWriteBehindResourceResponseWrapper(createWrapper(FacesConfigProcessor
.getWriteBehindResourceResponseWrapperClassName()));
// Default View Id Mappings
bridgeConfig.setDefaultViewMappings((Map<String, String>) portletContext.getAttribute(bridgeParametersPrefix
+ Bridge.DEFAULT_VIEWID_MAP));
if (null == bridgeConfig.getDefaultViewMappings() || 0 == bridgeConfig.getDefaultViewMappings().size()) {
throw new BridgeException("No JSF view id's defined in portlet.xml for " + portletConfig.getPortletName());
}
// PBR-495 - Parameter to determine whether HeadRenderer forces non self-closing script tags
String preventSelfClosing = portletContext.getInitParameter(PortletBridgeConstants.PREVENT_SELF_CLOSING_SCRIPT_TAG_PARAM);
if (preventSelfClosing != null) {
bridgeConfig.setPreventSelfClosingScriptTag(Boolean.parseBoolean(preventSelfClosing) ? true : false);
}
// PBR-510 - Parameter to determine whether f:viewParam will work during Render Response
String disableViewParam = portletContext.getInitParameter(PortletBridgeConstants.VIEW_PARAM_DISABLED);
if (disableViewParam != null) {
bridgeConfig.setViewParamHandlingDisabled(Boolean.parseBoolean(disableViewParam) ? true : false);
}
// PBR-516 - Parameter to determine whether to udpate the Bridge Request Scope with the result of an Ajax call
String ajaxEnabledParam = portletContext.getInitParameter(PortletBridgeConstants.SCOPE_ENABLED_ON_AJAX);
if (ajaxEnabledParam != null) {
bridgeConfig.setBridgeScopeEnabledOnAjaxRequest(Boolean.parseBoolean(ajaxEnabledParam) ? true : false);
}
// PBR-516 - Parameter to determine whether to store Faces Messages in Bridge Request Scope if
// PortletBridgeConstants.SCOPE_ENABLED_ON_AJAX is true
String facesMessagesEnabledParam = portletContext.getInitParameter(PortletBridgeConstants.FACES_MESSAGES_STORED_ON_AJAX);
if (facesMessagesEnabledParam != null) {
bridgeConfig.setFacesMessagesStoredOnAjaxRequest(Boolean.parseBoolean(facesMessagesEnabledParam) ? true : false);
}
// Retrieve name of session id parameter, if set
String sessionIdParameter = portletContext.getInitParameter(PortletBridgeConstants.SESSION_ID_PARAMETER_NAME);
if (sessionIdParameter != null && sessionIdParameter.trim().length() > 0) {
bridgeConfig.setSessionIdParameterName(sessionIdParameter);
}
// PBR-499 - Parameter to determine whether Bridge Scope is retained after Render
String bridgeScopeRetained = getInitParameter(portletConfig, PortletBridgeConstants.REQUEST_SCOPE_PRESERVED);
if (bridgeScopeRetained != null) {
bridgeConfig.setBridgeScopePreservedPostRender(Boolean.parseBoolean(bridgeScopeRetained) ? true : false);
}
// PBR-547 - Parameter to determine whether to shorten component namespaces
String componentNamespaceShortened = getInitParameter(portletConfig, PortletBridgeConstants.COMPONENT_NAMESPACE_SHORTENED);
if (componentNamespaceShortened != null) {
bridgeConfig.setComponentNamespaceShortened(Boolean.parseBoolean(componentNamespaceShortened) ? true : false);
}
// Determine whether we're running with JSF 2.2 Runtime or not
// Use FlashFactory presence to determine it
try {
this.getClass().getClassLoader().loadClass("javax.faces.context.FlashFactory");
bridgeConfig.setJsf22Runtime(true);
} catch (ClassNotFoundException e) {
// Default is already non JSF 2.2 runtime, so don't need to set again.
}
return bridgeConfig;
}
private String getInitParameter(PortletConfig portletConfig, String name) {
String parameter = portletConfig.getInitParameter(name);
if (parameter == null) {
parameter = portletConfig.getPortletContext().getInitParameter(name);
}
return parameter;
}
@SuppressWarnings("unchecked")
private Class<? extends BridgeWriteBehindResponse> createWrapper(String wrapperClassName) {
if (null != wrapperClassName) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
return (Class<? extends BridgeWriteBehindResponse>) loader.loadClass(wrapperClassName);
} catch (Exception e) {
bridgeConfig.getLogger().log(
BridgeLogger.Level.WARNING,
"Unable to instantiate BridgeWriteBehindResponse class: " + wrapperClassName + " due to "
+ e.getMessage());
return null;
}
}
return null;
}
/**
* @see javax.portlet.faces.Bridge#doFacesRequest(javax.portlet.ActionRequest, javax.portlet.ActionResponse)
*/
public void doFacesRequest(ActionRequest request, ActionResponse response) throws BridgeException {
assertParameters(request, response);
try {
initRequest(request, response, PortletPhase.ACTION_PHASE);
BridgeContext bridgeContext = getBridgeContext(request, response, PortletPhase.ACTION_PHASE);
bridgeController.processPortletAction(bridgeContext);
} finally {
finalizeRequest(request, BridgeContext.getCurrentInstance());
}
}
/**
* @see javax.portlet.faces.Bridge#doFacesRequest(javax.portlet.EventRequest, javax.portlet.EventResponse)
*/
public void doFacesRequest(EventRequest request, EventResponse response) throws BridgeException {
assertParameters(request, response);
try {
initRequest(request, response, PortletPhase.EVENT_PHASE);
BridgeContext bridgeContext = getBridgeContext(request, response, PortletPhase.EVENT_PHASE);
bridgeController.handlePortletEvent(bridgeContext);
} finally {
finalizeRequest(request, BridgeContext.getCurrentInstance());
}
}
/**
* @see javax.portlet.faces.Bridge#doFacesRequest(javax.portlet.RenderRequest, javax.portlet.RenderResponse)
*/
public void doFacesRequest(RenderRequest request, RenderResponse response) throws BridgeException {
assertParameters(request, response);
try {
initRequest(request, response, PortletPhase.RENDER_PHASE);
BridgeContext bridgeContext = getBridgeContext(request, response, PortletPhase.RENDER_PHASE);
bridgeController.renderPortletBody(bridgeContext);
} finally {
finalizeRequest(request, BridgeContext.getCurrentInstance());
}
}
/**
* @see javax.portlet.faces.Bridge#doFacesRequest(javax.portlet.ResourceRequest, javax.portlet.ResourceResponse)
*/
public void doFacesRequest(ResourceRequest request, ResourceResponse response) throws BridgeException {
assertParameters(request, response);
try {
initRequest(request, response, PortletPhase.RESOURCE_PHASE);
BridgeContext bridgeContext = getBridgeContext(request, response, PortletPhase.RESOURCE_PHASE);
bridgeController.renderResource(bridgeContext);
} finally {
finalizeRequest(request, BridgeContext.getCurrentInstance());
}
}
/**
* @see javax.portlet.faces.Bridge#destroy()
*/
public void destroy() {
if (this.initialized) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Destroy Portlet Bridge for " + this.bridgeConfig.getPortletConfig().getPortletName());
}
this.bridgeConfig = null;
this.bridgeController = null;
this.initialized = false;
}
}
protected void assertParameters(PortletRequest request, PortletResponse response) {
if (null == request) {
throw new IllegalArgumentException("PortletRequest parameter is null");
}
if (null == response) {
throw new IllegalArgumentException("PortletResponse parameter is null");
}
if (!initialized) {
throw new BridgeUninitializedException("JSF Portlet Bridge is not initialized");
}
}
protected void initRequest(PortletRequest request, PortletResponse response, PortletPhase phase) {
request.setAttribute(Bridge.PORTLET_LIFECYCLE_PHASE, phase);
}
protected BridgeContext getBridgeContext(PortletRequest request, PortletResponse response, PortletPhase phase) {
BridgeContext bridgeContext = ((BridgeContextFactory) BridgeFactoryFinder
.getFactoryInstance(BridgeContextFactory.class)).getBridgeContext(bridgeConfig);
bridgeContext.setPortletRequest(request);
bridgeContext.setPortletRequestPhase(phase);
bridgeContext.setPortletResponse(response);
bridgeContext.setPreFacesRequestAttrNames(Collections.list(request.getAttributeNames()));
fireFacesSystemEvent(bridgeContext, BridgeInitializeRequestSystemEvent.class);
return bridgeContext;
}
private void finalizeRequest(PortletRequest request, BridgeContext bridgeContext) {
request.removeAttribute(Bridge.PORTLET_LIFECYCLE_PHASE);
if (null != bridgeContext) {
releaseBridgeContext(bridgeContext);
}
}
private void releaseBridgeContext(BridgeContext bridgeContext) {
fireFacesSystemEvent(bridgeContext, BridgeDestroyRequestSystemEvent.class);
bridgeContext.release();
}
private void fireFacesSystemEvent(BridgeContext bridgeContext, Class<? extends SystemEvent> eventClass) {
FacesContext facesContext = FacesContext.getCurrentInstance();
boolean createdInitContext = false;
if (null == facesContext) {
ApplicationFactory factory = (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
facesContext = new InitFacesContext(factory.getApplication(), bridgeConfig.getPortletConfig().getPortletContext());
createdInitContext = true;
}
facesContext.getApplication().publishEvent(facesContext, eventClass, bridgeContext);
if (createdInitContext) {
facesContext.release();
}
}
}