View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
    10   *   http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing,
    13   * software distributed under the License is distributed on an
    14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   * KIND, either express or implied.  See the License for the
    16   * specific language governing permissions and limitations
    17   * under the License.
    18   */
    19  package org.apache.myfaces.orchestra.lib.jsf;
    20
    21  import java.util.LinkedList;
    22  import java.util.List;
    23  import java.util.Map;
    24  import java.util.concurrent.atomic.AtomicLong;
    25
    26  import javax.faces.FacesException;
    27  import javax.faces.context.ExternalContext;
    28  import javax.faces.context.FacesContext;
    29  import javax.portlet.ActionResponse;
    30  import javax.portlet.PortletRequest;
    31
    32  import org.apache.commons.logging.Log;
    33  import org.apache.commons.logging.LogFactory;
    34  import org.apache.myfaces.orchestra.frameworkAdapter.FrameworkAdapter;
    35  import org.apache.myfaces.orchestra.requestParameterProvider.RequestParameterServletFilter;
    36
    37  /**
    38   * Class used by OrchestraFacesContextFactory to allow orchestra work in portlets
    39   *
    40   * In portlet world we have the following differences against servlets:
    41   *
    42   * 1. In servlet, the same thread is responsible from execute action and render. But
    43   *    in portlets we could have separate threads that execute action and render.
    44   * 2. The request attribute values between portlet action and render could be lost, so
    45   *    if we need some param to be passed between action and render phase.
    46   *
    47   *
    48   * @author Leonardo Uribe(latest modification by $Author: lu4242 $)
    49   * @version $Revision: 826577 $ $Date: 2009-10-18 20:50:13 -0500 (Sun, 18 Oct 2009) $
    50   */
    51  public class PortletOrchestraFacesContextFactory
    52  {
    53      private final Log log = LogFactory.getLog(PortletOrchestraFacesContextFactory.class);
    54
    55      private final AtomicLong _count;
    56
    57      private final static String REQUEST_CONTEXT_PARAM = "requestContext";
    58
    59      private final static String CONVERSATION_CONTEXT_PARAM = "conversationContext";
    60
    61      public final static String REQUEST_HANDLERS = "org.apache.myfaces.orchestra.REQUEST_HANDLERS";
    62
    63      /**
    64       * Constant used by orchestra to check if we are on a portlet request or not.
    65       */
    66      public final static String PORTLET_LIFECYCLE_PHASE = "javax.portlet.orchestra.phase";
    67
    68
    69      public PortletOrchestraFacesContextFactory()
    70      {
    71          this._count = new AtomicLong(1);
    72      }
    73
    74      /**
    75       * Return the next token to identify in a unique way the current request. This is
    76       * used when there is no conversationContext param available. In that remote case,
    77       * a dummy param is added just to allow pass installed handlers between action
    78       * and request scope.
    79       *
    80       * @return
    81       */
    82      protected String _getNextToken()
    83      {
    84          long nextToken = _count.incrementAndGet();
    85          return Integer.toString(hashCode())+nextToken;
    86      }
    87
    88      public FacesContext getFacesContext(final FacesContext facesContext, Object context,
    89              Object request, Object response) throws FacesException
    90      {
    91          ExternalContext externalContext = facesContext.getExternalContext();
    92          List handlers = null;
    93          boolean init = false;
    94          String nextToken = null;
    95
    96          Map requestMap = externalContext.getRequestMap();
    97
    98          RequestType type = ExternalContextUtils.getRequestType(context, request);
    99
    100         if (RequestType.RENDER.equals(type))
    101         {
    102             PortletRequest portletRequest = (PortletRequest) request;
    103             nextToken = portletRequest.getParameter(CONVERSATION_CONTEXT_PARAM);
    104             if (nextToken == null)
    105             {
    106                 nextToken = portletRequest.getParameter(REQUEST_CONTEXT_PARAM);
    107             }
    108             if (nextToken != null)
    109             {
    110                 //Restore it from application map and remove it from application map,
    111                 //since it will not be used anymore
    112                 handlers = (List) externalContext.getApplicationMap().remove(REQUEST_HANDLERS+nextToken);
    113             }
    114         }
    115
    116         // AbstractSpringOrchestraScope add retrieved values to request scope, but in portlet
    117         // containers later this map could be serialized. This cause orchestra try to resolve
    118         // its proxy, but since FrameworkAdapter instance is only available after this point.
    119         // it causes an Exception.
    120         // Since from AbstractSpringOrchestraScope we can't have access to ExternalContext, we should put
    121         // a param to identify if we are or not in portlet request and which phase we are, in a similar
    122         // way as JSR-301 does
    123         if (RequestType.RENDER.equals(type))
    124         {
    125             requestMap.put(PORTLET_LIFECYCLE_PHASE, "RENDER_PHASE");
    126         }
    127         else if (RequestType.ACTION.equals(type))
    128         {
    129             requestMap.put(PORTLET_LIFECYCLE_PHASE, "ACTION_PHASE");
    130         }
    131         else if (RequestType.EVENT.equals(type))
    132         {
    133             requestMap.put(PORTLET_LIFECYCLE_PHASE, "EVENT_PHASE");
    134         }
    135         else if (RequestType.RESOURCE.equals(type))
    136         {
    137             requestMap.put(PORTLET_LIFECYCLE_PHASE, "RESOURCE_PHASE");
    138         }
    139
    140         if (handlers == null)
    141         {
    142             //Init
    143             handlers = new LinkedList();
    144             handlers.add(new FrameworkAdapterRequestHandler());
    145             //TODO: put this one causes context never released because
    146             //in portlets different threads could handle same request (one
    147             //thread action and the other one render)
    148             //handlers.add(new ContextLockRequestHandler());
    149             handlers.add(new ConversationManagerRequestHandler());
    150             handlers.add(new DataSourceLeakRequestHandler());
    151             // Add any other handlers registered by filters or similar
    152             Map reqScope = facesContext.getExternalContext().getRequestMap();
    153             handlers.addAll(ConfigUtils.getRequestHandlers(reqScope));
    154
    155             if (RequestType.ACTION.equals(ExternalContextUtils.getRequestType(context, request)))
    156             {
    157                 ActionResponse actionResponse = (ActionResponse) response;
    158
    159                 PortletRequest portletRequest = (PortletRequest) request;
    160                 nextToken = portletRequest.getParameter(CONVERSATION_CONTEXT_PARAM);
    161                 if (nextToken == null)
    162                 {
    163                     nextToken = _getNextToken();
    164                     actionResponse.setRenderParameter(REQUEST_CONTEXT_PARAM, nextToken);
    165                 }
    166                 if (nextToken != null)
    167                 {
    168                     //Put it on application map, to make it available on render response phase
    169                     externalContext.getApplicationMap().put(REQUEST_HANDLERS+nextToken, handlers);
    170                 }
    171             }
    172             init = true;
    173         }
    174
    175         // Do the stuff that suppose to be done by RequestParameterFacesContextFactory
    176         // We need to do it here, because the code requires handlers are already set.
    177         setRequestParameterResponseWrappedMode(context, request);
    178
    179         final RequestHandler contextLockHandler = new ContextLockRequestHandler();
    180
    181         //Since we override ExternalContext, install = true for this wrapper.
    182         return new _PortletFacesContextWrapper(facesContext, true,
    183                 init, nextToken, handlers, contextLockHandler);
    184     }
    185
    186     private void setRequestParameterResponseWrappedMode(Object context, Object request)
    187     {
    188         FrameworkAdapter nonJsfFrameworkAdapter = FrameworkAdapter.getCurrentInstance();
    189         if (nonJsfFrameworkAdapter != null)
    190         {
    191             if (!Boolean.TRUE.equals(nonJsfFrameworkAdapter.getRequestAttribute(
    192                     RequestParameterServletFilter.REQUEST_PARAM_FILTER_CALLED)))
    193             {
    194                 nonJsfFrameworkAdapter.setRequestAttribute(
    195                         RequestParameterServletFilter.REQUEST_PARAM_RESPONSE_WRAPPED, Boolean.TRUE);
    196             }
    197         }
    198         else if (!getBooleanRequestValue(request,
    199                 RequestParameterServletFilter.REQUEST_PARAM_FILTER_CALLED))
    200         {
    201             setBooleanRequestValue(request,
    202                     RequestParameterServletFilter.REQUEST_PARAM_RESPONSE_WRAPPED, Boolean.TRUE);
    203         }
    204     }
    205
    206     private boolean getBooleanRequestValue(Object request, String key)
    207     {
    208         PortletRequest portletRequest = (PortletRequest) request;
    209
    210         return Boolean.TRUE.equals(portletRequest.getAttribute(key));
    211     }
    212
    213     private void setBooleanRequestValue(Object request, String key, Boolean value)
    214     {
    215         PortletRequest portletRequest = (PortletRequest) request;
    216
    217         portletRequest.setAttribute(key,value);
    218     }
    219 }