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 }