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
    20  package org.apache.myfaces.orchestra.viewController.jsf;
    21
    22  import java.util.Set;
    23  import java.util.TreeSet;
    24
    25  import javax.faces.component.UIViewRoot;
    26  import javax.faces.context.FacesContext;
    27  import javax.faces.event.PhaseEvent;
    28  import javax.faces.event.PhaseId;
    29  import javax.faces.event.PhaseListener;
    30
    31  import org.apache.commons.logging.Log;
    32  import org.apache.commons.logging.LogFactory;
    33  import org.apache.myfaces.orchestra.viewController.ViewControllerManager;
    34  import org.apache.myfaces.orchestra.viewController.ViewControllerManagerFactory;
    35
    36  /**
    37   * Causes lifecycle methods to be invoked on backing beans that are associated with
    38   * the current view.
    39   * <p>
    40   * For details about when initView callbacks occur, see the documentation for
    41   * method initView on class ViewController.
    42   * <p>
    43   * See the javadoc for class ViewControllerManager on how to configure the ViewController
    44   * behaviour.
    45   * <p>
    46   * Note that at the moment this does not support a ViewController bean for subviews
    47   * (ie f:subView tags), which the Shale ViewController framework does provide. ViewController
    48   * beans can only be associated with the main viewId, ie the "top level" view template file.
    49   * <p>
    50   * Note that some callbacks might not be invoked if exceptions are thrown by actionlisteners,
    51   * etc (which again Shale guarantees). This is particularly important for an "endView"
    52   * callback, where resources allocated on initView (such as database connections) might
    53   * be released. Hopefully this will be implemented in some later Orchestra release.
    54   */
    55  public class ViewControllerPhaseListener implements PhaseListener
    56  {
    57      private static final long serialVersionUID = -3975277433747722402L;
    58      private final Log log = LogFactory.getLog(ViewControllerPhaseListener.class);
    59
    60      /**
    61       * @since 1.1
    62       */
    63      public static class ViewControllerPhaseListenerState
    64      {
    65          private Set initedViews = new TreeSet();
    66
    67          protected ViewControllerPhaseListenerState()
    68          {
    69          }
    70      }
    71
    72      public void beforePhase(PhaseEvent event)
    73      {
    74          if (PhaseId.RESTORE_VIEW.equals(event.getPhaseId()) ||
    75              PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
    76          {
    77              assertConversationState(event.getFacesContext());
    78              if (event.getFacesContext().getResponseComplete())
    79              {
    80                  // we have a redirect ... stop now
    81                  return;
    82              }
    83          }
    84
    85          // Try to init the view in every phase, just so we are sure to never miss it.
    86          // This skips the actual call if init has already happened for the current
    87          // view root instance.
    88          executeInitView(event.getFacesContext());
    89
    90          if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
    91          {
    92              preRenderResponse(event.getFacesContext());
    93          }
    94
    95          if (PhaseId.INVOKE_APPLICATION.equals(event.getPhaseId()))
    96          {
    97              preInvokeApplication(event.getFacesContext());
    98          }
    99      }
    100
    101     public void afterPhase(PhaseEvent event)
    102     {
    103         if (PhaseId.RESTORE_VIEW.equals(event.getPhaseId()))
    104         {
    105             assertConversationState(event.getFacesContext());
    106             if (event.getFacesContext().getResponseComplete())
    107             {
    108                 // we have a redirect ... stop now
    109                 return;
    110             }
    111         }
    112
    113         executeInitView(event.getFacesContext());
    114     }
    115
    116     public PhaseId getPhaseId()
    117     {
    118         return PhaseId.ANY_PHASE;
    119     }
    120
    121     protected String getViewId(FacesContext facesContext)
    122     {
    123         UIViewRoot viewRoot = facesContext.getViewRoot();
    124         if (viewRoot == null)
    125         {
    126             return null;
    127         }
    128         return viewRoot.getViewId();
    129     }
    130
    131     /**
    132      * invoked multiple times during the lifecycle to ensure the conversation(s)
    133      * to the associated viewController are running.
    134      *
    135      * @param facesContext
    136      */
    137     protected void assertConversationState(FacesContext facesContext)
    138     {
    139         ViewControllerManager manager = ViewControllerManagerFactory.getInstance();
    140         if (manager == null)
    141         {
    142             return;
    143         }
    144
    145         String viewId = getViewId(facesContext);
    146         if (viewId == null)
    147         {
    148             return;
    149         }
    150
    151         manager.assertConversationState(viewId);
    152     }
    153
    154     /**
    155      * invokes the preRenderView method on your view controller
    156      */
    157     protected void preRenderResponse(FacesContext facesContext)
    158     {
    159         ViewControllerManager manager = ViewControllerManagerFactory.getInstance();
    160         if (manager == null)
    161         {
    162             return;
    163         }
    164
    165         String viewId = getViewId(facesContext);
    166         if (viewId == null)
    167         {
    168             return;
    169         }
    170
    171         manager.executePreRenderView(viewId);
    172     }
    173
    174     /**
    175      * invokes the initView method on your view controller
    176      * @since 1.1
    177      */
    178     protected void executeInitView(FacesContext facesContext)
    179     {
    180         postRestoreView(facesContext);
    181     }
    182
    183     /**
    184      * @deprecated overload/use {@link #executeInitView(javax.faces.context.FacesContext)} instead
    185      */
    186     protected void postRestoreView(FacesContext facesContext)
    187     {
    188         ViewControllerManager manager = ViewControllerManagerFactory.getInstance();
    189         if (manager == null)
    190         {
    191             return;
    192         }
    193
    194         UIViewRoot viewRoot = facesContext.getViewRoot();
    195         if (viewRoot == null)
    196         {
    197             return;
    198         }
    199
    200         String viewId = viewRoot.getViewId();
    201         if (viewId == null)
    202         {
    203             return;
    204         }
    205
    206         // Here we keep track of the ViewRoot instances that we have already called initView for,
    207         // and if it changes then we call initView again.
    208         //
    209         // An alternative would be to keep track of the ViewController instance, and call initView
    210         // if that instance changes. But this is tricky as this object is often a proxy for the
    211         // real object, and may not change even when the target is invalidated and recreated.
    212
    213         String viewKey = String.valueOf(System.identityHashCode(viewRoot));
    214         ViewControllerPhaseListenerState state = getState(facesContext);
    215         if (state.initedViews.contains(viewKey))
    216         {
    217             // this view instance is already initialized
    218             if (log.isDebugEnabled())
    219             {
    220                 log.debug("Skipping already-initialized viewcontroller bean " + viewKey + " for view " + viewId);
    221             }
    222             return;
    223         }
    224
    225         if (log.isDebugEnabled())
    226         {
    227             log.debug("Initializing viewcontroller bean " + viewKey + " for view " + viewId);
    228         }
    229
    230         state.initedViews.add(viewKey);
    231         manager.executeInitView(viewId);
    232     }
    233
    234     protected ViewControllerPhaseListenerState getState(FacesContext facesContext)
    235     {
    236         ViewControllerPhaseListenerState state = (ViewControllerPhaseListenerState)
    237             facesContext.getExternalContext().getRequestMap()
    238                 .get(ViewControllerPhaseListenerState.class.getName());
    239         if (state == null)
    240         {
    241             state = new ViewControllerPhaseListenerState();
    242             facesContext.getExternalContext().getRequestMap().put(
    243                 ViewControllerPhaseListenerState.class.getName(), state);
    244         }
    245         return state;
    246     }
    247
    248     /**
    249      * invokes the preProcess method on your view controller
    250      */
    251     protected void preInvokeApplication(FacesContext facesContext)
    252     {
    253         ViewControllerManager manager = ViewControllerManagerFactory.getInstance();
    254         if (manager == null)
    255         {
    256             return;
    257         }
    258
    259         String viewId = getViewId(facesContext);
    260         if (viewId == null)
    261         {
    262             return;
    263         }
    264
    265         manager.executePreProcess(viewId);
    266     }
    267 }