/* * 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.bridge.context; import java.util.List; import java.util.Map; import javax.faces.context.FacesContext; import javax.portlet.PortletContext; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.faces.Bridge; import javax.portlet.faces.BridgeDefaultViewNotSpecifiedException; import javax.portlet.faces.BridgeInvalidViewPathException; import org.jboss.portletbridge.bridge.config.BridgeConfig; import org.jboss.portletbridge.bridge.scope.BridgeRequestScope; import org.jboss.portletbridge.bridge.scope.BridgeRequestScopeManager; /** * The <CODE>BridgeContext</CODE> contains the runtime specific state the bridge makes public so its various independent * pieces can work together to properly operate. Simplistically, the bridge is a controller and a set of Faces * extensions that work together execute Faces requests in a portlet container. By encapsulating state that is shared * between these pieces, an extensible, pluggable bridge is supported as bridge implementors can now extend a base * implementation by extending or overriding any of the Faces extensions (and other hook points) required to add their * specific behaviors. * <p> * A new <code>BridgeContext</code> is created in each bridge request <code>doFacesRequest()</code>. After the bridge * controller has initialized the context the <code>SystemEvent BridgeContextInitializedEvent</code> is dispatched to * any registered listeners for further transformation. Once the bridge has acquired the <code>FacesContext</code>, the * <code>BridgeContext</code> is added to the <code>FacesContext</code> scope making it freely available from anywhere * within Faces during that request. */ public abstract class BridgeContext { /** * Called at the end of bridge request processing. It releases associated state. */ public abstract void release(); /** * Sets the <code>PortletContext</code> of the portlet invoking this bridge. * * @param context */ public abstract void setPortletContext(PortletContext context); /** * * @return the <code>PortletContext</code> of the portlet invoking this bridge. */ public abstract PortletContext getPortletContext(); /** * Sets the <code>PortletRequest</code> that invoked the bridge. * * @param request */ public abstract void setPortletRequest(PortletRequest request); /** * * @return the <code>PortletRequest</code> that invoked the bridge. */ public abstract PortletRequest getPortletRequest(); /** * Sets the <code>PortletResponse</code> that invoked the bridge. * * @param response */ public abstract void setPortletResponse(PortletResponse response); /** * * @return the <code>PortletResponse</code> that invoked the bridge. */ public abstract PortletResponse getPortletResponse(); /** * Sets the request phase govering this context. Used to both detect whether one is executing in a portlet request * without referencing potential unbound classes as well as to determine the type of request and response one is * dealing with as there are distinct portlet request and response objects per portlet lifecycle phase. * <p> * Note: as per specification, its more typical for the Faces developer to rely on the request attribute containing * this same information. Its included in the BridgeContext primarily for bridge (extension) implementors. * * @param phase */ public abstract void setPortletRequestPhase(Bridge.PortletPhase phase); /** * Gets the request phase govering this context. Used to both detect whether one is executing in a portlet request * without referencing potential unbound classes as well as to determine the type of request and response one is * dealing with as there are distinct portlet request and response objects per portlet lifecycle phase. * <p> * Note: as per specification, its more typical for the Faces developer to rely on the request attribute containing * this same information. Its included in the BridgeContext primarily for bridge (extension) implementors. * * @return the current portlet request lifcycle phase we are executing in. */ public abstract Bridge.PortletPhase getPortletRequestPhase(); /** * Sets the <code>BridgeConfig</code> that corresponds to this instance. * * @param config */ public abstract void setBridgeConfig(BridgeConfig config); /** * * @return the <code>BridgeConfig</code> that corresponds to this instance. */ public abstract BridgeConfig getBridgeConfig(); /** * This <code>Map</code> is a place to put extra (implementation specific) bridge state or anything else whose * lifetime matches this scope. * * @return a mutable <code>Map<String, Object></code> of bridge context scoped attributes */ public abstract Map<String, Object> getAttributes(); // Default = true; always parts of the bridge read/write whether bridge request scope is preserved at the end of // this // request /** * Indicates whether the controller should preserve the bridge request scope. By specification there are a variety * of reasons why the bridge shouldn't preserve a request scope. Sometimes this is determined in one of the bridge's * Faces extensions that is separate from the controller. The dectector used this method to convery to the * controller its behavior when its done with the request and considers whether to save the bridge request scope or * not. <code>true</code> means the request scope should be preserved. <code>false</code> means the request scope * should not be preserved. The default is <code>true</code> * * @param preserve */ public abstract void setBridgeRequestScopePreserved(boolean preserve); /** * @return the <code>boolean</code> indicating whether the controller should preserve the bridge request scope or * not. <code>true</code> means the request scope should be preserved. <code>false</code> means the request * scope should not be preserved. The default is <code>true</code> */ public abstract boolean isBridgeRequestScopePreserved(); /** * Faces stores the current view state in a hidden field in the response. When this is resupplied as part of the * POSTBACK, Faces is able to restore the view and process the action. In the portlet model, however, not every * (following) request is a POSTBACK. The portal can ask the portlet to redisplay itself at any time merely because * something has else has occurred on the page (outside the portlet) that causes the portal to rerender the entire * page. As this isn't a POSTBACK the view state hidden field isn't supplied, yet Faces still needs to restore the * view from this state if its to properly re-render. To support this bridge must capture the value of this hidden * field and manage it in its state (often the bridge request scope). This capture involves using a Faces extension * (<code>StateManager</code>) to extract the parameter value and the controller to manage the state. The value is * conveyed between these two subsystems through here. This method sets the captured view state parameter value for * later retrieval. * * @param savedViewStateParam */ // Allows a StateManager to communicate the saved ViewState to the controller -- so controller // can retain for reference on portlet redisplay public abstract void setSavedViewStateParam(String savedViewStateParam); /** * Faces stores the current view state in a hidden field in the response. When this is resupplied as part of the * POSTBACK, Faces is able to restore the view and process the action. In the portlet model, however, not every * (following) request is a POSTBACK. The portal can ask the portlet to redisplay itself at any time merely because * something has else has occurred on the page (outside the portlet) that causes the portal to rerender the entire * page. As this isn't a POSTBACK the view state hidden field isn't supplied, yet Faces still needs to restore the * view from this state if its to properly re-render. To support this bridge must capture the value of this hidden * field and manage it in its state (often the bridge request scope). This capture involves using a Faces extension * (<code>StateManager</code>) to extract the parameter value and the controller to manage the state. The value is * conveyed between these two subsystems through here. * * @return the captured view state parameter. */ public abstract String getSavedViewStateParam(); /** * The bridge allows viewIds in the faces-config.xml to contain queryStrings. As the <code>NavigationHandler</code> * resolves such navigation and create the asscoiated view and Faces itself doesn't expect these query strings, the * bridge needs to extract this query string and stash its value for later retrieval/use by the controller which * encodes these extra parameters as part of its action response. * * @param queryString * query string portion of a viewId */ public abstract void setNavigationQueryString(String queryString); /** * The bridge allows viewIds in the faces-config.xml to contain queryStrings. As the <code>NavigationHandler</code> * resolves such navigation and create the associated view and Faces itself doesn't expect these query strings, the * bridge needs to extract this query string and stash its value for later retrieval/use by the controller which * encodes these extra parameters as part of its action response. * * @return the queryString portion associated with the viewId that created the current <code>UIViewRoot</code> */ public abstract String getNavigationalQueryString(); /** * The bridge is required to support redirects to other Faces views during render. As the portlet model doesn't * support redirects, the bridge controller must simulate the redirect by unwinding the current lifecycle execution * and restarting a new lifecycle execution based on the new target. In addition any additional query string * parameters must be represented in the request when this lifecycle is rerun. * <p> * Such redirects are detected in <code>ExternalContext.redirect()</code> but implemented by the controller. This * sets the query string portion of the redirect url (exclusive of the ?) so its available to the controller in * handling the render redirect. This method automatically calls <code>setRenderRedirect(true);</code> to flag that * a renderRedirect has been detected. * * @param queryString */ public abstract void setRenderRedirectQueryString(String queryString); /** * The bridge is required to support redirects to other Faces views during render. As the portlet model doesn't * support redirects, the bridge controller must simulate the redirect by unwinding the current lifecycle execution * and restarting a new lifecycle execution based on the new target. In addition any additional query string * parameters must be represented in the request when this lifecycle is rerun. * <p> * Such redirects are detected in <code>ExternalContext.redirect()</code> but implemented by the controller. * * @return prevously set query string (exclusive of the ?) portion of the render redirect url */ public abstract String getRenderRedirectQueryString(); /** * * @param viewId */ public abstract void setRedirectViewId(String viewId); /** * * @return prevously set redirect view id */ public abstract String getRedirectViewId(); /** * Sets whether a render redirect has occurred in this request. Generally this is set automatically by * <code>setRenderRedirectQueryString</code>. * * @param redirect */ public abstract void setRenderRedirect(boolean redirect); /** * * @return indication of whether a render redirect has occured. <code>true</code> if there has been one in this * request. Default is <code>false</code>. */ public abstract boolean hasRenderRedirect(); /** * Set if the renderRedirect occurred after the (JSP) dispatch. Needed because <code>dispatch.forward()</code> can't * be called twice in the same request. Setting this will allow the bridge to know to use * <code>dispatch.include()</code> for rendering the "redirected" view. * * @param afterDispatch */ public abstract void setRenderRedirectAfterDispatch(boolean afterDispatch); /** * * @return indication of whether the renderRedirect occurred before or after the dispatch. <code>true</code> * indicates the redirect ocurred after the dispatch. <code>false</code> by default. */ public abstract boolean hasRenderRedirectAfterDispatch(); // Holds a List of attr names that exist prior to acquriing Faces artifacts -- used // to properly clean things up post Lifecycle and in support of renderredirects /** * Sets the names of the attributes which existed before the <code>FacesContext</code> is acquired. Used for * attribute exclusion, request resetting before a render redirect, as well as allowing the FacesContext to release * request attrs during its release. * * @param names * list of attribute names that exist in the request scope before acquiring the <code>FacesContext</code> */ public abstract void setPreFacesRequestAttrNames(List<String> names); /** * * @return the names of the request attributes that existed before the <code>FacesContext</code> was acquired. */ public abstract List<String> getPreFacesRequestAttrNames(); // Set by the controller to communicate which parameters are part of the preserved ActionParameters // so the encodeActionURL can excldue them when encoding a self-referencing portlet: url -- i.e // one that is supposed to be a redisplay preserving the current state. /** * <code>Map</code> of the action parameters that have been preserved for this render request. Set by the controller * when processing a render so the <code>ExternalContext.encodeActionURL()</code> can exclude them from its encoding * of a self-referencing portlet: url. I.e. one that is supposed to redisplay preserving the existing state. * * @param actionParamMap * <code>Map</code> of the action parameters in this render request. */ public abstract void setPreservedActionParams(Map<String, String[]> actionParamMap); /** * @return previously set <code>Map</code> of the action parameters in this render request. */ public abstract Map<String, String[]> getPreservedActionParams(); /** * Bridge maintains in the session the last viewId accessed in each mode. This allows developers to use EL access to * get the viewId (with appropriate QS) to navigate back to the view that was left when a new mode was entered. Key * in the session is: javax.portlet.faces.viewIdHistory.<i>modeName</i>. value is the corresponding viewId with an * encoded QS containing the pertinent state (scope id, etc) to return you to the state you left. Prior to the first * request the bridge sets up a default history by adding one session attribute per supported mode. The value is the * default viewId configured for this mode for this portlet. This is done so a developers EL will always resolve to * non-null. * * @param mode * name of the portlet mode * @param viewId * JSF view that we are currently in/adding to the history. Includes a QS of the various state needed to * restore this view * @param preserveRenderParams * indicates whether the render params are carried into the history. Generally <code>true</code> is the * view's mode hasn't changed from what the bridge finds encoded in its render params and * <code>false</code> otherwise. */ public abstract void setViewHistory(String mode, String viewId, boolean preserveRenderParams); /** * Returns the last viewId rendered for a given mode. Allows one to return to the view you left when you entered a * new mode. Generally this isn't called directly other than potentially to verify that the attributes have * initially been set up as the primary use case is to allow developers to access this value directly from the * session object using EL. * * @param mode * @return */ public abstract String getViewHistory(String mode); /** * Returns the Faces (viewid) target as described by the request. Note: This value must always be calculated (never * cached). The value must reflect the target described by the request object contained in the * <code>ExternalContext</code> if available. If not, then the target is computed from the <code>BridgeContext's * PortletRequest</code> * * @param excludeQueryString * if <code>true</code> then only the target Faces viewId is returned regardless of whether the viewId * contains a query string. I.e. unlike core Faces, the bridge allows the default viewIds and viewIds in * the Faces navigation rules to contain query strings that augment the view. For example one can include * a new mode in a Faces navigation rule. This boolean controls whether this extra query string portion * of a viewid is returned or not. * * @return Returns the Faces (viewid) target as described by the request. Returns <code>null</code> if the request * doesn't explicitly specify a target or if the target specified in the request doesn't match the request's * <code>PortletMode</code>. When a target viewId is returned it may contain contain (an optional) query * string if not specifically excluded. */ public abstract String getFacesViewIdFromRequest(boolean excludeQueryString) throws BridgeInvalidViewPathException; /** * Returns the Faces (viewid) currently targeted. Note: This value must always be calculated (never cached). If the * <code>FacesContext</code> has a current <code>UIViewRoot</code>, it returns its id. Otherwise it computes the * target <code>viewId</code> as follows: returns the result of calling <code>getFacesViewIdFromRequest()</code> if * not null otherwise return the result of <code>getDefaultViewIdForRequest()</code>. * * @param excludeQueryString * if <code>true</code> then only the target Faces viewId is returned regardless of whether the viewId * contains a query string. I.e. unlike core Faces, the bridge allows the default viewIds and viewIds in * the Faces navigation rules to contain query strings that augment the view. For example one can include * a new mode in a Faces navigation rule. This boolean controls whether this extra query string portion * of a viewid is returned or not. * * @return Returns the Faces (viewid) target as described by the request. */ public abstract String getFacesViewId(boolean excludeQueryString) throws BridgeInvalidViewPathException; /** * Returns the Bridge request scope corresponding to current view and mode. I.e. the result of calling * <code>getFacesViewId</code> and <code>PortletRequest.getPortletMode().toString()</code>. * * @return the current Bridge request scope or null if no scope corresponding to the current view and mode */ public abstract BridgeRequestScope getBridgeScope(); /** * Returns the Bridge request scope corresponding to passed viewId and mode. * * @param viewId * scope's viewId * @param mode * scope's portlet mode * @return the current Bridge request scope or null if no scope corresponding to the current view and mode */ public abstract BridgeRequestScope getBridgeScope(String viewId, String mode); /** * * @return the Bridge's RequestScopeManager used by this portlet */ public abstract BridgeRequestScopeManager getBridgeRequestScopeManager(); /** * Returns the Faces (viewid) target as represented in the path. Basically, this does a path to viewId mapping based * on the Faces servlet mappings in the web.xml. I.e. if Faces is suffix mapped (*.jsf) then a path of /foo.jsf will * return foo.jsp (or whatever is the configured mapping from .jsf to .xxx). If Faces is prefix mapped (/faces/*) * then a path of /faces/foo.jsp will return foo.jsp. Note: * * @param path * contains encoded viewId to be extracted. <code>path</code> can either contain the context path or be * relative to it. If <code>path</code> contains a query string it is ignored. * * @return the target viewId extracted from the path. This viewId will never contain a query string. */ public abstract String getFacesViewIdFromPath(String path) throws BridgeInvalidViewPathException; /** * Returns the default Faces (viewid) target configured for the current request's <code>PortletMode</code>. The * value must use the request contained in the <code>ExternalContext</code> if available. If not, then the target is * computed from the <code>BridgeContext's * PortletRequest</code> * * @param excludeQueryString * if <code>true</code> then only the target Faces viewId is returned regardless of whether the viewId * contains a query string. I.e. unlike core Faces, the bridge allows the default viewIds and viewIds in * the Faces navigation rules to contain query strings that augment the view. For example one can include * a new mode in a Faces navigation rule. This boolean controls whether this extra query string portion * of a viewid is returned or not. * * @return Returns the default Faces (viewid) target configured for the current request's <code>PortletMode</code>. * The return value may contain contain (an optional) query string if not specifically excluded. */ public abstract String getDefaultFacesViewIdForRequest(boolean excludeQueryString) throws BridgeDefaultViewNotSpecifiedException; /** * Provide the ability to append the Client Window Id to urls that need to be encoded. This is only expected to result * in a modified url being returned when running under JSF 2.2. * * @param url URL that requires the Client Window Id appended to it. * @return String representing a URL that will have the Client Window Id appended when running under JSF 2.2. */ public abstract String appendClientWindowId(String url); // ---------------------------------------------------------- Static Methods /** * <p> * The <code>ThreadLocal</code> variable used to record the {@link FacesContext} instance for each processing * thread. * </p> */ private static ThreadLocal<BridgeContext> sInstance = new ThreadLocal<BridgeContext>() { protected BridgeContext initialValue() { return (null); } }; /** * Return the {@link BridgeContext} instance for the request that is being processed by the current thread. */ public static BridgeContext getCurrentInstance() { return (sInstance.get()); } /** * <p> * Set the {@link FacesContext} instance for the request that is being processed by the current thread. * </p> * * @param context * The {@link FacesContext} instance for the current thread, or <code>null</code> if this thread no * longer has a <code>FacesContext</code> instance. * */ protected static void setCurrentInstance(BridgeContext context) { if (context == null) { sInstance.remove(); } else { sInstance.set(context); } } /** * Convenience methods for classes that need to log without having or knowing whether they are currently executing * in a BridgeContext * * @param s * @param t */ public static void log(String s, Throwable t) { BridgeContext bCtx = getCurrentInstance(); if (bCtx != null) { bCtx.getBridgeConfig().getLogger().log(s, t); } } /** * Convenience methods for classes that need to log without having or knowing whether they are currently executing * in a BridgeContext * * @param s */ public static void log(String s) { BridgeContext bCtx = getCurrentInstance(); if (bCtx != null) { bCtx.getBridgeConfig().getLogger().log(s); } } }