You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by so...@apache.org on 2007/11/16 21:48:15 UTC
svn commit: r595816 [2/3] - in /myfaces/portlet-bridge/trunk:
api/src/main/java/javax/portlet/faces/
impl/src/main/java/org/apache/myfaces/portlet/faces/application/
impl/src/main/java/org/apache/myfaces/portlet/faces/bridge/
impl/src/main/java/org/apa...
Modified: myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/bridge/BridgeImpl.java
URL: http://svn.apache.org/viewvc/myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/bridge/BridgeImpl.java?rev=595816&r1=595815&r2=595816&view=diff
==============================================================================
--- myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/bridge/BridgeImpl.java (original)
+++ myfaces/portlet-bridge/trunk/impl/src/main/java/org/apache/myfaces/portlet/faces/bridge/BridgeImpl.java Fri Nov 16 12:48:15 2007
@@ -1,1012 +1,1199 @@
-/* Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-package org.apache.myfaces.portlet.faces.bridge;
-
-import java.io.IOException;
-import java.io.Serializable;
-
-import java.rmi.server.UID;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
-
-import javax.el.ELContext;
-import javax.el.ELContextEvent;
-import javax.el.ELContextListener;
-
-import javax.faces.FacesException;
-import javax.faces.FactoryFinder;
-import javax.faces.application.Application;
-import javax.faces.application.ApplicationFactory;
-import javax.faces.application.FacesMessage;
-import javax.faces.application.ViewHandler;
-import javax.faces.component.UIViewRoot;
-import javax.faces.context.ExternalContext;
-import javax.faces.context.FacesContext;
-
-import javax.faces.context.FacesContextFactory;
-import javax.faces.event.PhaseEvent;
-import javax.faces.event.PhaseId;
-import javax.faces.event.PhaseListener;
-import javax.faces.lifecycle.Lifecycle;
-import javax.faces.lifecycle.LifecycleFactory;
-import javax.faces.render.ResponseStateManager;
-import javax.faces.webapp.FacesServlet;
-
-import javax.portlet.ActionRequest;
-import javax.portlet.ActionResponse;
-import javax.portlet.PortalContext;
-import javax.portlet.PortletConfig;
-import javax.portlet.PortletContext;
-import javax.portlet.PortletPreferences;
-import javax.portlet.PortletRequest;
-import javax.portlet.PortletResponse;
-import javax.portlet.PortletSession;
-import javax.portlet.RenderRequest;
-import javax.portlet.RenderResponse;
-
-import javax.portlet.faces.Bridge;
-import javax.portlet.faces.BridgeException;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpSession;
-import javax.servlet.http.HttpSessionBindingEvent;
-import javax.servlet.http.HttpSessionBindingListener;
-
-import org.apache.myfaces.portlet.faces.bridge.wrapper.BridgeRenderRequestWrapper;
-import org.apache.myfaces.portlet.faces.util.config.WebConfigurationProcessor;
-import org.apache.myfaces.portlet.faces.context.PortletExternalContextImpl;
-
-public class BridgeImpl implements Bridge, ELContextListener, PhaseListener
-{
-
- /**
- *
- */
- private static final long serialVersionUID = -2181720908326762776L;
- private static final String REQUEST_SCOPE_LOCK = "org.apache.myfaces.portlet.faces.requestScopeLock";
- private static final String REQUEST_SCOPE_MAP = "org.apache.myfaces.portlet.faces.requestScopeMap";
- private static final String REQUEST_SCOPE_LISTENER = "org.apache.myfaces.portlet.faces.requestScopeWatch";
- private static final String FACES_VIEWROOT = "org.apache.myfaces.portlet.faces.facesViewRoot";
- private static final String FACES_MESSAGES = "org.apache.myfaces.portlet.faces.facesMessages";
- private static final String REQUEST_PARAMETERS = "org.apache.myfaces.portlet.faces.requestParameters";
- private static final String REQUEST_SCOPE_ID_RENDER_PARAM = "_bridgeRequestScopeId";
-
- private PortletConfig mPortletConfig = null;
- private boolean mPreserveActionParams = false;
-
- private FacesContextFactory mFacesContextFactory = null;
- private Lifecycle mLifecycle = null;
-
- private Vector mFacesMappings = null;
-
- private static final Integer sDefaultMaxManagedRequestScopes = new Integer(100);
-
- public BridgeImpl()
- {
- // everything gets done in the init call.
- }
-
- public void init(PortletConfig config) throws BridgeException
- {
- mPortletConfig = config;
- PortletContext portletContext = mPortletConfig.getPortletContext();
-
- // get preserveActionParams here because we use later in this class.
- // however don't process renderPolicy here because its used in
- // ViewHandler (and needs to be at request scope) -- hence this
- // is done in ExternalContext.
- Boolean configParam = (Boolean) portletContext.getAttribute(Bridge.BRIDGE_PACKAGE_PREFIX
- + mPortletConfig.getPortletName()
- + "."
- + Bridge.PRESERVE_ACTION_PARAMS);
- if (configParam != null)
- {
- mPreserveActionParams = configParam.booleanValue();
- }
-
- // Set up the synchronziation object for the RequestScopeMap as we don't
- // want to sync on the PortletContext because its too broad. Note:
- // needed
- // because we not only need to sync the Map but also creating the Map
- // and
- // putting it in the PortletContext. Hence the sync object allows us
- // to limit syncronizing the PortletContext to once per portlet (init
- // time);
- synchronized (portletContext)
- {
- Object lock = portletContext.getAttribute(REQUEST_SCOPE_LOCK);
- if (lock == null)
- {
- portletContext.setAttribute(REQUEST_SCOPE_LOCK, new Object());
- }
- }
-
- // Add self as ELContextListener to the Faces App so we can add the
- // portletConfig to any newly created contexts.
- ApplicationFactory appFactory = (ApplicationFactory) FactoryFinder
- .getFactory(FactoryFinder.APPLICATION_FACTORY);
- Application app = appFactory.getApplication();
- app.addELContextListener(this);
-
- // Process and cache the FacesServlet mappings for use by
- // ExternalContext
- WebConfigurationProcessor facesConfig = new WebConfigurationProcessor(portletContext);
- mFacesMappings = facesConfig.getFacesMappings();
- for (int i = 0; i < mFacesMappings.size(); i++)
- {
- portletContext.log("Mapping: " + (String) mFacesMappings.elementAt(i));
- }
- }
-
- public void doFacesRequest(ActionRequest request, ActionResponse response) throws BridgeException
- {
- Map m = null;
- // Set the Portlet lifecycle phase as a request attribute so its
- // available to Faces extensions -- allowing that code to NOT rely on
- // instanceof which can fail if a portlet container uses a single class
- // to implement both the action and render request/response objects
- request.setAttribute(Bridge.PORTLET_LIFECYCLE_PHASE, Bridge.PortletPhase.ActionPhase);
-
- // Set the FacesServletMapping attribute so the ExternalContext can
- // pick it up and use it to reverse map viewIds to paths
- if (mFacesMappings != null)
- {
- request.setAttribute(PortletExternalContextImpl.FACES_MAPPING_ATTRIBUTE, mFacesMappings);
- }
-
- // cache names of existing request attributes so can exclude them
- // from being saved in the bridge's request scope. Note: this is done
- // before
- // acquiring the FacesContext because its possible (though unlikely)
- // the application has inserted itself in this process and sets up
- // needed request attributes.
- String[] excludedAttributes = getExcludedAttributes(request);
-
- FacesContext context = null;
- try
- {
- // Get the FacesContext instance for this request
- context = getFacesContextFactory().getFacesContext(mPortletConfig, request, response,
- getLifecycle());
- logMap("Received ActionParams: ", context.getExternalContext().getRequestParameterValuesMap());
-
- // Each action starts a new "action lifecycle"
- // The Bridge preserves request scoped data and if so configured
- // Action Parameters for the duration of an action lifecycle
- String scopeId = initBridgeRequestScope(response);
-
- // For actions we only execute the lifecycle phase
- getLifecycle().execute(context);
-
- // Check responseComplete -- if responseComplete the
- // lifecycle.execute
- // resulted in a redirect navigation. To preserve Faces semantics
- // the viewState isn't preserved nor is the data associated with the
- // action lifecycle.
-
- if (!context.getResponseComplete())
- {
- // navigation didn't redirect
-
- // Before preserving the request scope data in the bridge's
- // request scope,
- // put the Faces view into request scope. This is done because
- // JSF 1.2 manages the tree save state opaquely exclusively in
- // the render phase -- I.e. there is no JSF 1.2 way of having
- // the
- // bridge manually save and restore the view
- saveFacesView(context);
-
- // Spec requires we preserve the FACES_VIEW_STATE parameter
- // in addition the portlet may be configured to preserve the
- // rest of them.
- saveActionParams(context);
-
- // Because the portlet model doesn't execute its render phase
- // within the same request scope but Faces does (assumes this),
- // preserve the request scope data and the Faces view tree at
- // RequestScope.
- saveBridgeRequestScopeData(context, scopeId, excludedAttributes);
-
- // Finalize the action response -- key here is the reliance on
- // ExternalContext.encodeActionURL to migrate info encoded
- // in the actionURL constructed from the target of this
- // navigation
- // into the ActionResponse so it can be decoded in the
- // asscoicated portlet render.
-
- finalizeActionResponse(context);
-
- }
- }
- catch (Exception e)
- {
- context.getExternalContext().log("Exception thrown in doFacesRequest:action", e);
- if (!(e instanceof BridgeException))
- {
- Throwable rootCause = e.getCause();
- throw new BridgeException(e.getMessage(), rootCause);
- }
- else
- {
- throw (BridgeException) e;
- }
- }
- finally
- {
- if (context != null)
- {
- m = context.getExternalContext().getRequestMap();
- context.release();
- }
- }
-
- logMap("Action completed: ", m);
- }
-
- public void doFacesRequest(RenderRequest request, RenderResponse response) throws BridgeException
- {
-
- // Set the Portlet lifecycle phase as a request attribute so its
- // available to Faces extensions -- allowing that code to NOT rely on
- // instanceof which can fail if a portlet container uses a single class
- // to implement both the action and render request/response objects
- request.setAttribute(Bridge.PORTLET_LIFECYCLE_PHASE, Bridge.PortletPhase.RenderPhase);
-
- // Set the FacesServletMapping attribute so the ExternalContext can
- // pick it up and use it to reverse map viewIds to paths
- if (mFacesMappings != null)
- {
- request.setAttribute(PortletExternalContextImpl.FACES_MAPPING_ATTRIBUTE, mFacesMappings);
- }
-
- FacesContext context = null;
- try
- {
- // Get the FacesContext instance for this request
- Lifecycle lifecycle = getLifecycle();
- context = getFacesContextFactory().getFacesContext(mPortletConfig, request, response,
- lifecycle);
- ExternalContext extCtx = context.getExternalContext();
-
- logMap("Received RenderParams: ", context.getExternalContext().getRequestParameterValuesMap());
-
- // Use request from ExternalContext in case its been wrapped by an
- // extension
- RenderRequest extRequest = (RenderRequest) extCtx.getRequest();
-
- String scopeId = extRequest.getParameter(REQUEST_SCOPE_ID_RENDER_PARAM);
-
- if (restoreBridgeRequestScopeData(context, scopeId))
- {
- // Because the Bridge is required to always save/restore the
- // VIEW_STATE
- // parameter -- always attempt a restore
- extRequest = restoreActionParams(context);
-
- // only restores if first render after action
- // afterwards not restored from Bridge request scope
- // rather its saved/restored by Faces.
- restoreFacesView(context, scopeId);
- }
-
- // Ensure the ContentType is set before rendering
- if (extCtx.getResponseContentType() == null)
- {
- response.setContentType(extRequest.getResponseContentType());
- }
-
- // ensure that isPostback attribute set if VIEW_STATE param exists
- if (extCtx.getRequestParameterValuesMap().containsKey(ResponseStateManager.VIEW_STATE_PARAM))
- {
- extCtx.getRequestMap().put(Bridge.IS_POSTBACK_ATTRIBUTE, Boolean.TRUE);
- }
-
- // Note: if the scope wasn't restored then the Faces
- // FACES_VIEW_STATE
- // parameter will not have been carried into this render and hence
- // default Faces impls will not see this render as occuring in a
- // in a postback (isPostback() will return false. This means Faces
- // will create a new Tree instead of restoring one -- the semantics
- // one should get if the Bridge can't access its requestScope.
-
- // if the requestScope restored the ViewRoot then this must be
- // the first render after the action -- hence the tree isn't yet
- // stored/managed by Faces -- we can merely render it
- if (context.getViewRoot() == null)
- {
- // add self as PhaseListener to prevent action phases from
- // executing
- lifecycle.addPhaseListener(this);
- try
- {
- lifecycle.execute(context);
- }
- catch (Exception e)
- {
- ;
- }
- finally
- {
- lifecycle.removePhaseListener(this);
- }
- }
- getLifecycle().render(context);
-
- }
- catch (Exception e)
- {
- context.getExternalContext().log("Exception thrown in doFacesRequest:render", e);
- if (!(e instanceof BridgeException))
- {
- Throwable rootCause = e.getCause();
- throw new BridgeException(e.getMessage(), rootCause);
- }
- else
- {
- throw (BridgeException) e;
- }
- }
- finally
- {
- if (context != null)
- {
- context.release();
- }
- }
- }
-
- public void destroy()
- {
- // remove any scopes being managed for this portlet
- // Each scope has a per portlet prefix -- pass in the prefix
- // constructed by adding the prefix to an empty string.
- // removeRequestScopes(qualifyScopeId(""));
-
- mPortletConfig = null;
- }
-
- /**
- * ELContextListener impl
- */
- public void contextCreated(ELContextEvent ece)
- {
- // Add the portletConfig to the ELContext so it is evaluated
- ELContext elContext = ece.getELContext();
-
- // Only add if not already there
- if (elContext.getContext(PortletConfig.class) == null)
- {
- elContext.putContext(PortletConfig.class, mPortletConfig);
- }
- }
-
- private FacesContextFactory getFacesContextFactory() throws BridgeException
- {
- try
- {
- if (mFacesContextFactory == null)
- {
- mFacesContextFactory = (FacesContextFactory) FactoryFinder
- .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
- }
- return mFacesContextFactory;
- }
- catch (FacesException e)
- {
- Throwable rootCause = e.getCause();
- throw new BridgeException(e.getMessage(), rootCause);
- }
- }
-
- private Lifecycle getLifecycle() throws BridgeException
- {
- try
- {
- if (mLifecycle == null)
- {
- LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
- .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
- String lifecycleId = mPortletConfig.getPortletContext()
- .getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
- if (lifecycleId == null)
- {
- lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
- }
-
- mLifecycle = lifecycleFactory.getLifecycle(lifecycleId);
- }
- return mLifecycle;
- }
- catch (FacesException e)
- {
- Throwable rootCause = e.getCause();
- throw new BridgeException(e.getMessage(), rootCause);
- }
- }
-
- private void saveFacesView(FacesContext context)
- {
-
- // first save any current Faces messages in the viewRoot
- saveFacesMessageState(context);
-
- // now place the viewRoot in the request scope
- Map requestMap = context.getExternalContext().getRequestMap();
- requestMap.put(FACES_VIEWROOT, context.getViewRoot());
- }
-
- private void restoreFacesView(FacesContext context, String scopeId)
- {
- Map requestMap = context.getExternalContext().getRequestMap();
- UIViewRoot viewRoot = (UIViewRoot) requestMap.get(FACES_VIEWROOT);
- if (viewRoot != null)
- {
- context.setViewRoot(viewRoot);
- // remove from current Request Scope and the saved Bridge Request
- // Scope
- requestMap.remove(FACES_VIEWROOT);
- removeFromBridgeRequestScopeData(context, scopeId, FACES_VIEWROOT);
- }
- restoreFacesMessageState(context);
- // Don't remove the messages as Faces doesn't save these during render
- }
-
- private void saveActionParams(FacesContext context)
- {
- // Always preserve the FACES_VIEW_STATE parameter as per spec.
- // If portlet requests it, also preserve the rst of them.
- ExternalContext ec = context.getExternalContext();
- Map requestMap = ec.getRequestMap();
- Map requestParameterMap = ec.getRequestParameterValuesMap();
- if (!mPreserveActionParams)
- {
- if (requestMap != null && requestParameterMap != null
- && requestParameterMap.containsKey(ResponseStateManager.VIEW_STATE_PARAM))
- {
- HashMap m = new HashMap(1);
- m.put(ResponseStateManager.VIEW_STATE_PARAM,
- requestParameterMap.get(ResponseStateManager.VIEW_STATE_PARAM));
- requestMap.put(REQUEST_PARAMETERS, m);
- }
- }
- else
- {
- // place the parameter map in the portlet request scope
- // so it will be promoted into the Bridge's request scope and hence
- // be available during render.
- requestMap.put(REQUEST_PARAMETERS, requestParameterMap);
- }
- }
-
- private RenderRequest restoreActionParams(FacesContext context)
- {
- // this is a little trickier then saving because there is no
- // corresponding set. Instead we wrap the request object and set it
- // on the externalContext.
- ExternalContext ec = context.getExternalContext();
- // Note: only available/restored if this scope was restored.
- Map m = (Map) ec.getRequestMap().get(REQUEST_PARAMETERS);
-
- // ensures current request returned if nothing to restore/wrap
- RenderRequest wrapped = (RenderRequest) ec.getRequest();
- if (m != null && !m.isEmpty())
- {
- wrapped = new BridgeRenderRequestWrapper(wrapped, m);
- ec.setRequest(wrapped);
- }
- return wrapped;
- }
-
- public void saveFacesMessageState(FacesContext context)
- {
- Iterator messages;
- // get the messages from Faces Context
- Iterator clientIds = context.getClientIdsWithMessages();
- if (clientIds.hasNext())
- {
- FacesMessageState state = new FacesMessageState();
- while (clientIds.hasNext())
- {
- String clientId = (String) clientIds.next();
- messages = context.getMessages(clientId);
- while (messages.hasNext())
- {
- state.addMessage(clientId, (FacesMessage) messages.next());
- }
- }
-
- context.getExternalContext().log("PortletPhaseListenerImpl.saveFacesMessageState()");
-
- // save state in ViewRoot attributes
- Map requestMap = context.getExternalContext().getRequestMap();
- requestMap.put(FACES_MESSAGES, state);
- }
- }
-
- public void restoreFacesMessageState(FacesContext context)
- {
- // Only restore for Render request
- if (context.getExternalContext().getRequest() instanceof RenderRequest)
- {
- Map map = context.getExternalContext().getRequestMap();
-
- // restoring FacesMessages
- FacesMessageState state1 = (FacesMessageState) map.get(FACES_MESSAGES);
-
- if (state1 != null)
- {
- context.getExternalContext().log("PortletPhaseListenerImpl.restoreFacesMessageState()");
- Iterator messages;
- Iterator clientIds = state1.getClientIds();
- while (clientIds.hasNext())
- {
- String clientId = (String) clientIds.next();
- messages = state1.getMessages(clientId);
- while (messages.hasNext())
- {
- context.addMessage(clientId, (FacesMessage) messages.next());
- }
- }
- }
- }
- }
-
- private String initBridgeRequestScope(ActionResponse response)
- {
-
- // Generate an RMI UID, which is a unique identifier WITHIN the local
- // host. This will be used as the new lifecyleID
- UID uid = new UID();
- String requestScopeId = qualifyScopeId(uid.toString());
-
- // set in response render parameter so will receive in future calls
- // however don't store internally until there is specific state to
- // manage
- response.setRenderParameter(REQUEST_SCOPE_ID_RENDER_PARAM, requestScopeId);
-
- return requestScopeId;
- }
-
- private void saveBridgeRequestScopeData(FacesContext context, String scopeId, String[] excludeList)
- {
-
- // TODO -- check config setting and if stipulated preserve ActionParams
-
- // Store the RequestMap @ the bridge's request scope
- putBridgeRequestScopeData(scopeId, copyRequestMap(context.getExternalContext().getRequestMap(),
- excludeList));
-
- // flag the data so can remove it if the session terminates
- // as its unlikely useful if the session disappears
- watchScope(context, scopeId);
- }
-
- private void putBridgeRequestScopeData(String scopeId, Object o)
- {
- PortletContext portletContext = mPortletConfig.getPortletContext();
-
- // Get the request scope lock -- because its added during init it should
- // always be there.
- synchronized (portletContext.getAttribute(REQUEST_SCOPE_LOCK))
- {
- // get the managedScopeMap
- LRUMap requestScopeMap = (LRUMap) portletContext.getAttribute(REQUEST_SCOPE_MAP);
-
- if (requestScopeMap == null)
- {
- // see if portlet has defined how many requestScopes to manage
- // for this portlet
- Integer managedScopes = (Integer) portletContext
- .getAttribute(Bridge.MAX_MANAGED_REQUEST_SCOPES);
- if (managedScopes == null || managedScopes.intValue() <= 0)
- {
- managedScopes = sDefaultMaxManagedRequestScopes;
- }
-
- requestScopeMap = new LRUMap(managedScopes.intValue());
- portletContext.setAttribute(REQUEST_SCOPE_MAP, requestScopeMap);
- }
-
- logMap("Saving RequestScope @ requestScopeId: " + scopeId, (Map) o);
- requestScopeMap.put(scopeId, o);
- }
- }
-
- private Map copyRequestMap(Map m, String[] excludeList)
- {
- HashMap copy = new HashMap(m.size());
-
- Set keySet = m.keySet();
- if (keySet != null)
- {
- Iterator keys = keySet.iterator();
- while (keys != null && keys.hasNext())
- {
- String requestAttrKey = (String) keys.next();
- Object requestAttrValue = m.get(requestAttrKey);
- // TODO -- restore the ACTION PARAMS if there
-
- // Don't copy any of the portlet or Faces objects
- if (!inExcludeList(excludeList, requestAttrKey)
- && !contextObject(requestAttrKey, requestAttrValue))
- {
- copy.put(requestAttrKey, requestAttrValue);
- }
- }
- }
- return copy;
- }
-
- private boolean inExcludeList(String[] excludeList, String key)
- {
- if (excludeList == null || excludeList.length <= 0)
- {
- return false;
- }
- for (String element : excludeList)
- {
- if (element.equals(key))
- {
- return true;
- }
- }
- return false;
- }
-
- private String[] getExcludedAttributes(PortletRequest request)
- {
- String[] names = null;
-
- // first count them to allocate the array
- int i = 0, count = 0;
- Enumeration e = request.getAttributeNames();
- if (e == null)
- {
- return null;
- }
-
- while (e.hasMoreElements())
- {
- e.nextElement();
- count += 1;
- }
-
- names = new String[count];
- e = request.getAttributeNames();
- if (e == null)
- {
- return null;
- }
-
- while (e.hasMoreElements() && i < count)
- {
- names[i++] = (String) e.nextElement();
- }
- return names;
- }
-
- private boolean contextObject(String s, Object o)
- {
- return o instanceof PortletConfig || o instanceof PortletContext || o instanceof PortletRequest
- || o instanceof PortletResponse || o instanceof PortletSession
- || o instanceof PortletPreferences || o instanceof PortalContext
- || o instanceof FacesContext || o instanceof ExternalContext
- || o instanceof ServletConfig || o instanceof ServletContext
- || o instanceof ServletRequest || o instanceof ServletResponse
- || o instanceof HttpSession || s.startsWith("javax.servlet.include");
- }
-
- private void logMap(String message, Map m)
- {
- PortletContext context = mPortletConfig.getPortletContext();
-
- context.log(message);
- Set keySet = m.keySet();
- if (keySet != null)
- {
- Iterator keys = keySet.iterator();
- while (keys != null && keys.hasNext())
- {
- String requestAttrKey = (String) keys.next();
- context.log(" Map entry: " + requestAttrKey);
- }
- }
- else
- {
- context.log("Map is empty");
- }
-
- context.log("logMap completed");
- }
-
- private boolean restoreBridgeRequestScopeData(FacesContext context, String scopeId)
- throws BridgeException
- {
-
- PortletContext portletContext = mPortletConfig.getPortletContext();
- Map m = null;
- Map requestMap = context.getExternalContext().getRequestMap();
-
- if (scopeId == null)
- {
- return false;
- }
-
- // Get the data from the scope
- synchronized (portletContext.getAttribute(REQUEST_SCOPE_LOCK))
- {
- // get the managedScopeMap
- LRUMap requestScopeMap = (LRUMap) portletContext.getAttribute(REQUEST_SCOPE_MAP);
- // No scope for all renders before first action to this portletApp
- if (requestScopeMap == null)
- {
- return false;
- }
-
- m = (Map) requestScopeMap.get(scopeId);
- if (m == null)
- {
- return false;
- }
-
- logMap("Restoring scope: " + scopeId, m);
- }
-
- // Restore it as the RequestMap
- Set keySet = m.keySet();
- if (keySet != null)
- {
- Iterator keys = keySet.iterator();
- while (keys != null && keys.hasNext())
- {
- String requestAttrKey = (String) keys.next();
- Object requestAttrValue = m.get(requestAttrKey);
-
- requestMap.put(requestAttrKey, requestAttrValue);
- }
- }
- else
- {
- return false;
- }
- // Let's see what actually made it into the Map
- logMap("Restored RequestMap: ", context.getExternalContext().getRequestMap());
-
- return true;
- }
-
- private boolean removeFromBridgeRequestScopeData(FacesContext context, String scopeId, String key)
- {
- PortletContext portletContext = mPortletConfig.getPortletContext();
- Map m = null;
- Map requestMap = context.getExternalContext().getRequestMap();
-
- if (scopeId == null)
- {
- return false;
- }
-
- // Get the data from the scope
- synchronized (portletContext.getAttribute(REQUEST_SCOPE_LOCK))
- {
- // get the managedScopeMap
- LRUMap requestScopeMap = (LRUMap) portletContext.getAttribute(REQUEST_SCOPE_MAP);
- // No scope for all renders before first action to this portletApp
- if (requestScopeMap == null)
- {
- return false;
- }
-
- m = (Map) requestScopeMap.get(scopeId);
- if (m != null)
- {
- return m.remove(key) != null;
- }
-
- }
-
- return false;
- }
-
- /*
- * Takes in the scopeId and prefixes it with portletName: This is done so we can later remove this
- * portlet's managed scopes when needed
- */
-
- private String qualifyScopeId(String scopeId)
- {
- StringBuffer sb = new StringBuffer(mPortletConfig.getPortletName());
- sb.append(':');
- sb.append(scopeId);
- return sb.toString();
- }
-
- private void watchScope(FacesContext context, String scopeId)
- {
- PortletSession session = (PortletSession) context.getExternalContext().getSession(true);
- if (session != null)
- {
- RequestScopeListener scopeListener = (RequestScopeListener) session
- .getAttribute(REQUEST_SCOPE_LISTENER);
- if (scopeListener == null)
- {
- // only store the qualified prefix
- // if invalidated we walk the entire REQUEST_SCOPE Map and
- // remove
- // every scope that starts with this prefix.
- session.setAttribute(REQUEST_SCOPE_LISTENER, new RequestScopeListener(qualifyScopeId("")));
- }
- }
- }
-
- private void finalizeActionResponse(FacesContext context) throws IOException
- {
-
- // We rely on Faces ExternalContext.encodeActionURL to do the heavy
- // lifting here. First we construct a true actionURL using the viewId
- // for the view that is the target of the navigation. Then we call
- // encodeActionURL passing this URL. encodeActionURL encodes into
- // ActionResponse sufficient information (pulled from the supplied
- // actionURL) so that it (the EXteranlContext) can decode the
- // information
- // in the subsequent render request(s).
-
- ViewHandler viewHandler = context.getApplication().getViewHandler();
- String actionURL = viewHandler.getActionURL(context, context.getViewRoot().getViewId());
- String encodedActionURL = context.getExternalContext().encodeActionURL(actionURL);
-
- // Strictly speaking this is a redundant call (noop) as
- // ExternalContext.redirect() JSR 301 rules require redirects of
- // URLs containing viewIds (aka actionURLs) to be handled as
- // regular Faces navigation not full client redirects. Its
- // included here primarily to ensure that redirect is implemented
- // correctly.
- context.getExternalContext().redirect(encodedActionURL);
- }
-
- /* Implement the PhaseListener methods */
-
- public PhaseId getPhaseId()
- {
- return PhaseId.RESTORE_VIEW;
- }
-
- public void beforePhase(PhaseEvent event)
- {
- // do nothing
- return;
- }
-
- public void afterPhase(PhaseEvent event)
- {
- // only set renderresponse if in RESTORE_VIEW phase
- if (event.getPhaseId() == PhaseId.RESTORE_VIEW)
- {
- event.getFacesContext().renderResponse();
- }
- }
-
- private final class LRUMap extends LinkedHashMap
- {
-
- /**
- *
- */
- private static final long serialVersionUID = 4372455368577337965L;
- private int mMaxCapacity;
-
- public LRUMap(int maxCapacity)
- {
- super(maxCapacity, 1.0f, true);
- mMaxCapacity = maxCapacity;
- }
-
- @Override
- protected boolean removeEldestEntry(Map.Entry eldest)
- {
- return size() > mMaxCapacity;
- }
-
- }
-
- // TODO: Should we store these as attributes of the ViewTree??? It would
- // work as
- // everything is serializable. -- Issue is we need to implement a
- // PhaseListener to
- // to deal with this -- at the moment I prefer to isolate the Faces
- // extensions from this
- // detail and leave it all in this controller part.
-
- private final class FacesMessageState implements Serializable
- {
- /**
- *
- */
- private static final long serialVersionUID = 8438070672451887050L;
- // For saving and restoring FacesMessages
- private Map mMessages = new HashMap(); // key=clientId;
-
- // value=FacesMessages
-
- public void addMessage(String clientId, FacesMessage message)
- {
- List list = (List) mMessages.get(clientId);
- if (list == null)
- {
- list = new ArrayList();
- mMessages.put(clientId, list);
- }
- list.add(message);
- }
-
- public Iterator getMessages(String clientId)
- {
- List list = (List) mMessages.get(clientId);
- if (list != null)
- {
- return list.iterator();
- }
- else
- {
- return Collections.EMPTY_LIST.iterator();
- }
- }
-
- public Iterator getClientIds()
- {
- return mMessages.keySet().iterator();
- }
- }
-
- private final class RequestScopeListener implements HttpSessionBindingListener
- {
- String mScopePrefix = null;
-
- public RequestScopeListener(String scopePrefix)
- {
- mScopePrefix = scopePrefix;
- }
-
- public void valueBound(HttpSessionBindingEvent event)
- {
-
- }
-
- public void valueUnbound(HttpSessionBindingEvent event)
- {
- // Call is in the BridgeImpl class
- // removeRequestScopes((String)event.getValue());
- }
-
- }
-}
+/* Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package org.apache.myfaces.portlet.faces.bridge;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+import java.rmi.server.UID;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.el.ELContext;
+import javax.el.ELContextEvent;
+import javax.el.ELContextListener;
+
+import javax.faces.FacesException;
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.application.FacesMessage;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+import javax.faces.context.FacesContextFactory;
+import javax.faces.event.PhaseEvent;
+import javax.faces.event.PhaseId;
+import javax.faces.event.PhaseListener;
+import javax.faces.lifecycle.Lifecycle;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.ResponseStateManager;
+import javax.faces.webapp.FacesServlet;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.PortalContext;
+import javax.portlet.PortletConfig;
+import javax.portlet.PortletContext;
+import javax.portlet.PortletPreferences;
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletResponse;
+import javax.portlet.PortletSession;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+
+import javax.portlet.faces.Bridge;
+import javax.portlet.faces.BridgeException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+
+import org.apache.myfaces.portlet.faces.bridge.wrapper.BridgeRenderRequestWrapper;
+import org.apache.myfaces.portlet.faces.util.config.WebConfigurationProcessor;
+import org.apache.myfaces.portlet.faces.context.PortletExternalContextImpl;
+
+public class BridgeImpl
+ implements Bridge, ELContextListener, PhaseListener
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -2181720908326762776L;
+ private static final String REQUEST_SCOPE_LOCK =
+ "org.apache.myfaces.portlet.faces.requestScopeLock";
+ private static final String REQUEST_SCOPE_MAP =
+ "org.apache.myfaces.portlet.faces.requestScopeMap";
+ private static final String REQUEST_SCOPE_LISTENER =
+ "org.apache.myfaces.portlet.faces.requestScopeWatch";
+ private static final String FACES_VIEWROOT = "org.apache.myfaces.portlet.faces.facesViewRoot";
+ private static final String FACES_MESSAGES = "org.apache.myfaces.portlet.faces.facesMessages";
+ // public so PortletStateManager can see/use
+ public static final String UPDATED_VIEW_STATE_PARAM =
+ "org.apache.myfaces.portlet.faces.updatedViewStateParam";
+ private static final String REQUEST_PARAMETERS =
+ "org.apache.myfaces.portlet.faces.requestParameters";
+ private static final String REQUEST_SCOPE_ID_RENDER_PARAM = "_bridgeRequestScopeId";
+
+ private PortletConfig mPortletConfig = null;
+ private boolean mPreserveActionParams = false;
+
+ private FacesContextFactory mFacesContextFactory = null;
+ private Lifecycle mLifecycle = null;
+
+ private Vector mFacesMappings = null;
+
+ private static final Integer sDefaultMaxManagedRequestScopes = new Integer(100);
+
+ public BridgeImpl()
+ {
+ // everything gets done in the init call.
+ }
+
+ public void init(PortletConfig config)
+ throws BridgeException
+ {
+ mPortletConfig = config;
+ PortletContext portletContext = mPortletConfig.getPortletContext();
+
+ // get preserveActionParams here because we use later in this class.
+ // however don't process renderPolicy here because its used in
+ // ViewHandler (and needs to be at request scope) -- hence this
+ // is done in ExternalContext.
+ Boolean configParam =
+ (Boolean) portletContext.getAttribute(Bridge.BRIDGE_PACKAGE_PREFIX + mPortletConfig.getPortletName() +
+ "." + Bridge.PRESERVE_ACTION_PARAMS);
+ if (configParam != null)
+ {
+ mPreserveActionParams = configParam.booleanValue();
+ }
+
+ // Set up the synchronziation object for the RequestScopeMap as we don't
+ // want to sync on the PortletContext because its too broad. Note:
+ // needed
+ // because we not only need to sync the Map but also creating the Map
+ // and
+ // putting it in the PortletContext. Hence the sync object allows us
+ // to limit syncronizing the PortletContext to once per portlet (init
+ // time);
+ synchronized (portletContext)
+ {
+ Object lock = portletContext.getAttribute(REQUEST_SCOPE_LOCK);
+ if (lock == null)
+ {
+ portletContext.setAttribute(REQUEST_SCOPE_LOCK, new Object());
+ }
+ }
+
+ // Add self as ELContextListener to the Faces App so we can add the
+ // portletConfig to any newly created contexts.
+ ApplicationFactory appFactory =
+ (ApplicationFactory) FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
+ Application app = appFactory.getApplication();
+ app.addELContextListener(this);
+
+ // Process and cache the FacesServlet mappings for use by
+ // ExternalContext
+ WebConfigurationProcessor facesConfig = new WebConfigurationProcessor(portletContext);
+ mFacesMappings = facesConfig.getFacesMappings();
+ for (int i = 0; i < mFacesMappings.size(); i++)
+ {
+ portletContext.log("Mapping: " + (String) mFacesMappings.elementAt(i));
+ }
+ }
+
+ public void doFacesRequest(ActionRequest request, ActionResponse response)
+ throws BridgeException
+ {
+ Map m = null;
+ // Set the Portlet lifecycle phase as a request attribute so its
+ // available to Faces extensions -- allowing that code to NOT rely on
+ // instanceof which can fail if a portlet container uses a single class
+ // to implement both the action and render request/response objects
+ request.setAttribute(Bridge.PORTLET_LIFECYCLE_PHASE, Bridge.PortletPhase.ActionPhase);
+
+ // Set the FacesServletMapping attribute so the ExternalContext can
+ // pick it up and use it to reverse map viewIds to paths
+ if (mFacesMappings != null)
+ {
+ request.setAttribute(PortletExternalContextImpl.FACES_MAPPING_ATTRIBUTE, mFacesMappings);
+ }
+
+ // cache names of existing request attributes so can exclude them
+ // from being saved in the bridge's request scope. Note: this is done
+ // before
+ // acquiring the FacesContext because its possible (though unlikely)
+ // the application has inserted itself in this process and sets up
+ // needed request attributes.
+ String[] excludedAttributes = getExcludedAttributes(request);
+
+ FacesContext context = null;
+ try
+ {
+ // Get the FacesContext instance for this request
+ context =
+ getFacesContextFactory().getFacesContext(mPortletConfig, request, response, getLifecycle());
+ logMap("Received ActionParams: ",
+ context.getExternalContext().getRequestParameterValuesMap());
+
+ // Each action starts a new "action lifecycle"
+ // The Bridge preserves request scoped data and if so configured
+ // Action Parameters for the duration of an action lifecycle
+ String scopeId = initBridgeRequestScope(request, response);
+
+ // For actions we only execute the lifecycle phase
+ getLifecycle().execute(context);
+
+ // Check responseComplete -- if responseComplete the
+ // lifecycle.execute
+ // resulted in a redirect navigation. To preserve Faces semantics
+ // the viewState isn't preserved nor is the data associated with the
+ // action lifecycle.
+
+ if (!context.getResponseComplete())
+ {
+ // navigation didn't redirect
+
+ // Before preserving the request scope data in the bridge's
+ // request scope,
+ // put the Faces view into request scope. This is done because
+ // JSF 1.2 manages the tree save state opaquely exclusively in
+ // the render phase -- I.e. there is no JSF 1.2 way of having
+ // the
+ // bridge manually save and restore the view
+ saveFacesView(context);
+
+ // Spec requires we preserve the FACES_VIEW_STATE parameter
+ // in addition the portlet may be configured to preserve the
+ // rest of them.
+ saveActionParams(context);
+
+ // Because the portlet model doesn't execute its render phase
+ // within the same request scope but Faces does (assumes this),
+ // preserve the request scope data and the Faces view tree at
+ // RequestScope.
+ saveBridgeRequestScopeData(context, scopeId, excludedAttributes);
+
+ // Finalize the action response -- key here is the reliance on
+ // ExternalContext.encodeActionURL to migrate info encoded
+ // in the actionURL constructed from the target of this
+ // navigation
+ // into the ActionResponse so it can be decoded in the
+ // asscoicated portlet render.
+
+ finalizeActionResponse(context);
+
+ }
+ }
+ catch (Exception e)
+ {
+ context.getExternalContext().log("Exception thrown in doFacesRequest:action", e);
+ if (!(e instanceof BridgeException))
+ {
+ Throwable rootCause = e.getCause();
+ throw new BridgeException(e.getMessage(), rootCause);
+ }
+ else
+ {
+ throw (BridgeException) e;
+ }
+ }
+ finally
+ {
+ if (context != null)
+ {
+ m = context.getExternalContext().getRequestMap();
+ context.release();
+ }
+ }
+
+ logMap("Action completed: ", m);
+ }
+
+ public void doFacesRequest(RenderRequest request, RenderResponse response)
+ throws BridgeException
+ {
+ String scopeId = null;
+
+ // Set the Portlet lifecycle phase as a request attribute so its
+ // available to Faces extensions -- allowing that code to NOT rely on
+ // instanceof which can fail if a portlet container uses a single class
+ // to implement both the action and render request/response objects
+ request.setAttribute(Bridge.PORTLET_LIFECYCLE_PHASE, Bridge.PortletPhase.RenderPhase);
+
+ // Set the FacesServletMapping attribute so the ExternalContext can
+ // pick it up and use it to reverse map viewIds to paths
+ if (mFacesMappings != null)
+ {
+ request.setAttribute(PortletExternalContextImpl.FACES_MAPPING_ATTRIBUTE, mFacesMappings);
+ }
+
+ FacesContext context = null;
+ try
+ {
+ // Get the FacesContext instance for this request
+ Lifecycle lifecycle = getLifecycle();
+ context =
+ getFacesContextFactory().getFacesContext(mPortletConfig, request, response, lifecycle);
+ ExternalContext extCtx = context.getExternalContext();
+
+ logMap("Received RenderParams: ",
+ context.getExternalContext().getRequestParameterValuesMap());
+
+ // Use request from ExternalContext in case its been wrapped by an
+ // extension
+ RenderRequest extRequest = (RenderRequest) extCtx.getRequest();
+
+ scopeId = extRequest.getParameter(REQUEST_SCOPE_ID_RENDER_PARAM);
+
+ if (restoreBridgeRequestScopeData(context, scopeId))
+ {
+ // Because the Bridge is required to always save/restore the
+ // VIEW_STATE
+ // parameter -- always attempt a restore
+ extRequest = restoreActionParams(context);
+
+ // only restores if first render after action
+ // afterwards not restored from Bridge request scope
+ // rather its saved/restored by Faces.
+ restoreFacesView(context, scopeId);
+ }
+
+ // Ensure the ContentType is set before rendering
+ if (extCtx.getResponseContentType() == null)
+ {
+ response.setContentType(extRequest.getResponseContentType());
+ }
+
+ // ensure that isPostback attribute set if VIEW_STATE param exists
+ if (extCtx.getRequestParameterValuesMap().containsKey(ResponseStateManager.VIEW_STATE_PARAM))
+ {
+ extCtx.getRequestMap().put(Bridge.IS_POSTBACK_ATTRIBUTE, Boolean.TRUE);
+ }
+
+ // Note: if the scope wasn't restored then the Faces
+ // FACES_VIEW_STATE
+ // parameter will not have been carried into this render and hence
+ // default Faces impls will not see this render as occuring in a
+ // in a postback (isPostback() will return false. This means Faces
+ // will create a new Tree instead of restoring one -- the semantics
+ // one should get if the Bridge can't access its requestScope.
+
+ // if the requestScope restored the ViewRoot then this must be
+ // the first render after the action -- hence the tree isn't yet
+ // stored/managed by Faces -- we can merely render it
+ if (context.getViewRoot() == null)
+ {
+ // add self as PhaseListener to prevent action phases from
+ // executing
+ lifecycle.addPhaseListener(this);
+ try
+ {
+ lifecycle.execute(context);
+ }
+ catch (Exception e)
+ {
+ // When exception occurs remove stored scope so don't
+ // get stuck replaying the error when/if user refreshes
+ if (scopeId != null)
+ {
+ removeRequestScopes(scopeId);
+ }
+ }
+ finally
+ {
+ lifecycle.removePhaseListener(this);
+ }
+ }
+ getLifecycle().render(context);
+
+ // When we have navigated to this view between the action and render
+ // the initial VIEW_STATE_PARAM reflects the actions view -- update
+ // here to the one from this render so refresh will work.
+ if (scopeId != null)
+ {
+ updateViewStateParam(context, scopeId);
+ }
+
+ }
+ catch (Exception e)
+ {
+ // When exception occurs remove stored scope so don't
+ // get stuck replaying the error when/if user refreshes
+ if (scopeId != null)
+ {
+ removeRequestScopes(scopeId);
+ }
+
+ context.getExternalContext().log("Exception thrown in doFacesRequest:render", e);
+ if (!(e instanceof BridgeException))
+ {
+ Throwable rootCause = e.getCause();
+ throw new BridgeException(e.getMessage(), rootCause);
+ }
+ else
+ {
+ throw (BridgeException) e;
+ }
+ }
+ finally
+ {
+ if (context != null)
+ {
+ context.release();
+ }
+ }
+ }
+
+ public void destroy()
+ {
+ // remove any scopes being managed for this portlet
+ // Each scope has a per portlet prefix -- pass in the prefix
+ // constructed by adding the prefix to an empty string.
+ removeRequestScopes(qualifyScopeId(mPortletConfig.getPortletName(), null, null));
+
+ mPortletConfig = null;
+ }
+
+ /**
+ * ELContextListener impl
+ */
+ public void contextCreated(ELContextEvent ece)
+ {
+ // Add the portletConfig to the ELContext so it is evaluated
+ ELContext elContext = ece.getELContext();
+
+ // Only add if not already there
+ if (elContext.getContext(PortletConfig.class) == null)
+ {
+ elContext.putContext(PortletConfig.class, mPortletConfig);
+ }
+ }
+
+ private FacesContextFactory getFacesContextFactory()
+ throws BridgeException
+ {
+ try
+ {
+ if (mFacesContextFactory == null)
+ {
+ mFacesContextFactory =
+ (FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+ }
+ return mFacesContextFactory;
+ }
+ catch (FacesException e)
+ {
+ Throwable rootCause = e.getCause();
+ throw new BridgeException(e.getMessage(), rootCause);
+ }
+ }
+
+ private Lifecycle getLifecycle()
+ throws BridgeException
+ {
+ try
+ {
+ if (mLifecycle == null)
+ {
+ LifecycleFactory lifecycleFactory =
+ (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+ String lifecycleId =
+ mPortletConfig.getPortletContext().getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
+ if (lifecycleId == null)
+ {
+ lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
+ }
+
+ mLifecycle = lifecycleFactory.getLifecycle(lifecycleId);
+ }
+ return mLifecycle;
+ }
+ catch (FacesException e)
+ {
+ Throwable rootCause = e.getCause();
+ throw new BridgeException(e.getMessage(), rootCause);
+ }
+ }
+
+ private void saveFacesView(FacesContext context)
+ {
+
+ // first save any current Faces messages in the viewRoot
+ saveFacesMessageState(context);
+
+ // now place the viewRoot in the request scope
+ Map requestMap = context.getExternalContext().getRequestMap();
+ requestMap.put(FACES_VIEWROOT, context.getViewRoot());
+ }
+
+ private void restoreFacesView(FacesContext context, String scopeId)
+ {
+ Map requestMap = context.getExternalContext().getRequestMap();
+ UIViewRoot viewRoot = (UIViewRoot) requestMap.get(FACES_VIEWROOT);
+ if (viewRoot != null)
+ {
+ context.setViewRoot(viewRoot);
+ // remove from current Request Scope and the saved Bridge Request
+ // Scope
+ requestMap.remove(FACES_VIEWROOT);
+ removeFromBridgeRequestScopeData(context, scopeId, FACES_VIEWROOT);
+ }
+ restoreFacesMessageState(context);
+ // Don't remove the messages as Faces doesn't save these during render
+ }
+
+ private void saveActionParams(FacesContext context)
+ {
+ // Always preserve the FACES_VIEW_STATE parameter as per spec.
+ // If portlet requests it, also preserve the rst of them.
+ ExternalContext ec = context.getExternalContext();
+ Map requestMap = ec.getRequestMap();
+ Map requestParameterMap = ec.getRequestParameterValuesMap();
+ if (!mPreserveActionParams)
+ {
+ if (requestMap != null && requestParameterMap != null &&
+ requestParameterMap.containsKey(ResponseStateManager.VIEW_STATE_PARAM))
+ {
+ HashMap m = new HashMap(1);
+ m.put(ResponseStateManager.VIEW_STATE_PARAM,
+ requestParameterMap.get(ResponseStateManager.VIEW_STATE_PARAM));
+ requestMap.put(REQUEST_PARAMETERS, m);
+ }
+ }
+ else
+ {
+ // place the parameter map in the portlet request scope
+ // so it will be promoted into the Bridge's request scope and hence
+ // be available during render.
+ requestMap.put(REQUEST_PARAMETERS, requestParameterMap);
+ }
+ }
+
+ private void updateViewStateParam(FacesContext context, String scopeId)
+ {
+
+ // First make sure we have a value to update
+ String updatedViewStateParam = (String) context.getExternalContext()
+ .getRequestMap().get(UPDATED_VIEW_STATE_PARAM);
+
+ if (updatedViewStateParam == null)
+ return;
+
+ // Otherwise we need to update/store this value in the scope
+ PortletContext portletContext = mPortletConfig.getPortletContext();
+
+ // Get the request scope lock -- because its added during init it should
+ // always be there.
+ synchronized (portletContext.getAttribute(REQUEST_SCOPE_LOCK))
+ {
+ // get the managedScopeMap
+ LRUMap requestScopeMap = (LRUMap) portletContext.getAttribute(REQUEST_SCOPE_MAP);
+
+ if (requestScopeMap == null)
+ {
+ requestScopeMap = createRequestScopeMap(portletContext);
+ portletContext.setAttribute(REQUEST_SCOPE_MAP, requestScopeMap);
+ }
+
+ // Prepare the value for storing as a preserved parameter
+ // Store as an array of Strings with just one entry as per
+ // portlet request
+ String[] values = new String[1];
+ values[0] = updatedViewStateParam;
+
+ // now see if this scope is in the Map
+ Map scopeMap = (Map) requestScopeMap.get(scopeId);
+ Boolean isNew = false;
+
+ if (scopeMap == null)
+ {
+ // allocate a Map and put
+ scopeMap = new HashMap(1);
+ // delay adding to requestScopeMap until populated
+ isNew = true;
+ }
+
+ // Now get the RequestParameters from the scope
+ Map requestParams = (Map) scopeMap.get(REQUEST_PARAMETERS);
+ if (requestParams == null)
+ {
+ requestParams = (Map) new HashMap(1);
+ scopeMap.put(REQUEST_PARAMETERS, requestParams);
+ }
+ // finally update the value in the Map
+ requestParams.put(ResponseStateManager.VIEW_STATE_PARAM, values);
+
+ // if a newly allocated scope -- don't forget to add it in
+ if (isNew)
+ {
+ requestScopeMap.put(scopeId, scopeMap);
+ }
+
+ }
+
+ }
+
+ private LRUMap createRequestScopeMap(PortletContext portletContext)
+ {
+ // see if portlet has defined how many requestScopes to manage
+ // for this portlet
+ String managedScopesSetting =
+ portletContext.getInitParameter(Bridge.MAX_MANAGED_REQUEST_SCOPES);
+ Integer managedScopes = null;
+
+ if (managedScopesSetting != null)
+ {
+ managedScopes = Integer.getInteger(managedScopesSetting);
+ }
+
+ if (managedScopes == null || managedScopes.intValue() <= 0)
+ {
+ managedScopes = sDefaultMaxManagedRequestScopes;
+ }
+
+ return new LRUMap(managedScopes.intValue());
+ }
+
+ private RenderRequest restoreActionParams(FacesContext context)
+ {
+ // this is a little trickier then saving because there is no
+ // corresponding set. Instead we wrap the request object and set it
+ // on the externalContext.
+ ExternalContext ec = context.getExternalContext();
+ // Note: only available/restored if this scope was restored.
+ Map m = (Map) ec.getRequestMap().get(REQUEST_PARAMETERS);
+
+ // ensures current request returned if nothing to restore/wrap
+ RenderRequest wrapped = (RenderRequest) ec.getRequest();
+ if (m != null && !m.isEmpty())
+ {
+ wrapped = new BridgeRenderRequestWrapper(wrapped, m);
+ ec.setRequest(wrapped);
+ }
+ return wrapped;
+ }
+
+ public void saveFacesMessageState(FacesContext context)
+ {
+ Iterator messages;
+ // get the messages from Faces Context
+ Iterator clientIds = context.getClientIdsWithMessages();
+ if (clientIds.hasNext())
+ {
+ FacesMessageState state = new FacesMessageState();
+ while (clientIds.hasNext())
+ {
+ String clientId = (String) clientIds.next();
+ messages = context.getMessages(clientId);
+ while (messages.hasNext())
+ {
+ state.addMessage(clientId, (FacesMessage) messages.next());
+ }
+ }
+
+ // save state in ViewRoot attributes
+ Map requestMap = context.getExternalContext().getRequestMap();
+ requestMap.put(FACES_MESSAGES, state);
+ }
+ }
+
+ public void restoreFacesMessageState(FacesContext context)
+ {
+ // Only restore for Render request
+ if (context.getExternalContext().getRequest() instanceof RenderRequest)
+ {
+ Map map = context.getExternalContext().getRequestMap();
+
+ // restoring FacesMessages
+ FacesMessageState state1 = (FacesMessageState) map.get(FACES_MESSAGES);
+
+ if (state1 != null)
+ {
+ Iterator messages;
+ Iterator clientIds = state1.getClientIds();
+ while (clientIds.hasNext())
+ {
+ String clientId = (String) clientIds.next();
+ messages = state1.getMessages(clientId);
+ while (messages.hasNext())
+ {
+ context.addMessage(clientId, (FacesMessage) messages.next());
+ }
+ }
+ }
+ }
+ }
+
+ private String initBridgeRequestScope(ActionRequest request, ActionResponse response)
+ {
+
+ // Generate an RMI UID, which is a unique identifier WITHIN the local
+ // host. This will be used as the new lifecyleID
+ UID uid = new UID();
+ String requestScopeId = qualifyScopeId(mPortletConfig.getPortletName(),
+ request.getPortletSession(true).getId(),
+ uid.toString());
+
+ // set in response render parameter so will receive in future calls
+ // however don't store internally until there is specific state to
+ // manage
+ response.setRenderParameter(REQUEST_SCOPE_ID_RENDER_PARAM, requestScopeId);
+
+ return requestScopeId;
+ }
+
+ private void saveBridgeRequestScopeData(FacesContext context, String scopeId,
+ String[] excludeList)
+ {
+
+ // Store the RequestMap @ the bridge's request scope
+ putBridgeRequestScopeData(scopeId,
+ copyRequestMap(context.getExternalContext().getRequestMap(), excludeList));
+
+ // flag the data so can remove it if the session terminates
+ // as its unlikely useful if the session disappears
+ watchScope(context, scopeId);
+ }
+
+ private void putBridgeRequestScopeData(String scopeId, Object o)
+ {
+ PortletContext portletContext = mPortletConfig.getPortletContext();
+
+ // Get the request scope lock -- because its added during init it should
+ // always be there.
+ synchronized (portletContext.getAttribute(REQUEST_SCOPE_LOCK))
+ {
+ // get the managedScopeMap
+ LRUMap requestScopeMap = (LRUMap) portletContext.getAttribute(REQUEST_SCOPE_MAP);
+
+ if (requestScopeMap == null)
+ {
+ requestScopeMap = createRequestScopeMap(portletContext);
+ portletContext.setAttribute(REQUEST_SCOPE_MAP, requestScopeMap);
+ }
+
+ logMap("Saving RequestScope @ requestScopeId: " + scopeId, (Map) o);
+ requestScopeMap.put(scopeId, o);
+ }
+ }
+
+ private Map copyRequestMap(Map m, String[] excludeList)
+ {
+ HashMap copy = new HashMap(m.size());
+
+ Set keySet = m.keySet();
+ if (keySet != null)
+ {
+ Iterator keys = keySet.iterator();
+ while (keys != null && keys.hasNext())
+ {
+ String requestAttrKey = (String) keys.next();
+ Object requestAttrValue = m.get(requestAttrKey);
+ // TODO -- restore the ACTION PARAMS if there
+
+ // Don't copy any of the portlet or Faces objects
+ if (!inExcludeList(excludeList, requestAttrKey) &&
+ !contextObject(requestAttrKey, requestAttrValue))
+ {
+ copy.put(requestAttrKey, requestAttrValue);
+ }
+ }
+ }
+ return copy;
+ }
+
+ private boolean inExcludeList(String[] excludeList, String key)
+ {
+ if (excludeList == null || excludeList.length <= 0)
+ {
+ return false;
+ }
+ for (String element: excludeList)
+ {
+ if (element.equals(key))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String[] getExcludedAttributes(PortletRequest request)
+ {
+ String[] names = null;
+
+ // first count them to allocate the array
+ int i = 0, count = 0;
+ Enumeration e = request.getAttributeNames();
+ if (e == null)
+ {
+ return null;
+ }
+
+ while (e.hasMoreElements())
+ {
+ e.nextElement();
+ count += 1;
+ }
+
+ names = new String[count];
+ e = request.getAttributeNames();
+ if (e == null)
+ {
+ return null;
+ }
+
+ while (e.hasMoreElements() && i < count)
+ {
+ names[i++] = (String) e.nextElement();
+ }
+ return names;
+ }
+
+ private boolean contextObject(String s, Object o)
+ {
+ return o instanceof PortletConfig || o instanceof PortletContext ||
+ o instanceof PortletRequest || o instanceof PortletResponse || o instanceof PortletSession ||
+ o instanceof PortletPreferences || o instanceof PortalContext || o instanceof FacesContext ||
+ o instanceof ExternalContext || o instanceof ServletConfig || o instanceof ServletContext ||
+ o instanceof ServletRequest || o instanceof ServletResponse || o instanceof HttpSession ||
+ s.startsWith("javax.servlet.include") ||
+ s.startsWith("javax.portlet.faces.") ||
+ s.startsWith("org.apache.myfaces.trinidad") ||
+ s.startsWith("com.sun.faces.") ||
+ s.startsWith("javax.portlet.");
+ }
+
+ private void logMap(String message, Map m)
+ {
+ PortletContext context = mPortletConfig.getPortletContext();
+
+ context.log(message);
+ Set keySet = m.keySet();
+ if (keySet != null)
+ {
+ Iterator keys = keySet.iterator();
+ while (keys != null && keys.hasNext())
+ {
+ String requestAttrKey = (String) keys.next();
+ Object o = m.get(requestAttrKey);
+ if (o instanceof String)
+ context.log(" Map entry: " + requestAttrKey +" value:" + (String) o);
+ else if (o instanceof String[])
+ context.log(" Map entry: " + requestAttrKey + " value:" + ((String[]) o)[0]);
+ else
+ context.log(" Map entry: " + requestAttrKey);
+ }
+ }
+ else
+ {
+ context.log("Map is empty");
+ }
+
+ context.log("logMap completed");
+ }
+
+ private boolean restoreBridgeRequestScopeData(FacesContext context, String scopeId)
+ throws BridgeException
+ {
+
+ PortletContext portletContext = mPortletConfig.getPortletContext();
+ Map m = null;
+ Map requestMap = context.getExternalContext().getRequestMap();
+
+ if (scopeId == null)
+ {
+ return false;
+ }
+
+ // Get the data from the scope
+ synchronized (portletContext.getAttribute(REQUEST_SCOPE_LOCK))
+ {
+ // get the managedScopeMap
+ LRUMap requestScopeMap = (LRUMap) portletContext.getAttribute(REQUEST_SCOPE_MAP);
+ // No scope for all renders before first action to this portletApp
+ if (requestScopeMap == null)
+ {
+ return false;
+ }
+
+ m = (Map) requestScopeMap.get(scopeId);
+ if (m == null)
+ {
+ return false;
+ }
+
+ logMap("Restoring scope: " + scopeId, m);
+ }
+
+ // Restore it as the RequestMap
+ Set keySet = m.keySet();
+ if (keySet != null)
+ {
+ Iterator keys = keySet.iterator();
+ while (keys != null && keys.hasNext())
+ {
+ String requestAttrKey = (String) keys.next();
+ Object requestAttrValue = m.get(requestAttrKey);
+
+ requestMap.put(requestAttrKey, requestAttrValue);
+ }
+ }
+ else
+ {
+ return false;
+ }
+ // Let's see what actually made it into the Map
+ logMap("Restored RequestMap: ", context.getExternalContext().getRequestMap());
+
+ return true;
+ }
+
+ private boolean removeFromBridgeRequestScopeData(FacesContext context, String scopeId,
+ String key)
+ {
+ PortletContext portletContext = mPortletConfig.getPortletContext();
+ Map m = null;
+ Map requestMap = context.getExternalContext().getRequestMap();
+
+ if (scopeId == null)
+ {
+ return false;
+ }
+
+ // Get the data from the scope
+ synchronized (portletContext.getAttribute(REQUEST_SCOPE_LOCK))
+ {
+ // get the managedScopeMap
+ LRUMap requestScopeMap = (LRUMap) portletContext.getAttribute(REQUEST_SCOPE_MAP);
+ // No scope for all renders before first action to this portletApp
+ if (requestScopeMap == null)
+ {
+ return false;
+ }
+
+ m = (Map) requestScopeMap.get(scopeId);
+ if (m != null)
+ {
+ return m.remove(key) != null;
+ }
+
+ }
+
+ return false;
+ }
+
+ /*
+ * A scope is qualified first by the portlet this scope has been created for
+ * and then second by the specific session this scope is used in. By doing
+ * this we are able to remove this specific scope, all the scopes associated
+ * with a particular session, or all the scopes associated with a particular
+ * portlet regardless of sessions.
+ */
+
+ private String qualifyScopeId(String portletId, String sessionId, String scopeId)
+ {
+ // a qualified scope Id must at a minimum be qualified by a portletId
+ if (portletId == null) portletId = mPortletConfig.getPortletName();
+
+ StringBuffer sb = new StringBuffer(portletId);
+ sb.append(':');
+ if (sessionId != null)
+ {
+ sb.append(sessionId);
+ sb.append(':');
+ if (scopeId != null)
+ {
+ sb.append(scopeId);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private void watchScope(FacesContext context, String scopeId)
+ {
+ PortletSession session = (PortletSession) context.getExternalContext().getSession(true);
+ if (session != null)
+ {
+ RequestScopeListener scopeListener =
+ (RequestScopeListener) session.getAttribute(REQUEST_SCOPE_LISTENER);
+ if (scopeListener == null)
+ {
+ // only store the qualified prefix
+ // if invalidated we walk the entire REQUEST_SCOPE Map and
+ // remove
+ // every scope that starts with this prefix.
+ session.setAttribute(REQUEST_SCOPE_LISTENER, new RequestScopeListener(
+ qualifyScopeId(mPortletConfig.getPortletName(),
+ session.getId(),
+ null)));
+ }
+ }
+ }
+
+ private void finalizeActionResponse(FacesContext context)
+ throws IOException
+ {
+
+ // We rely on Faces ExternalContext.encodeActionURL to do the heavy
+ // lifting here. First we construct a true actionURL using the viewId
+ // for the view that is the target of the navigation. Then we call
+ // encodeActionURL passing this URL. encodeActionURL encodes into
+ // ActionResponse sufficient information (pulled from the supplied
+ // actionURL) so that it (the EXteranlContext) can decode the
+ // information
+ // in the subsequent render request(s).
+
+ ViewHandler viewHandler = context.getApplication().getViewHandler();
+ String actionURL = viewHandler.getActionURL(context, context.getViewRoot().getViewId());
+ String encodedActionURL = context.getExternalContext().encodeActionURL(actionURL);
+
+ // Strictly speaking this is a redundant call (noop) as
+ // ExternalContext.redirect() JSR 301 rules require redirects of
+ // URLs containing viewIds (aka actionURLs) to be handled as
+ // regular Faces navigation not full client redirects. Its
+ // included here primarily to ensure that redirect is implemented
+ // correctly.
+ context.getExternalContext().redirect(encodedActionURL);
+ }
+
+ private void removeRequestScopes(String scopePrefix)
+ {
+
+ // Get the RequestScope Map and remove all entries/scopes with this prefix
+ PortletContext portletContext = mPortletConfig.getPortletContext();
+
+ // Get the request scope lock -- because its added during init it should
+ // always be there.
+ Object lock = portletContext.getAttribute(REQUEST_SCOPE_LOCK);
+ if (lock == null)
+ return;
+
+ synchronized (lock)
+ {
+ // get the managedScopeMap
+ LRUMap requestScopeMap = (LRUMap) portletContext.getAttribute(REQUEST_SCOPE_MAP);
+ Vector v = new Vector(10);
+
+ if (requestScopeMap != null)
+ {
+ Set keySet = requestScopeMap.keySet();
+ if (keySet != null)
+ {
+ Iterator keys = keySet.iterator();
+ while (keys != null && keys.hasNext())
+ {
+ String scopeId = (String) keys.next();
+ if (scopeId != null && scopeId.startsWith(scopePrefix))
+ {
+ // can't remove while iterating or else get a concurrency exception
+ v.add(scopeId);
+ }
+ }
+ }
+
+ for (int i = 0; i < v.size(); i++)
+ {
+ String scopeId = (String) v.elementAt(i);
+ requestScopeMap.remove(scopeId);
+ }
+ }
+ }
+
+
+ }
+
+ /* Implement the PhaseListener methods */
+
+ public PhaseId getPhaseId()
+ {
+ return PhaseId.RESTORE_VIEW;
+ }
+
+ public void beforePhase(PhaseEvent event)
+ {
+ // do nothing
+ return;
+ }
+
+ public void afterPhase(PhaseEvent event)
+ {
+ // only set renderresponse if in RESTORE_VIEW phase
+ if (event.getPhaseId() == PhaseId.RESTORE_VIEW)
+ {
+ event.getFacesContext().renderResponse();
+ }
+ }
+
+ private final class LRUMap
+ extends LinkedHashMap
+ {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 4372455368577337965L;
+ private int mMaxCapacity;
+
+ public LRUMap(int maxCapacity)
+ {
+ super(maxCapacity, 1.0f, true);
+ mMaxCapacity = maxCapacity;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest)
+ {
+ return size() > mMaxCapacity;
+ }
+
+ }
+
+ // TODO: Should we store these as attributes of the ViewTree??? It would
+ // work as
+ // everything is serializable. -- Issue is we need to implement a
+ // PhaseListener to
+ // to deal with this -- at the moment I prefer to isolate the Faces
+ // extensions from this
+ // detail and leave it all in this controller part.
+
+ private final class FacesMessageState
+ implements Serializable
+ {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8438070672451887050L;
+ // For saving and restoring FacesMessages
+ private Map mMessages = new HashMap(); // key=clientId;
+
+ // value=FacesMessages
+
+ public void addMessage(String clientId, FacesMessage message)
+ {
+ List list = (List) mMessages.get(clientId);
+ if (list == null)
+ {
+ list = new ArrayList();
+ mMessages.put(clientId, list);
+ }
+ list.add(message);
+ }
+
+ public Iterator getMessages(String clientId)
+ {
+ List list = (List) mMessages.get(clientId);
+ if (list != null)
+ {
+ return list.iterator();
+ }
+ else
+ {
+ return Collections.EMPTY_LIST.iterator();
+ }
+ }
+
+ public Iterator getClientIds()
+ {
+ return mMessages.keySet().iterator();
+ }
+ }
+
+ private final class RequestScopeListener
+ implements HttpSessionBindingListener
+ {
+ String mScopePrefix = null;
+
+ public RequestScopeListener(String scopePrefix)
+ {
+ mScopePrefix = scopePrefix;
+ }
+
+ public void valueBound(HttpSessionBindingEvent event)
+ {
+
+ }
+
+ public void valueUnbound(HttpSessionBindingEvent event)
+ {
+ // Call is in the BridgeImpl class
+ removeRequestScopes(mScopePrefix);
+ }
+
+ }
+}