View Javadoc

    1   /*
    2    * $Id: RestoreViewInterceptor.java 471756 2006-11-06 15:01:43Z husted $
    3    *
    4    * Licensed to the Apache Software Foundation (ASF) under one
    5    * or more contributor license agreements.  See the NOTICE file
    6    * distributed with this work for additional information
    7    * regarding copyright ownership.  The ASF licenses this file
    8    * to you under the Apache License, Version 2.0 (the
    9    * "License"); you may not use this file except in compliance
    10   * with the License.  You may obtain a copy of the License at
    11   *
    12   *  http://www.apache.org/licenses/LICENSE-2.0
    13   *
    14   * Unless required by applicable law or agreed to in writing,
    15   * software distributed under the License is distributed on an
    16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    17   * KIND, either express or implied.  See the License for the
    18   * specific language governing permissions and limitations
    19   * under the License.
    20   */
    21  package org.apache.struts2.jsf;
    22
    23  import java.io.IOException;
    24  import java.lang.reflect.Method;
    25  import java.util.Iterator;
    26
    27  import javax.faces.FacesException;
    28  import javax.faces.application.Application;
    29  import javax.faces.application.ViewHandler;
    30  import javax.faces.component.UIComponent;
    31  import javax.faces.component.UIInput;
    32  import javax.faces.component.UIViewRoot;
    33  import javax.faces.context.ExternalContext;
    34  import javax.faces.context.FacesContext;
    35  import javax.faces.el.ValueBinding;
    36  import javax.faces.event.PhaseId;
    37
    38  /***
    39   * Restores the view or component tree
    40   */
    41  public class RestoreViewInterceptor extends FacesInterceptor {
    42
    43      private static final long serialVersionUID = -1500785113037140668L;
    44
    45      /***
    46       * Restore View (JSF.2.2.1)
    47       *
    48       * @param viewId
    49       *            The view id
    50       * @param facesContext
    51       *            The faces context
    52       * @return true, if immediate rendering should occur
    53       */
    54      protected boolean executePhase(String viewId, FacesContext facesContext) {
    55          boolean skipFurtherProcessing = false;
    56          if (log.isTraceEnabled())
    57              log.trace("entering restoreView");
    58
    59          informPhaseListenersBefore(facesContext, PhaseId.RESTORE_VIEW);
    60
    61          try {
    62              if (isResponseComplete(facesContext, "restoreView", true)) {
    63                  // have to skips this phase
    64                  return true;
    65              }
    66              if (shouldRenderResponse(facesContext, "restoreView", true)) {
    67                  skipFurtherProcessing = true;
    68              }
    69
    70              ExternalContext externalContext = facesContext.getExternalContext();
    71              String defaultSuffix = externalContext
    72                      .getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
    73              String suffix = defaultSuffix != null ? defaultSuffix
    74                      : ViewHandler.DEFAULT_SUFFIX;
    75              if (viewId != null) {
    76                  viewId += suffix;
    77              }
    78
    79              if (viewId == null) {
    80                  if (!externalContext.getRequestServletPath().endsWith("/")) {
    81                      try {
    82                          externalContext.redirect(externalContext
    83                                  .getRequestServletPath()
    84                                  + "/");
    85                          facesContext.responseComplete();
    86                          return true;
    87                      } catch (IOException e) {
    88                          throw new FacesException("redirect failed", e);
    89                      }
    90                  }
    91              }
    92
    93              Application application = facesContext.getApplication();
    94              ViewHandler viewHandler = application.getViewHandler();
    95
    96              // boolean viewCreated = false;
    97              UIViewRoot viewRoot = viewHandler.restoreView(facesContext, viewId);
    98              if (viewRoot == null) {
    99                  viewRoot = viewHandler.createView(facesContext, viewId);
    100                 viewRoot.setViewId(viewId);
    101                 facesContext.renderResponse();
    102                 // viewCreated = true;
    103             }
    104
    105             facesContext.setViewRoot(viewRoot);
    106
    107             /*
    108              * This section has been disabled because it causes some bug. Be
    109              * careful if you need to re-enable it. Furthermore, for an unknown
    110              * reason, it seems that by default it is executed (i.e.
    111              * log.isTraceEnabled() is true). Bug example : This traceView
    112              * causes DebugUtil.printComponent to print all the attributes of
    113              * the view components. And if you have a data table within an
    114              * aliasBean, this causes the data table to initialize it's value
    115              * attribute while the alias isn't set. So, the value initializes
    116              * with an UIData.EMPTY_DATA_MODEL, and not with the aliased one.
    117              * But as it's initialized, it will not try to get the value from
    118              * the ValueBinding next time it needs to. I expect this to cause
    119              * more similar bugs. TODO : Completely remove or be SURE by default
    120              * it's not executed, and it has no more side-effects.
    121              *
    122              * if (log.isTraceEnabled()) { //Note: DebugUtils Logger must also
    123              * be in trace level DebugUtils.traceView(viewCreated ? "Newly
    124              * created view" : "Restored view"); }
    125              */
    126
    127             if (facesContext.getExternalContext().getRequestParameterMap()
    128                     .isEmpty()) {
    129                 // no POST or query parameters --> set render response flag
    130                 facesContext.renderResponse();
    131             }
    132
    133             recursivelyHandleComponentReferencesAndSetValid(facesContext,
    134                     viewRoot);
    135         } finally {
    136             informPhaseListenersAfter(facesContext, PhaseId.RESTORE_VIEW);
    137         }
    138
    139         if (isResponseComplete(facesContext, "restoreView", false)
    140                 || shouldRenderResponse(facesContext, "restoreView", false)) {
    141             // since this phase is completed we don't need to return right away
    142             // even if the response is completed
    143             skipFurtherProcessing = true;
    144         }
    145
    146         if (!skipFurtherProcessing && log.isTraceEnabled())
    147             log.trace("exiting restoreView ");
    148         return skipFurtherProcessing;
    149     }
    150
    151     /***
    152      * Walk the component tree, executing any component-bindings to reattach
    153      * components to their backing beans. Also, any UIInput component is marked
    154      * as Valid.
    155      * <p>
    156      * Note that this method effectively breaks encapsulation; instead of asking
    157      * each component to update itself and its children, this method just
    158      * reaches into each component. That makes it impossible for any component
    159      * to customise its behaviour at this point.
    160      * <p>
    161      * This has been filed as an issue against the spec. Until this issue is
    162      * resolved, we'll add a new marker-interface for components to allow them
    163      * to define their interest in handling children bindings themselves.
    164      */
    165     protected void recursivelyHandleComponentReferencesAndSetValid(
    166             FacesContext facesContext, UIComponent parent) {
    167         recursivelyHandleComponentReferencesAndSetValid(facesContext, parent,
    168                 false);
    169     }
    170
    171     protected void recursivelyHandleComponentReferencesAndSetValid(
    172             FacesContext facesContext, UIComponent parent, boolean forceHandle) {
    173         Method handleBindingsMethod = getBindingMethod(parent);
    174
    175         if (handleBindingsMethod != null && !forceHandle) {
    176             try {
    177                 handleBindingsMethod.invoke(parent, new Object[] {});
    178             } catch (Throwable th) {
    179                 log.error(
    180                         "Exception while invoking handleBindings on component with client-id:"
    181                                 + parent.getClientId(facesContext), th);
    182             }
    183         } else {
    184             for (Iterator it = parent.getFacetsAndChildren(); it.hasNext();) {
    185                 UIComponent component = (UIComponent) it.next();
    186
    187                 ValueBinding binding = component.getValueBinding("binding"); // TODO:
    188                 // constant
    189                 if (binding != null && !binding.isReadOnly(facesContext)) {
    190                     binding.setValue(facesContext, component);
    191                 }
    192
    193                 if (component instanceof UIInput) {
    194                     ((UIInput) component).setValid(true);
    195                 }
    196
    197                 recursivelyHandleComponentReferencesAndSetValid(facesContext,
    198                         component);
    199             }
    200         }
    201     }
    202
    203     /***
    204      * This is all a hack to work around a spec-bug which will be fixed in
    205      * JSF2.0
    206      *
    207      * @param parent
    208      * @return true if this component is bindingAware (e.g. aliasBean)
    209      */
    210     private static Method getBindingMethod(UIComponent parent) {
    211         Class[] clazzes = parent.getClass().getInterfaces();
    212
    213         for (int i = 0; i < clazzes.length; i++) {
    214             Class clazz = clazzes[i];
    215
    216             if (clazz.getName().indexOf("BindingAware") != -1) {
    217                 try {
    218                     return parent.getClass().getMethod("handleBindings",
    219                             new Class[] {});
    220                 } catch (NoSuchMethodException e) {
    221                     // return
    222                 }
    223             }
    224         }
    225
    226         return null;
    227     }
    228 }