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.portlet;
20  
21  import java.io.IOException;
22  
23  import javax.faces.FactoryFinder;
24  import javax.faces.application.Application;
25  import javax.faces.application.ApplicationFactory;
26  import javax.faces.application.ViewHandler;
27  import javax.faces.component.UIViewRoot;
28  import javax.faces.context.ExternalContext;
29  import javax.faces.context.FacesContext;
30  import javax.faces.context.FacesContextFactory;
31  import javax.faces.lifecycle.Lifecycle;
32  import javax.faces.lifecycle.LifecycleFactory;
33  import javax.faces.webapp.FacesServlet;
34  import javax.portlet.ActionRequest;
35  import javax.portlet.ActionResponse;
36  import javax.portlet.GenericPortlet;
37  import javax.portlet.PortletContext;
38  import javax.portlet.PortletException;
39  import javax.portlet.PortletRequest;
40  import javax.portlet.PortletResponse;
41  import javax.portlet.RenderRequest;
42  import javax.portlet.RenderResponse;
43  import javax.portlet.UnavailableException;
44  
45  import org.apache.commons.logging.Log;
46  import org.apache.commons.logging.LogFactory;
47  import org.apache.myfaces.context.ReleaseableExternalContext;
48  import org.apache.myfaces.context.portlet.PortletExternalContextImpl;
49  import org.apache.myfaces.context.servlet.FacesContextImpl;
50  import org.apache.myfaces.shared_impl.webapp.webxml.WebXml;
51  
52  /**
53   * This portlet initializes MyFaces and converts portlet requests into
54   * JSF requests.
55   *
56   * @deprecated Replaced by jsr 301 portlet bridge. 
57   * @author  Stan Silvert (latest modification by $Author: lu4242 $)
58   * @version $Revision: 827879 $ $Date: 2009-10-20 22:10:03 -0500 (Tue, 20 Oct 2009) $
59   */
60  public class MyFacesGenericPortlet extends GenericPortlet
61  {
62      private static final Log log = LogFactory.getLog(MyFacesGenericPortlet.class);
63  
64      // PortletRequest parameter
65      public static final String VIEW_ID =
66          MyFacesGenericPortlet.class.getName() + ".VIEW_ID";
67  
68      // PortletSession attribute
69      protected static final String CURRENT_FACES_CONTEXT =
70          MyFacesGenericPortlet.class.getName() + ".CURRENT_FACES_CONTEXT";
71  
72      // portlet config parameter from portlet.xml
73      protected static final String DEFAULT_VIEW = "default-view";
74  
75      // portlet config parameter from portlet.xml
76      protected static final String DEFAULT_VIEW_SELECTOR = "default-view-selector";
77  
78      protected static final String FACES_INIT_DONE =
79          MyFacesGenericPortlet.class.getName() + ".FACES_INIT_DONE";
80  
81      protected PortletContext portletContext;
82  
83      protected FacesContextFactory facesContextFactory;
84      protected Lifecycle lifecycle;
85  
86      protected String defaultView;
87      protected DefaultViewSelector defaultViewSelector;
88  
89      /**
90       * Creates a new instance of MyFacesPortlet
91       */
92      public MyFacesGenericPortlet()
93      {
94      }
95  
96      /**
97       * Portlet lifecycle.
98       */
99      public void destroy()
100     {
101         super.destroy();
102         FactoryFinder.releaseFactories();
103     }
104 
105     /**
106      * Portlet lifecycle.
107      */
108     public void init() throws PortletException, UnavailableException
109     {
110         this.portletContext = getPortletContext();
111         setDefaultView();
112         setDefaultViewSelector();
113         initMyFaces();
114 
115         facesContextFactory = (FacesContextFactory)FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
116 
117         // Javadoc says: Lifecycle instance is shared across multiple simultaneous requests, it must be
118         // implemented in a thread-safe manner.  So we can acquire it here once:
119         LifecycleFactory lifecycleFactory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
120         lifecycle = lifecycleFactory.getLifecycle(getLifecycleId());
121     }
122 
123     protected void setDefaultView() throws UnavailableException
124     {
125         this.defaultView = getPortletConfig().getInitParameter(DEFAULT_VIEW);
126         if (defaultView == null)
127         {
128             String msg = "Fatal: must specify a JSF view id as the default view in portlet.xml";
129             throw new UnavailableException(msg);
130         }
131     }
132 
133     protected void setDefaultViewSelector() throws UnavailableException
134     {
135         String selectorClass = getPortletConfig().getInitParameter(DEFAULT_VIEW_SELECTOR);
136         if (selectorClass == null) return;
137 
138         try
139         {
140             this.defaultViewSelector = (DefaultViewSelector)Class.forName(selectorClass).newInstance();
141             this.defaultViewSelector.setPortletContext(getPortletContext());
142         }
143         catch (Exception e)
144         {
145             log.error("Failed to load " + DEFAULT_VIEW_SELECTOR, e);
146             throw new UnavailableException(e.getMessage());
147         }
148     }
149 
150     protected void setContentType(RenderRequest request, RenderResponse response)
151     {
152 
153         if (response.getContentType() == null)
154         {
155             String portalPreferredContentType = request.getResponseContentType();
156             if (portalPreferredContentType != null)
157             {
158                 response.setContentType(portalPreferredContentType);
159             }
160             else
161             {
162                 response.setContentType("text/html");
163             }
164         }
165     }
166 
167     protected String getLifecycleId()
168     {
169         String lifecycleId = getPortletConfig().getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
170         return lifecycleId != null ? lifecycleId : LifecycleFactory.DEFAULT_LIFECYCLE;
171     }
172 
173     protected void initMyFaces()
174     {
175         try
176         {
177             Boolean b = (Boolean)portletContext.getAttribute(FACES_INIT_DONE);
178 
179             if (b == null || b.booleanValue() == false)
180             {
181                 log.trace("Initializing MyFaces");
182 
183                 //Load the configuration
184                 ExternalContext externalContext = new PortletExternalContextImpl(portletContext, null, null);
185 
186                 //And configure everything
187                 
188                 // TODO: FIX PORTLET STUFF FOR JSF 1.2
189                 // new FacesConfigurator(externalContext).configure();
190 
191                 // parse web.xml - not sure if this is needed for portlet
192                 WebXml.init(externalContext);
193 
194                 portletContext.setAttribute(FACES_INIT_DONE, Boolean.TRUE);
195             }
196             else
197             {
198                 log.info("MyFaces already initialized");
199             }
200         }
201         catch (Exception ex)
202         {
203             log.error("Error initializing MyFacesGenericPortlet", ex);
204         }
205 
206         log.info("PortletContext '" + portletContext.getRealPath("/") + "' initialized.");
207     }
208 
209     /**
210      * Called by the portlet container to allow the portlet to process an action request.
211      */
212     public void processAction(ActionRequest request, ActionResponse response)
213             throws PortletException, IOException
214     {
215         if (log.isTraceEnabled()) log.trace("called processAction");
216 
217         if (sessionTimedOut(request)) return;
218 
219         setPortletRequestFlag(request);
220 
221         FacesContext facesContext = facesContext(request, response);
222 
223         try
224         {
225             lifecycle.execute(facesContext);
226 
227             if (!facesContext.getResponseComplete())
228             {
229                 response.setRenderParameter(VIEW_ID, facesContext.getViewRoot().getViewId());
230             }
231 
232             request.getPortletSession().setAttribute(CURRENT_FACES_CONTEXT, facesContext);
233         }
234         catch (Throwable e)
235         {
236             facesContext.release();
237             handleExceptionFromLifecycle(e);
238         }
239     }
240 
241     protected void handleExceptionFromLifecycle(Throwable e)
242             throws PortletException, IOException
243     {
244         logException(e, null);
245 
246         if (e instanceof IOException)
247         {
248             throw (IOException)e;
249         }
250 
251         if (e instanceof PortletException)
252         {
253             throw (PortletException)e;
254         }
255 
256         if (e.getMessage() != null)
257         {
258             throw new PortletException(e.getMessage(), e);
259         }
260 
261         throw new PortletException(e);
262     }
263 
264     /**
265      * Helper method to serve up the view mode.
266      */
267     protected void doView(RenderRequest request, RenderResponse response)
268             throws PortletException, IOException
269     {
270         facesRender(request, response);
271     }
272 
273     /**
274      * Helper method to serve up the edit mode.  Can be overridden to add
275      * the edit mode concept to a JSF application.
276      */
277     protected void doEdit(RenderRequest request, RenderResponse response)
278             throws PortletException, IOException
279     {
280         facesRender(request, response);
281     }
282 
283     /**
284      * Helper method to serve up the edit mode.  Can be overridden to add
285      * the help mode concept to a JSF application.
286      */
287     protected void doHelp(RenderRequest request, RenderResponse response)
288             throws PortletException, IOException
289     {
290         facesRender(request, response);
291     }
292 
293     /**
294      * This method follows JSF Spec section 2.1.1.  It renders the default view from a non-faces
295      * request.
296      *
297      * @param request The portlet render request.
298      * @param response The portlet render response.
299      */
300     protected void nonFacesRequest(RenderRequest request, RenderResponse response) throws PortletException
301     {
302         nonFacesRequest(request, response, selectDefaultView(request, response));
303     }
304 
305     /**
306      * This method follows JSF Spec section 2.1.1.  It renders a view from a non-faces
307      * request.  This is useful for a default view as well as for views that need to
308      * be rendered from the portlet's edit and help buttons.
309      *
310      * @param request The portlet render request.
311      * @param response The portlet render response.
312      * @param view The name of the view that needs to be rendered.
313      */
314     protected void nonFacesRequest(RenderRequest request, RenderResponse response, String view)
315             throws PortletException
316     {
317         if (log.isTraceEnabled()) log.trace("Non-faces request: contextPath = " + request.getContextPath());
318         setContentType(request, response); // do this in case nonFacesRequest is called by a subclass
319         ApplicationFactory appFactory =
320             (ApplicationFactory)FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
321         Application application = appFactory.getApplication();
322         ViewHandler viewHandler = application.getViewHandler();
323         FacesContext facesContext = facesContext(request, response);
324         UIViewRoot viewRoot = viewHandler.createView(facesContext, view);
325         viewRoot.setViewId(view);
326         facesContext.setViewRoot(viewRoot);
327         lifecycle.render(facesContext);
328     }
329 
330     protected String selectDefaultView(RenderRequest request, RenderResponse response) throws PortletException
331     {
332         String view = this.defaultView;
333         if (this.defaultViewSelector != null)
334         {
335             String selectedView = this.defaultViewSelector.selectViewId(request, response);
336             if (selectedView != null)
337             {
338                 view = selectedView;
339             }
340         }
341 
342         return view;
343     }
344 
345     protected FacesContext facesContext(PortletRequest request,
346                                         PortletResponse response)
347     {
348         return facesContextFactory.getFacesContext(portletContext,
349                                                    request,
350                                                    response,
351                                                    lifecycle);
352     }
353 
354     protected ReleaseableExternalContext makeExternalContext(PortletRequest request,
355                                                              PortletResponse response)
356     {
357         return new PortletExternalContextImpl(portletContext, request, response);
358     }
359 
360     protected boolean sessionTimedOut(PortletRequest request)
361     {
362         return request.getPortletSession(false) == null;
363     }
364 
365     protected void setPortletRequestFlag(PortletRequest request)
366     {
367         request.getPortletSession().setAttribute(PortletUtil.PORTLET_REQUEST_FLAG, "true");
368     }
369 
370     /**
371      * Render a JSF view.
372      */
373     protected void facesRender(RenderRequest request, RenderResponse response)
374             throws PortletException, java.io.IOException
375     {
376         if (log.isTraceEnabled()) log.trace("called facesRender");
377 
378         setContentType(request, response);
379 
380         String viewId = request.getParameter(VIEW_ID);
381         if ((viewId == null) || sessionTimedOut(request))
382         {
383             setPortletRequestFlag(request);
384             nonFacesRequest(request,  response);
385             return;
386         }
387 
388         setPortletRequestFlag(request);
389 
390         try
391         {
392             FacesContextImpl facesContext = (FacesContextImpl)request.
393                                                    getPortletSession().
394                                                    getAttribute(CURRENT_FACES_CONTEXT);
395 
396             // TODO: not sure if this can happen.  Also double check this against spec section 2.1.3
397             if (facesContext.getResponseComplete()) return;
398 
399             facesContext.setExternalContext(makeExternalContext(request, response));
400             lifecycle.render(facesContext);
401         }
402         catch (Throwable e)
403         {
404             handleExceptionFromLifecycle(e);
405         }
406     }
407 
408     protected void logException(Throwable e, String msgPrefix) {
409         String msg;
410         if (msgPrefix == null)
411         {
412             if (e.getMessage() == null)
413             {
414                 msg = "Exception in FacesServlet";
415             }
416             else
417             {
418                 msg = e.getMessage();
419             }
420         }
421         else
422         {
423             if (e.getMessage() == null)
424             {
425                 msg = msgPrefix;
426             }
427             else
428             {
429                 msg = msgPrefix + ": " + e.getMessage();
430             }
431         }
432 
433         portletContext.log(msg, e);
434 
435         Throwable cause = e.getCause();
436         if (cause != null && cause != e)
437         {
438             logException(cause, "Root cause");
439         }
440 
441         if(e instanceof PortletException)
442         {
443             cause = ((PortletException) e).getCause();
444 
445             if(cause != null && cause != e)
446             {
447                 logException(cause, "Root cause of PortletException");
448             }
449         }
450     }
451 
452 }