View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.portals.bridges.jsf;
18  
19  import java.io.IOException;
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import javax.faces.FacesException;
27  import javax.faces.FactoryFinder;
28  import javax.faces.application.Application;
29  import javax.faces.application.FacesMessage;
30  import javax.faces.component.UIViewRoot;
31  import javax.faces.context.FacesContext;
32  import javax.faces.context.FacesContextFactory;
33  import javax.faces.lifecycle.Lifecycle;
34  import javax.faces.lifecycle.LifecycleFactory;
35  import javax.faces.render.RenderKitFactory;
36  import javax.faces.webapp.FacesServlet;
37  import javax.portlet.ActionRequest;
38  import javax.portlet.ActionResponse;
39  import javax.portlet.PortletConfig;
40  import javax.portlet.PortletException;
41  import javax.portlet.PortletMode;
42  import javax.portlet.PortletRequest;
43  import javax.portlet.PortletResponse;
44  import javax.portlet.PortletSession;
45  import javax.portlet.RenderRequest;
46  import javax.portlet.RenderResponse;
47  
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogFactory;
50  import org.apache.portals.bridges.common.GenericServletPortlet;
51  
52  /***
53   * <p>
54   * FacesPortlet utilizes Java Server Faces to create the user interface in a
55   * portlet environment.
56   * </p>
57   * 
58   * @author <a href="mailto:dlestrat@yahoo.com">David Le Strat</a>
59   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
60   */
61  public class FacesPortlet extends GenericServletPortlet
62  {
63  
64      /*** The Log instance for this class. */
65      private static final Log log = LogFactory.getLog(FacesPortlet.class);
66  
67      /***
68       * The VIEW_ROOT used to keep track of action between the action request and
69       * the render request.
70       */
71      public static final String VIEW_ROOT = "org.apache.portals.bridges.jsf.VIEW_ROOT";
72  
73      /***
74       * The REQUEST_SERVLET_PATH used for
75       * externalContext.getRequestServletPath().
76       * externalContext.getRequestServletPath() should return null but this is a
77       * work around an issue with MyFaces JspViewHandler implementation
78       * getServletMapping().
79       */
80      public static final String REQUEST_SERVLET_PATH = "org.apache.portals.bridges.jsf.REQUEST_SERVLET_PATH";
81  
82      /***
83       * The REQUEST_TYPE request attribute can be used to determine the Portlet
84       * request type ({@link #ACTION_REQUEST}, {@link #VIEW_REQUEST},
85       * {@link #HELP_REQUEST}, {@link #EDIT_REQUEST} or {@link #CUSTOM_REQUEST})
86       * of the current request.
87       */
88      public static final String REQUEST_TYPE = "org.apache.portals.bridges.jsf.request_type";
89  
90      /*** The JSF_VIEW_ID used to maintain the state of the view action. */
91      public static final String JSF_VIEW_ID = "jsf_viewid";
92  
93      public static final String JSF_EDIT_ID = "jsf_editid";
94  
95      public static final String JSF_HELP_ID = "jsf_helpid";
96  
97      public static final String JSF_CUSTOM_ID = "jsf_customid";
98  
99      /*** Name of portlet preference for Action page. */
100     public static final String PARAM_ACTION_PAGE = "ActionPage";
101 
102     /*** Name of portlet preference for Custom page. */
103     public static final String PARAM_CUSTOM_PAGE = "CustomPage";
104 
105     /*** Name of portlet preference for Edit page. */
106     public static final String PARAM_EDIT_PAGE = "EditPage";
107 
108     /*** Name of portlet preference for Edit page */
109     public static final String PARAM_HELP_PAGE = "HelpPage";
110 
111     /*** Name of portlet preference for View page */
112     public static final String PARAM_VIEW_PAGE = "ViewPage";
113 
114     /*** Action request. */
115     public static final String ACTION_REQUEST = "ACTION";
116 
117     /*** View request. */
118     public static final String VIEW_REQUEST = "VIEW";
119 
120     /*** Custom request. */
121     public static final String CUSTOM_REQUEST = "CUSTOM";
122 
123     /*** Edit request. */
124     public static final String EDIT_REQUEST = "EDIT";
125 
126     /*** Help request. */
127     public static final String HELP_REQUEST = "HELP";
128 
129     /*** FacesMessage objects on portlet session */
130     public static final String FACES_MESSAGES = "FACES_MESSAGES";
131 
132     /*** Override default behavior for Unique IDS */
133     public static final String PARAM_UNIQUE_IDS = "OverrideUniqueIds";
134 
135     /*** Default URL for the action page. */
136     private String defaultActionPage = null;
137 
138     /*** Default URL for the custom page. */
139     private String defaultCustomPage = null;
140 
141     /*** Default URL for the edit page. */
142     private String defaultEditPage = null;
143 
144     /*** Default URL for the help page. */
145     private String defaultHelpPage = null;
146 
147     /*** Default URL for the view page. */
148     private String defaultViewPage = null;
149 
150     private String uniqueIds = null;
151 
152     /***
153      * <p>
154      * Context initialization parameter name for the lifecycle identifier of the
155      * {@link Lifecycle}instance to be utilized.
156      * </p>
157      */
158     private static final String LIFECYCLE_ID_ATTR = FacesServlet.LIFECYCLE_ID_ATTR;
159 
160     /***
161      * <p>
162      * The {@link Application}instance for this web application.
163      * </p>
164      */
165     private Application application = null;
166 
167     /***
168      * <p>
169      * Factory for {@link FacesContext}instances.
170      * </p>
171      */
172     private FacesContextFactory facesContextFactory = null;
173 
174     /***
175      * <p>
176      * The {@link Lifecycle}instance to use for request processing.
177      * </p>
178      */
179     private Lifecycle lifecycle = null;
180 
181     /***
182      * <p>
183      * The <code>PortletConfig</code> instance for this portlet.
184      * </p>
185      */
186     private PortletConfig portletConfig = null;
187 
188     /***
189      * <p>
190      * Release all resources acquired at startup time.
191      * </p>
192      */
193     public void destroy()
194     {
195         if (log.isTraceEnabled())
196         {
197             log.trace("Begin FacesPortlet.destory() ");
198         }
199         application = null;
200         facesContextFactory = null;
201         lifecycle = null;
202         portletConfig = null;
203         if (log.isTraceEnabled())
204         {
205             log.trace("End FacesPortlet.destory() ");
206         }
207 
208     }
209 
210     /***
211      * <p>
212      * Acquire the factory instance we will require.
213      * </p>
214      * 
215      * @exception PortletException
216      *                if, for any reason, the startp of this Faces application
217      *                failed. This includes errors in the config file that is
218      *                parsed before or during the processing of this
219      *                <code>init()</code> method.
220      */
221     public void init(PortletConfig portletConfig) throws PortletException
222     {
223 
224         if (log.isTraceEnabled())
225         {
226             log.trace("Begin FacesPortlet.init() ");
227         }
228 
229         super.init(portletConfig);
230 
231         // Save our PortletConfig instance
232         this.portletConfig = portletConfig;
233         this.defaultViewPage = portletConfig.getInitParameter(PARAM_VIEW_PAGE);
234         this.defaultEditPage = portletConfig.getInitParameter(PARAM_EDIT_PAGE);
235         this.defaultHelpPage = portletConfig.getInitParameter(PARAM_HELP_PAGE);
236         this.uniqueIds = portletConfig.getInitParameter(PARAM_UNIQUE_IDS);
237 
238         if (null == this.defaultViewPage)
239         {
240             // A Faces Portlet is required to have at least the
241             // defaultViewPage
242             // defined!
243             throw new PortletException(
244                     "Portlet "
245                             + portletConfig.getPortletName()
246                             + " is incorrectly configured. No default View page is defined.");
247         }
248         if (null == this.defaultActionPage)
249         {
250             this.defaultActionPage = this.defaultViewPage;
251         }
252         if (null == this.defaultCustomPage)
253         {
254             this.defaultCustomPage = this.defaultViewPage;
255         }
256         if (null == this.defaultHelpPage)
257         {
258             this.defaultHelpPage = this.defaultViewPage;
259         }
260         if (null == this.defaultEditPage)
261         {
262             this.defaultEditPage = this.defaultViewPage;
263         }
264         if (log.isTraceEnabled())
265         {
266             log.trace("End FacesPortlet.init() ");
267         }
268     }
269 
270     /***
271      * @see javax.portlet.GenericPortlet#doEdit(javax.portlet.RenderRequest,
272      *      javax.portlet.RenderResponse)
273      */
274     public void doEdit(RenderRequest request, RenderResponse response)
275             throws PortletException, IOException
276     {
277         process(request, response, defaultEditPage, FacesPortlet.EDIT_REQUEST,
278                 JSF_EDIT_ID);
279     }
280 
281     /***
282      * @see javax.portlet.GenericPortlet#doHelp(javax.portlet.RenderRequest,
283      *      javax.portlet.RenderResponse)
284      */
285     public void doHelp(RenderRequest request, RenderResponse response)
286             throws PortletException, IOException
287     {
288         if (this.defaultHelpPage != null
289                 && this.defaultHelpPage.endsWith(".html"))
290         {
291             super.doHelp(request, response);
292         } else
293         {
294             process(request, response, defaultHelpPage,
295                     FacesPortlet.HELP_REQUEST, JSF_HELP_ID);
296         }
297     }
298 
299     /***
300      * @param request
301      *            The {@link RenderRequest}.
302      * @param response
303      *            The {@link RenderResponse}.
304      * @throws PortletException
305      *             Throws a {@link PortletException}.
306      * @throws IOException
307      *             Throws a {@link IOException}.
308      */
309     public void doCustom(RenderRequest request, RenderResponse response)
310             throws PortletException, IOException
311     {
312         process(request, response, defaultCustomPage,
313                 FacesPortlet.CUSTOM_REQUEST, JSF_CUSTOM_ID);
314     }
315 
316     /***
317      * @see javax.portlet.GenericPortlet#doView(javax.portlet.RenderRequest,
318      *      javax.portlet.RenderResponse)
319      */
320     public void doView(RenderRequest request, RenderResponse response)
321             throws PortletException, IOException
322     {
323         process(request, response, defaultViewPage, FacesPortlet.VIEW_REQUEST,
324                 JSF_VIEW_ID);
325     }
326 
327     /***
328      * @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest,
329      *      javax.portlet.ActionResponse)
330      */
331     public void processAction(ActionRequest request, ActionResponse response)
332             throws PortletException, IOException
333     {
334         String viewId = JSF_CUSTOM_ID;
335         if (request.getPortletMode().equals(PortletMode.VIEW))
336         {
337             viewId = JSF_VIEW_ID;
338         } else if (request.getPortletMode().equals(PortletMode.EDIT))
339         {
340             viewId = JSF_EDIT_ID;
341         } else if (request.getPortletMode().equals(PortletMode.HELP))
342         {
343             viewId = JSF_HELP_ID;
344         }
345         process(request, response, defaultActionPage,
346                 FacesPortlet.ACTION_REQUEST, viewId);
347     }
348 
349     /***
350      * <p>
351      * Gets the {@link FacesContextFactory}.
352      * </p>
353      * 
354      * @return The {@link FacesContextFactory}.
355      * @throws PortletException
356      *             Throws a {@link PortletException}.
357      */
358     public FacesContextFactory getFacesContextFactory() throws PortletException
359     {
360         if (facesContextFactory != null) { return facesContextFactory; }
361         try
362         {
363             facesContextFactory = (FacesContextFactory) FactoryFinder
364                     .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
365             if (log.isTraceEnabled())
366             {
367                 log.trace("Retrieved facesContextFactory "
368                         + facesContextFactory);
369             }
370         } catch (FacesException e)
371         {
372             Throwable rootCause = e.getCause();
373             if (rootCause == null)
374             {
375                 throw e;
376             } else
377             {
378                 throw new PortletException(e.getMessage(), rootCause);
379             }
380         }
381         return facesContextFactory;
382     }
383 
384     /***
385      * <p>
386      * Get the faces life cycle.
387      * </p>
388      * 
389      * @return The {@link Lifecycle}.
390      * @throws PortletException
391      *             Throws a {@link PortletException}.
392      */
393     public Lifecycle getLifecycle() throws PortletException
394     {
395         if (lifecycle != null) { return lifecycle; }
396         try
397         {
398             LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
399                     .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
400             if (log.isTraceEnabled())
401             {
402                 log.trace("Retrieved lifecycleFactory " + lifecycleFactory);
403             }
404             String lifecycleId = portletConfig.getPortletContext()
405                     .getInitParameter(LIFECYCLE_ID_ATTR);
406             if (log.isDebugEnabled())
407             {
408                 log.debug("lifecycleId " + lifecycleId);
409             }
410             if (lifecycleId == null)
411             {
412                 lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
413             }
414             lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
415             if (log.isTraceEnabled())
416             {
417                 log.trace("Retrieved lifecycle from lifecycleFactory "
418                         + lifecycle);
419             }
420         } catch (FacesException e)
421         {
422             Throwable rootCause = e.getCause();
423             if (rootCause == null)
424             {
425                 throw e;
426             } else
427             {
428                 throw new PortletException(e.getMessage(), rootCause);
429             }
430         }
431         return lifecycle;
432     }
433 
434     /***
435      * <p>
436      * Processes the request.
437      * </p>
438      * 
439      * @param request
440      *            The {@link PortletRequest}.
441      * @param response
442      *            The {@link PortletResponse}.
443      * @param defaultPage
444      *            The default page.
445      * @param requestType
446      *            The request type.
447      * @throws PortletException
448      *             Throws a {@link PortletException}.
449      * @throws IOException
450      *             Throws an {@link IOException}.
451      */
452     private void process(PortletRequest request, PortletResponse response,
453             String defaultPage, String requestType, String viewId)
454             throws PortletException, IOException
455     {
456         boolean actionRequest = ACTION_REQUEST.equals(requestType);
457         boolean renderRequest = !actionRequest;
458         String defaultView = defaultPage;
459 
460         request.setAttribute(REQUEST_TYPE, requestType);
461 
462         if (actionRequest)
463         {
464             log.trace("Begin FacesPortlet.processAction()");
465         }
466 
467         // remove any lingering JSF attributes
468         cleanUpAfterPortal(request, response);
469 
470         // Acquire the FacesContext instance for this request
471         FacesContext context = getFacesContextFactory().getFacesContext(
472                 portletConfig.getPortletContext(), request, response,
473                 getLifecycle());
474 
475         // Restore view if available.
476         setDefaultView(context, defaultPage, viewId);
477         if (log.isTraceEnabled())
478         {
479             log.trace("Begin Executing phases");
480         }
481 
482         preProcessFaces(context);
483 
484         // Execute the pre-render request processing lifecycle for this request
485         try
486         {
487             if (actionRequest)
488             {
489                 String vi = context.getViewRoot().getViewId();
490                 context.getApplication().getViewHandler().restoreView(context, vi);
491                 getLifecycle().execute(context);
492                 if (log.isTraceEnabled())
493                 {
494                     log.trace("End Executing phases");
495                 }
496                 // The view should have been restore.
497                 // Pass it to the render request.
498                 request.getPortletSession().setAttribute(
499                         createViewRootKey(context, defaultPage, viewId),
500                         context.getViewRoot());
501                 ActionResponse actionResponse = (ActionResponse) response;
502 
503                 // save FacesMessage objects on session; so they can
504                 // be restored during the render phase
505                 saveFacesMessages(context, request.getPortletSession());
506             } else if (renderRequest)
507             {
508                 // getLifecycle().execute(context);
509                 String vi = context.getViewRoot().getViewId();
510                 context.getApplication().getViewHandler().restoreView(context,
511                         vi);
512 
513                 // restore FacesMessage objects that were previously established
514                 // in the lifecycle
515                 restoreFacesMessages(context, request.getPortletSession());
516 
517                 getLifecycle().render(context);
518                 if (log.isTraceEnabled())
519                 {
520                     log.trace("End executing RenderResponse phase ");
521                 }
522             } else
523             {
524                 throw new PortletException(
525                         "Request must be of type ActionRequest or RenderRequest");
526             }
527 
528             request.getPortletSession().setAttribute(viewId,
529                     context.getViewRoot().getViewId(),
530                     PortletSession.PORTLET_SCOPE);
531 
532         } catch (FacesException e)
533         {
534             Throwable t = ((FacesException) e).getCause();
535             if (t == null)
536             {
537                 throw new PortletException(e.getMessage(), e);
538             } else
539             {
540                 if (t instanceof PortletException)
541                 {
542                     throw ((PortletException) t);
543                 } else if (t instanceof IOException)
544                 {
545                     throw ((IOException) t);
546                 } else
547                 {
548                     throw new PortletException(t.getMessage(), t);
549                 }
550             }
551         } finally
552         {
553             // Release the FacesContext instance for this request
554             context.release();
555         }
556 
557         if (log.isTraceEnabled())
558         {
559             log.trace("End FacesPortlet.process()");
560         }
561     }
562 
563     protected void preProcessFaces(FacesContext context)
564     {
565     }
566 
567     private String createViewRootKey(FacesContext context, String defaultView,
568             String viewId)
569     {
570         PortletRequest portletRequest = (PortletRequest) context
571                 .getExternalContext().getRequest();
572         // String view = portletRequest.getParameter(viewId);
573         String view = (String) portletRequest.getPortletSession().getAttribute(
574                 viewId, PortletSession.PORTLET_SCOPE);
575 
576         if (view == null)
577         {
578             view = defaultView;
579         }
580         String key = VIEW_ROOT + ":" + getPortletName();
581         UIViewRoot root = context.getViewRoot();
582         if (root != null)
583         {
584             key = key + ":" + root.getViewId();
585         } else
586         {
587             key = key + ":" + view;
588         }
589         if (uniqueIds != null)
590         {
591             PortletResponse response = (PortletResponse) context
592                     .getExternalContext().getResponse();
593             if (!(response instanceof RenderResponse))
594             {
595                 log.error("Cant encode action response");
596             } else
597             {
598                 RenderResponse rr = (RenderResponse) response;
599                 key = key + rr.getNamespace();
600             }
601         }
602         return key;
603     }
604 
605     /***
606      * <p>
607      * Set the view identifier to the view for the page to be rendered.
608      * </p>
609      * 
610      * @param context
611      *            The {@link FacesContext}for the current request.
612      * @param defaultView
613      *            The default view identifier.
614      * @return The default view.
615      */
616     private void setDefaultView(FacesContext facesContext, String defaultView,
617             String viewId)
618     {
619         // Need to be able to transport viewId between actionRequest and
620         // renderRequest.
621         PortletRequest portletRequest = (PortletRequest) facesContext
622                 .getExternalContext().getRequest();
623         if (portletRequest instanceof ActionRequest)
624         {
625             String view = (String) portletRequest.getPortletSession()
626                     .getAttribute(viewId, PortletSession.PORTLET_SCOPE);
627 
628             if ((null != facesContext.getViewRoot())
629                     && (null != facesContext.getViewRoot().getViewId()))
630             {
631                 defaultView = facesContext.getViewRoot().getViewId();
632             }
633             // else if (null != portletRequest.getParameter(viewId))
634             else if (null != view)
635             {
636                 // defaultView = portletRequest.getParameter(viewId);
637                 defaultView = view;
638             }
639 
640             UIViewRoot viewRoot = (UIViewRoot) portletRequest
641                     .getPortletSession()
642                     .getAttribute(
643                             createViewRootKey(facesContext, defaultView, viewId));
644             if (viewRoot != null)
645             {
646                 facesContext.setViewRoot(viewRoot);
647                 defaultView = facesContext.getViewRoot().getViewId();
648             } else
649             {
650                 facesContext.setViewRoot(new PortletUIViewRoot());
651                 facesContext.getViewRoot().setViewId(view);
652                 String defaultRenderKitId = facesContext.getApplication().getDefaultRenderKitId();
653                 facesContext.getViewRoot().setRenderKitId(defaultRenderKitId != null ? defaultRenderKitId : RenderKitFactory.HTML_BASIC_RENDER_KIT);
654                 portletRequest.getPortletSession().setAttribute(
655                         createViewRootKey(facesContext, view, viewId),
656                         facesContext.getViewRoot());
657             }
658             portletRequest.setAttribute(REQUEST_SERVLET_PATH, defaultView
659                     .replaceAll("[.]jsp", ".jsf"));
660         } else if (portletRequest instanceof RenderRequest)
661         {
662             // String view = portletRequest.getParameter(viewId);
663             String view = (String) portletRequest.getPortletSession()
664                     .getAttribute(viewId, PortletSession.PORTLET_SCOPE);
665 
666             if (null == facesContext.getViewRoot())
667             {
668                 if (view == null)
669                 {
670                     view = defaultView;
671                 }
672                 UIViewRoot viewRoot = (UIViewRoot) portletRequest
673                         .getPortletSession().getAttribute(
674                                 createViewRootKey(facesContext, view, viewId));
675                 if (null != viewRoot)
676                 {
677                     facesContext.setViewRoot(viewRoot);
678                     defaultView = facesContext.getViewRoot().getViewId();
679                 } else
680                 {
681                     facesContext.setViewRoot(new PortletUIViewRoot());
682                     facesContext.getViewRoot().setViewId(view);
683                     String defaultRenderKitId = facesContext.getApplication().getDefaultRenderKitId();
684                     facesContext.getViewRoot().setRenderKitId(defaultRenderKitId != null ? defaultRenderKitId : RenderKitFactory.HTML_BASIC_RENDER_KIT);
685                     portletRequest.getPortletSession().setAttribute(
686                             createViewRootKey(facesContext, view, viewId),
687                             facesContext.getViewRoot());
688                 }
689             }
690             portletRequest.setAttribute(REQUEST_SERVLET_PATH, view.replaceAll(
691                     ".jsp", ".jsf"));
692         }
693     }
694 
695     /***
696      * Save FacesMessage objects on the PortletSession
697      * 
698      * @param context
699      * @param session
700      */
701     private void saveFacesMessages(FacesContext context, PortletSession session)
702     {
703         Iterator msgs = context.getMessages();
704         if (msgs != null && msgs.hasNext())
705         {
706             Map facesMsgs = new HashMap();
707 
708             // messages that have a clientId
709             Iterator idsWithMsgs = context.getClientIdsWithMessages();
710             while (idsWithMsgs.hasNext())
711             {
712                 String clientId = (String) idsWithMsgs.next();
713                 List clientMsgList = (List) facesMsgs.get(clientId);
714                 if (clientMsgList == null)
715                 {
716                     clientMsgList = new ArrayList();
717                     facesMsgs.put(clientId, clientMsgList);
718                 }
719 
720                 Iterator clientMsgs = context.getMessages(clientId);
721                 while (clientMsgs != null && clientMsgs.hasNext())
722                 {
723                     clientMsgList.add(clientMsgs.next());
724                 }
725             }
726 
727             // messages that do not have a clientId
728             Iterator msgsWithoutId = context.getMessages(null);
729             if (msgsWithoutId != null && msgsWithoutId.hasNext())
730             {
731                 List msgWithoutIdList = new ArrayList();
732                 while (msgsWithoutId.hasNext())
733                 {
734                     msgWithoutIdList.add(msgsWithoutId.next());
735                 }
736 
737                 facesMsgs.put("null", msgWithoutIdList);
738             }
739 
740             // save messages on session
741             session.setAttribute(FACES_MESSAGES, facesMsgs);
742         }
743     }
744 
745     /***
746      * Restore FacesMessage objects from the PortletSession
747      * 
748      * @param context
749      * @param session
750      */
751     private void restoreFacesMessages(FacesContext context,
752             PortletSession session)
753     {
754         Map facesMsgs = (Map) session.getAttribute(FACES_MESSAGES);
755 
756         if (facesMsgs != null)
757         {
758             for (Iterator clientIds = facesMsgs.keySet().iterator(); clientIds
759                     .hasNext();)
760             {
761                 String clientId = (String) clientIds.next();
762                 List clientMsgList = (List) facesMsgs.get(clientId);
763 
764                 // did not originally have a clientId specified
765                 if (clientId.equals("null"))
766                 {
767                     clientId = null;
768                 }
769 
770                 // restore messages on the FacesContext
771                 for (int index = 0; index < clientMsgList.size(); ++index)
772                 {
773                     FacesMessage msg = (FacesMessage) clientMsgList.get(index);
774                     context.addMessage(clientId, msg);
775                 }
776 
777                 clientMsgList.clear();
778             }
779 
780             facesMsgs.clear();
781             session.setAttribute(FACES_MESSAGES, null);
782         }
783     }
784 
785     /***
786      * Removes temporary JSF attributes from the request.
787      * 
788      * Under certain circumstances, internal JSF attributes from one portlet become
789      * available to another portlet on the same page (this can happen, for example, 
790      * when first portlet throws an exception while rendering). If this happens,
791      * a portlet would not render correctly. 
792      * 
793      * Theoretically, Portlet server should make sure that no request attributes
794      * from one portlet are visible to another portlet. In practice this isn't
795      * always the case, so a portlet needs to remove those request attributes
796      * before doing anything else. 
797      * 
798      * @param request portlet request we are processing
799      * @param response portlet response we are processing
800      */
801     protected void cleanUpAfterPortal(PortletRequest request,
802             PortletResponse response)
803     {
804         if (request != null)
805         {
806             request.removeAttribute("javax.faces.webapp.COMPONENT_TAG_STACK");
807             request.removeAttribute("javax.faces.webapp.CURRENT_VIEW_ROOT");
808             request.removeAttribute("javax.faces.webapp.CURRENT_FACES_CONTEXT");
809         }
810     }
811 }