/* * JBoss, Home of Professional Open Source. * Copyright 2012, 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.application; import java.io.IOException; import java.net.MalformedURLException; import java.util.Iterator; import java.util.Locale; import javax.faces.FacesException; import javax.faces.application.ViewHandler; import javax.faces.application.ViewHandlerWrapper; import javax.faces.component.UIViewRoot; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.portlet.PortletResponse; import javax.portlet.faces.Bridge; import javax.portlet.faces.BridgeUtil; import javax.portlet.faces.annotation.PortletNamingContainer; import javax.portlet.faces.component.PortletNamingContainerUIViewRoot; import org.jboss.portletbridge.bridge.context.BridgeContext; import org.jboss.portletbridge.bridge.logger.BridgeLogger; import org.jboss.portletbridge.bridge.logger.BridgeLogger.Level; import org.jboss.portletbridge.bridge.logger.JULLoggerImpl; import org.jboss.portletbridge.context.PortalActionURL; /** * @author asmirnov, <a href="http://community.jboss.org/people/kenfinni">Ken Finnigan</a> */ public class PortletViewHandler extends ViewHandlerWrapper { private static final BridgeLogger logger = new JULLoggerImpl(PortletViewHandler.class.getName()); ViewHandler parent; /** * @param parent */ public PortletViewHandler(ViewHandler parent) { super(); this.parent = parent; } @Override public Locale calculateLocale(FacesContext context) { Locale locale; if (BridgeUtil.isPortletRequest()) { locale = context.getExternalContext().getRequestLocale(); if (null == locale) { locale = super.calculateLocale(context); } else { // check if given locale is supported by JSF Application for (Iterator<Locale> i = context.getApplication().getSupportedLocales(); i.hasNext();) { Locale loc = i.next(); if (loc.equals(locale)) { break; } } // locale is not supported locale = super.calculateLocale(context); } } else { locale = super.calculateLocale(context); } return locale; } @Override public UIViewRoot createView(FacesContext facesContext, String viewId) { if (!BridgeUtil.isPortletRequest()) { return super.createView(facesContext, viewId); } viewId = evaluateUrl(facesContext, viewId); String queryString = null; try { PortalActionURL viewIdUrl = new PortalActionURL(viewId); viewId = viewIdUrl.getPath(); BridgeContext.getCurrentInstance().setNavigationQueryString(viewIdUrl.getQueryString()); queryString = viewIdUrl.getQueryString(); } catch (MalformedURLException e) { // Do nothing, it is ordinary view Id logger.log(Level.INFO, "Mailformed ViewId url", e); } UIViewRoot root = super.createView(facesContext, viewId); Class<? extends UIViewRoot> rootClass = root.getClass(); if (rootClass.getAnnotation(PortletNamingContainer.class) == null) { // Creates correct UIViewRoot with our NamingContainer if for some reason createComponent of // PortletApplicationImpl // was not called UIViewRoot portletRoot = new PortletNamingContainerUIViewRoot(); if (null != queryString && queryString.length() > 0) { portletRoot.setViewId(root.getViewId() + "?" + queryString); } else { portletRoot.setViewId(root.getViewId()); } portletRoot.setLocale(root.getLocale()); portletRoot.setRenderKitId(root.getRenderKitId()); portletRoot.setId(root.getId()); root = portletRoot; } else { if (null != queryString && queryString.length() > 0) { root.setViewId(root.getViewId() + "?" + queryString); } } Object response = facesContext.getExternalContext().getResponse(); if (response instanceof PortletResponse) { PortletResponse portletResponse = (PortletResponse) response; portletResponse.setProperty("X-JAVAX-PORTLET-IS-NAMESPACED", "true"); } return root; } public String getActionURL(FacesContext context, String url) { if (!BridgeUtil.isPortletRequest()) { return super.getActionURL(context, url); } // action URLs are processed by the bridge in encodeActionURL // however the bridge extends Faces navigation rule support in that it // allows a to-view-id element to contain an EL expression. // We recognize this EL expresion here and evaluate to a viewid // before delegating. Only executed during portlet request or AJAX // request // from portlet page. url = evaluateUrl(context, url); // Faces can't do suffix mapping (extension mapping) properly if there is a query string PortalActionURL viewIdUrl = null; try { viewIdUrl = new PortalActionURL(url); url = viewIdUrl.getPath(); } catch (MalformedURLException e) { // Do nothing, it is ordinary view Id logger.log(Level.INFO, "Mailformed ViewId url", e); } String actionURL = super.getActionURL(context, url); // Now add the parameters back on if (viewIdUrl.parametersSize() > 0) { int qMark = actionURL.indexOf('?'); if (qMark < 0) { actionURL += "?" + viewIdUrl.getQueryString(); } else { actionURL += "&" + viewIdUrl.getQueryString(); } } return actionURL; } protected String evaluateUrl(FacesContext context, String url) { if (url.startsWith("/#")) { url = url.substring(1); } if (url.startsWith("#")) { // evaluate this as an EL expression url = (String) context.getApplication().evaluateExpressionGet(context, url, String.class); if (url == null) { throw new FacesException("Evaluated view ID is null " + url); } } return url; } @Override public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException { if (!BridgeUtil.isPortletRequest()) { super.renderView(context, viewToRender); return; } // Get the renderPolicy from the init parameters ExternalContext externalContext = context.getExternalContext(); String renderPolicyParam = externalContext.getInitParameter(Bridge.RENDER_POLICY); Bridge.BridgeRenderPolicy renderPolicy; if (null == renderPolicyParam) { renderPolicy = Bridge.BridgeRenderPolicy.DEFAULT; } else { renderPolicy = Bridge.BridgeRenderPolicy.valueOf(renderPolicyParam); } if (renderPolicy == Bridge.BridgeRenderPolicy.ALWAYS_DELEGATE) { super.renderView(context, viewToRender); } else if (renderPolicy == Bridge.BridgeRenderPolicy.DEFAULT) { // https://jira.jboss.org/jira/browse/PBR-121 - save original // request/response objects. Object portletRequest = externalContext.getRequest(); Object portletResponse = externalContext.getResponse(); try { // IDEA - set ServletRequest/ServletResponse wrappers to // ExternalContext // to use original view handler functionality. super.renderView(context, viewToRender); } catch (Exception e) { logger.log(Level.DEBUG, "Error rendering view by parent ViewHandler, try to render as portletbridge JSP page", e); // Restore request/response objects if parent renderer change // them. if (portletRequest != externalContext.getRequest()) { externalContext.setRequest(portletRequest); } if (portletResponse != externalContext.getResponse()) { externalContext.setResponse(portletResponse); } // catch all throws and swallow -- falling through to our own // render // suppress rendering if "rendered" property on the component is // false if (viewToRender.isRendered()) { doRenderView(context, viewToRender); } } } else if (viewToRender.isRendered()) { // NEVER_DELEGATE doRenderView(context, viewToRender); } } private void doRenderView(FacesContext context, UIViewRoot viewToRender) throws IOException { getViewDeclarationLanguage(context, viewToRender.getViewId()).renderView(context, viewToRender); } @Override public ViewHandler getWrapped() { return parent; } }