View Javadoc

1   /*
2    * Copyright 2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.portals.bridges.jsf;
17  
18  import java.io.IOException;
19  
20  import javax.faces.FacesException;
21  import javax.faces.FactoryFinder;
22  import javax.faces.application.Application;
23  import javax.faces.component.UIViewRoot;
24  import javax.faces.context.FacesContext;
25  import javax.faces.context.FacesContextFactory;
26  import javax.faces.lifecycle.Lifecycle;
27  import javax.faces.lifecycle.LifecycleFactory;
28  import javax.faces.render.RenderKitFactory;
29  import javax.faces.webapp.FacesServlet;
30  
31  import javax.portlet.ActionRequest;
32  import javax.portlet.ActionResponse;
33  import javax.portlet.PortletConfig;
34  import javax.portlet.PortletException;
35  import javax.portlet.PortletMode;
36  import javax.portlet.PortletRequest;
37  import javax.portlet.PortletResponse;
38  import javax.portlet.PortletSession;
39  import javax.portlet.RenderRequest;
40  import javax.portlet.RenderResponse;
41  
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  import org.apache.portals.bridges.common.GenericServletPortlet;
45  
46  /***
47   * <p>
48   * FacesPortlet utilizes Java Server Faces to create the user interface in a
49   * portlet environment.
50   * </p>
51   * 
52   * @author <a href="mailto:dlestrat@yahoo.com">David Le Strat</a>
53   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
54   */
55  public class FacesPortlet extends GenericServletPortlet
56  {
57  
58      /*** The Log instance for this class. */
59      private static final Log log = LogFactory.getLog(FacesPortlet.class);
60  
61      /*** The VIEW_ROOT used to keep track of action between the action request and the render request. */
62      public static final String VIEW_ROOT = "org.apache.portals.bridges.jsf.VIEW_ROOT";
63      
64      /*** 
65       * The REQUEST_SERVLET_PATH used for externalContext.getRequestServletPath(). externalContext.getRequestServletPath()
66       * should return null but this is a work around an issue with MyFaces JspViewHandler implementation getServletMapping().
67       */
68      public static final String REQUEST_SERVLET_PATH = "org.apache.portals.bridges.jsf.REQUEST_SERVLET_PATH";
69  
70      /*** The JSF_VIEW_ID used to maintain the state of the view action. */
71      public static final String JSF_VIEW_ID = "jsf_viewid";
72      public static final String JSF_EDIT_ID = "jsf_editid";
73      public static final String JSF_HELP_ID = "jsf_helpid";
74      public static final String JSF_CUSTOM_ID = "jsf_customid";
75      
76      /*** Name of portlet preference for Action page. */
77      public static final String PARAM_ACTION_PAGE = "ActionPage";
78  
79      /*** Name of portlet preference for Custom page. */
80      public static final String PARAM_CUSTOM_PAGE = "CustomPage";
81  
82      /*** Name of portlet preference for Edit page. */
83      public static final String PARAM_EDIT_PAGE = "EditPage";
84  
85      /*** Name of portlet preference for Edit page */
86      public static final String PARAM_HELP_PAGE = "HelpPage";
87  
88      /*** Name of portlet preference for View page */
89      public static final String PARAM_VIEW_PAGE = "ViewPage";
90  
91      /*** Action request. */
92      public static final String ACTION_REQUEST = "ACTION";
93  
94      /*** View request. */
95      public static final String VIEW_REQUEST = "VIEW";
96  
97      /*** Custom request. */
98      public static final String CUSTOM_REQUEST = "CUSTOM";
99  
100     /*** Edit request. */
101     public static final String EDIT_REQUEST = "EDIT";
102 
103     /*** Help request. */
104     public static final String HELP_REQUEST = "HELP";
105 
106     /*** Default URL for the action page. */
107     private String defaultActionPage = null;
108 
109     /*** Default URL for the custom page. */
110     private String defaultCustomPage = null;
111 
112     /*** Default URL for the edit page. */
113     private String defaultEditPage = null;
114 
115     /*** Default URL for the help page. */
116     private String defaultHelpPage = null;
117 
118     /*** Default URL for the view page. */
119     private String defaultViewPage = null;
120 
121     /***
122      * <p>
123      * Context initialization parameter name for the lifecycle identifier of the
124      * {@link Lifecycle}instance to be utilized.
125      * </p>
126      */
127     private static final String LIFECYCLE_ID_ATTR = FacesServlet.LIFECYCLE_ID_ATTR;
128 
129     /***
130      * <p>
131      * The {@link Application}instance for this web application.
132      * </p>
133      */
134     private Application application = null;
135 
136     /***
137      * <p>
138      * Factory for {@link FacesContext}instances.
139      * </p>
140      */
141     private FacesContextFactory facesContextFactory = null;
142 
143     /***
144      * <p>
145      * The {@link Lifecycle}instance to use for request processing.
146      * </p>
147      */
148     private Lifecycle lifecycle = null;
149 
150     /***
151      * <p>
152      * The <code>PortletConfig</code> instance for this portlet.
153      * </p>
154      */
155     private PortletConfig portletConfig = null;
156 
157     /***
158      * <p>
159      * Release all resources acquired at startup time.
160      * </p>
161      */
162     public void destroy()
163     {
164         if (log.isTraceEnabled())
165         {
166             log.trace("Begin FacesPortlet.destory() ");
167         }
168         application = null;
169         facesContextFactory = null;
170         lifecycle = null;
171         portletConfig = null;
172         if (log.isTraceEnabled())
173         {
174             log.trace("End FacesPortlet.destory() ");
175         }
176 
177     }
178 
179     /***
180      * <p>
181      * Acquire the factory instance we will require.
182      * </p>
183      * 
184      * @exception PortletException if, for any reason, the startp of this Faces
185      *                application failed. This includes errors in the config
186      *                file that is parsed before or during the processing of
187      *                this <code>init()</code> method.
188      */
189     public void init(PortletConfig portletConfig) throws PortletException
190     {
191 
192         if (log.isTraceEnabled())
193         {
194             log.trace("Begin FacesPortlet.init() ");
195         }
196 
197         super.init(portletConfig);
198 
199         // Save our PortletConfig instance
200         this.portletConfig = portletConfig;
201         this.defaultViewPage = portletConfig.getInitParameter(PARAM_VIEW_PAGE);
202         this.defaultEditPage = portletConfig.getInitParameter(PARAM_EDIT_PAGE);
203         this.defaultHelpPage = portletConfig.getInitParameter(PARAM_HELP_PAGE);
204         
205         if (null == this.defaultViewPage)
206         {
207             // A Faces Portlet is required to have at least the
208             // defaultViewPage
209             // defined!
210             throw new PortletException("Portlet " + portletConfig.getPortletName()
211                     + " is incorrectly configured. No default View page is defined.");
212         }
213         if (null == this.defaultActionPage)
214         {
215             this.defaultActionPage = this.defaultViewPage;
216         }
217         if (null == this.defaultCustomPage)
218         {
219             this.defaultCustomPage = this.defaultViewPage;
220         }
221         if (null == this.defaultHelpPage)
222         {
223             this.defaultHelpPage = this.defaultViewPage;
224         }
225         if (null == this.defaultEditPage)
226         {
227             this.defaultEditPage = this.defaultViewPage;
228         }
229         if (log.isTraceEnabled())
230         {
231             log.trace("End FacesPortlet.init() ");
232         }
233     }
234 
235     /***
236      * @see javax.portlet.GenericPortlet#doEdit(javax.portlet.RenderRequest,
237      *      javax.portlet.RenderResponse)
238      */
239     public void doEdit(RenderRequest request, RenderResponse response) throws PortletException, IOException
240     {
241         process(request, response, defaultEditPage, FacesPortlet.EDIT_REQUEST, JSF_EDIT_ID);
242     }
243 
244     /***
245      * @see javax.portlet.GenericPortlet#doHelp(javax.portlet.RenderRequest,
246      *      javax.portlet.RenderResponse)
247      */
248     public void doHelp(RenderRequest request, RenderResponse response) throws PortletException, IOException
249     {
250         if (this.defaultHelpPage != null && this.defaultHelpPage.endsWith(".html"))
251         {
252             super.doHelp(request, response);
253         }
254         else
255         {
256             process(request, response, defaultHelpPage, FacesPortlet.HELP_REQUEST, JSF_HELP_ID);
257         }
258     }
259 
260     /***
261      * @param request The {@link RenderRequest}.
262      * @param response The {@link RenderResponse}.
263      * @throws PortletException Throws a {@link PortletException}.
264      * @throws IOException Throws a {@link IOException}.
265      */
266     public void doCustom(RenderRequest request, RenderResponse response) throws PortletException, IOException
267     {
268         process(request, response, defaultCustomPage, FacesPortlet.CUSTOM_REQUEST, JSF_CUSTOM_ID);
269     }
270 
271     /***
272      * @see javax.portlet.GenericPortlet#doView(javax.portlet.RenderRequest,
273      *      javax.portlet.RenderResponse)
274      */
275     public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException
276     {
277         process(request, response, defaultViewPage, FacesPortlet.VIEW_REQUEST, JSF_VIEW_ID);
278     }
279 
280     /***
281      * @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest,
282      *      javax.portlet.ActionResponse)
283      */
284     public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException
285     {
286         String viewId = JSF_CUSTOM_ID;
287         if (request.getPortletMode().equals(PortletMode.VIEW))
288         {
289             viewId = JSF_VIEW_ID;            
290         }
291         else if (request.getPortletMode().equals(PortletMode.EDIT))
292         {
293             viewId = JSF_EDIT_ID;                        
294         }
295         else if (request.getPortletMode().equals(PortletMode.HELP))
296         {
297             viewId = JSF_HELP_ID;                        
298         }
299         process(request, response, defaultActionPage, FacesPortlet.ACTION_REQUEST, viewId);
300     }
301 
302     /***
303      * <p>
304      * Gets the {@link FacesContextFactory}.
305      * </p>
306      * 
307      * @return The {@link FacesContextFactory}.
308      * @throws PortletException Throws a {@link PortletException}.
309      */
310     public FacesContextFactory getFacesContextFactory() throws PortletException
311     {
312         if (facesContextFactory != null)
313         {
314             return facesContextFactory;
315         }
316         try
317         {
318             facesContextFactory = (FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
319             if (log.isTraceEnabled())
320             {
321                 log.trace("Retrieved facesContextFactory " + facesContextFactory);
322             }
323         }
324         catch (FacesException e)
325         {
326             Throwable rootCause = e.getCause();
327             if (rootCause == null)
328             {
329                 throw e;
330             }
331             else
332             {
333                 throw new PortletException(e.getMessage(), rootCause);
334             }
335         }
336         return facesContextFactory;
337     }
338 
339     /***
340      * <p>
341      * Get the faces life cycle.
342      * </p>
343      * 
344      * @return The {@link Lifecycle}.
345      * @throws PortletException Throws a {@link PortletException}.
346      */
347     public Lifecycle getLifecycle() throws PortletException
348     {
349         if (lifecycle != null)
350         {
351             return lifecycle;
352         }
353         try
354         {
355             LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
356                     .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
357             if (log.isTraceEnabled())
358             {
359                 log.trace("Retrieved lifecycleFactory " + lifecycleFactory);
360             }
361             String lifecycleId = portletConfig.getPortletContext().getInitParameter(LIFECYCLE_ID_ATTR);
362             if (log.isDebugEnabled())
363             {
364                 log.debug("lifecycleId " + lifecycleId);
365             }
366             if (lifecycleId == null)
367             {
368                 lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
369             }
370             lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
371             if (log.isTraceEnabled())
372             {
373                 log.trace("Retrieved lifecycle from lifecycleFactory " + lifecycle);
374             }
375         }
376         catch (FacesException e)
377         {
378             Throwable rootCause = e.getCause();
379             if (rootCause == null)
380             {
381                 throw e;
382             }
383             else
384             {
385                 throw new PortletException(e.getMessage(), rootCause);
386             }
387         }
388         return lifecycle;
389     }
390 
391     /***
392      * <p>
393      * Processes the request.
394      * </p>
395      * 
396      * @param request The {@link PortletRequest}.
397      * @param response The {@link PortletResponse}.
398      * @param defaultPage The default page.
399      * @param requestType The request type.
400      * @throws PortletException Throws a {@link PortletException}.
401      * @throws IOException Throws an {@link IOException}.
402      */
403     private void process(PortletRequest request, PortletResponse response, String defaultPage, String requestType, String viewId)
404             throws PortletException, IOException
405     {
406         boolean actionRequest = (request instanceof ActionRequest);
407         boolean renderRequest = (request instanceof RenderRequest);
408         
409         String defaultView = defaultPage;
410         
411         if (actionRequest)
412         {
413             log.trace("Begin FacesPortlet.processAction()");
414         }
415 
416         
417         // Acquire the FacesContext instance for this request
418         FacesContext context = getFacesContextFactory().getFacesContext(
419                 portletConfig, 
420                 request, response, getLifecycle());
421 
422         // Restore view if available.
423         setDefaultView(context, defaultPage, viewId);
424         if (log.isTraceEnabled())
425         {
426             log.trace("Begin Executing phases");
427         }
428 
429         preProcessFaces(context);
430         
431         // Execute the pre-render request processing lifecycle for this request
432         try
433         {
434             if (actionRequest)
435             {
436                 getLifecycle().execute(context);
437                 if (log.isTraceEnabled())
438                 {
439                     log.trace("End Executing phases");
440                 }
441                 // The view should have been restore.
442                 // Pass it to the render request. 
443                                 
444                 request.getPortletSession().setAttribute(createViewRootKey(context, defaultPage, viewId), context.getViewRoot());
445                 ActionResponse actionResponse = (ActionResponse)response;
446                                 
447                 // actionResponse.setRenderParameter(viewId, context.getViewRoot().getViewId()); // get the navigation change
448             }
449             else if (renderRequest)
450             {
451                 //    getLifecycle().execute(context);
452                 String vi = context.getViewRoot().getViewId();
453                 context.getApplication().getViewHandler().restoreView(context, vi);
454                 
455                 getLifecycle().render(context);
456                 if (log.isTraceEnabled())
457                 {
458                     log.trace("End executing RenderResponse phase ");
459                 }
460             }
461             else
462             {
463                 throw new PortletException("Request must be of type ActionRequest or RenderRequest");
464             }            
465             request.getPortletSession().setAttribute(viewId, context.getViewRoot().getViewId(), PortletSession.PORTLET_SCOPE);            
466             
467         }
468         catch (FacesException e)
469         {
470             Throwable t = ((FacesException) e).getCause();
471             if (t == null)
472             {
473                 throw new PortletException(e.getMessage(), e);
474             }
475             else
476             {
477                 if (t instanceof PortletException)
478                 {
479                     throw ((PortletException) t);
480                 }
481                 else if (t instanceof IOException)
482                 {
483                     throw ((IOException) t);
484                 }
485                 else
486                 {
487                     throw new PortletException(t.getMessage(), t);
488                 }
489             }
490         }
491         finally
492         {
493             // Release the FacesContext instance for this request
494             context.release();
495         }
496         if (log.isTraceEnabled())
497         {
498             log.trace("End FacesPortlet.process()");
499         }
500     }
501 
502     protected void preProcessFaces(FacesContext context)
503     {        
504     }
505     
506     
507     private String createViewRootKey(FacesContext context, String defaultView, String viewId)
508     {
509         PortletRequest portletRequest = (PortletRequest) context.getExternalContext().getRequest();
510         // String view = portletRequest.getParameter(viewId);
511         String view = (String)portletRequest.getPortletSession().getAttribute(viewId, PortletSession.PORTLET_SCOPE);
512         
513         if (view == null)
514         {
515             view = defaultView;
516         }
517         String key = VIEW_ROOT + ":" + getPortletName();
518         UIViewRoot root = context.getViewRoot();
519         if (root != null)
520         {
521            key = key + ":" + root.getViewId();
522         }
523         else
524         {
525             key = key + ":" + view;
526         }
527         return key;
528     }
529     
530     /***
531      * <p>
532      * Set the view identifier to the view for the page to be rendered.
533      * </p>
534      * 
535      * @param context The {@link FacesContext}for the current request.
536      * @param defaultView The default view identifier.
537      * @return The default view.
538      */
539     private void setDefaultView(FacesContext facesContext, String defaultView, String viewId)
540     {
541         // Need to be able to transport viewId between actionRequest and
542         // renderRequest.
543         PortletRequest portletRequest = (PortletRequest) facesContext.getExternalContext().getRequest();
544         if (portletRequest instanceof ActionRequest)
545         {
546             String view = (String)portletRequest.getPortletSession().getAttribute(viewId, PortletSession.PORTLET_SCOPE);
547             
548             if ((null != facesContext.getViewRoot()) && (null != facesContext.getViewRoot().getViewId()))
549             {
550                 defaultView = facesContext.getViewRoot().getViewId();
551             }
552             //else if (null != portletRequest.getParameter(viewId))
553             else if (null != view)
554             {
555                 //defaultView = portletRequest.getParameter(viewId);
556                 defaultView = view;
557             }
558             
559             UIViewRoot viewRoot = (UIViewRoot)portletRequest.
560                                     getPortletSession().
561                                     getAttribute(createViewRootKey(facesContext, defaultView, viewId));
562             if (viewRoot != null)
563             {
564                 facesContext.setViewRoot(viewRoot);
565                 defaultView = facesContext.getViewRoot().getViewId();
566             }
567             
568             portletRequest.setAttribute(REQUEST_SERVLET_PATH, defaultView.replaceAll("[.]jsp", ".jsf"));
569         }
570         else if (portletRequest instanceof RenderRequest)
571         {
572             // String view = portletRequest.getParameter(viewId);
573             String view = (String)portletRequest.getPortletSession().getAttribute(viewId, PortletSession.PORTLET_SCOPE);
574             
575             if (null == facesContext.getViewRoot())
576             {                
577                 if (view == null)
578                 {
579                     view = defaultView;
580                 }
581                 UIViewRoot viewRoot = (UIViewRoot)portletRequest.
582                                         getPortletSession().
583                                         getAttribute(createViewRootKey(facesContext, view, viewId));
584                 if (null != viewRoot)
585                 {
586                     facesContext.setViewRoot(viewRoot);
587                     defaultView = facesContext.getViewRoot().getViewId();
588                 }
589                 else
590                 {
591                     facesContext.setViewRoot(new UIViewRoot());
592                     facesContext.getViewRoot().setViewId(view);
593                     facesContext.getViewRoot().setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
594                     portletRequest.getPortletSession().setAttribute(createViewRootKey(facesContext, view, viewId), viewRoot);
595                 }                   
596             }
597             portletRequest.setAttribute(REQUEST_SERVLET_PATH, view.replaceAll(".jsp", ".jsf"));
598         }
599         
600     }
601 }