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 }