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.conversation.jsf.lib;
    21
    22  import java.util.Collection;
    23
    24  import javax.faces.FacesException;
    25  import javax.faces.component.StateHolder;
    26  import javax.faces.component.UIComponentBase;
    27  import javax.faces.context.FacesContext;
    28  import javax.faces.el.EvaluationException;
    29  import javax.faces.el.MethodBinding;
    30  import javax.faces.el.MethodNotFoundException;
    31
    32  import org.apache.myfaces.orchestra.conversation.ConversationManager;
    33  import org.apache.myfaces.orchestra.conversation.ConversationUtils;
    34
    35  /**
    36   * A facade for the original method binding to deal with end conversation conditions.
    37   * <p>
    38   * This class implements MethodBinding, ie represents an EL expression string that specifies
    39   * a method to call. It is expected to be used when invoking action methods when the current
    40   * conversation should be closed upon certain results of the action.
    41   * <p>
    42   * This facade also enhances error-handling for action methods. If the invoked method throws
    43   * an exception of any kind, and an errorOutcome value has been specified then the errorOutcome
    44   * is returned instead of allowing the exception to propagate. The exception that occurred is
    45   * reported to the ConversationMessager object associated with the conversation, so it can
    46   * choose whether and how to present the error to the user.
    47   */
    48  public class _EndConversationMethodBindingFacade extends MethodBinding implements StateHolder
    49  {
    50      private MethodBinding original;
    51      private String conversationName;
    52      private Collection onOutcomes;
    53      private String errorOutcome;
    54
    55      private boolean _transient = false;
    56
    57      public _EndConversationMethodBindingFacade()
    58      {
    59      }
    60
    61      /**
    62       * Constructor.
    63       *
    64       * @param conversation is the name of the conversation to conditionally be closed.
    65       *
    66       * @param onOutcomes is a collection of navigation strings that may be returned from the
    67       * invoked method. One of the following rules is then used to determine whether the conversation
    68       * is ended or not:
    69       * <ul>
    70       * <li>If there was no action to invoke, end the conversation, else</li>
    71       * <li>If the action returned null, do not end the conversation, else</li>
    72       * <li>If the onOutcomes list is null or empty then end the conversation, else</li>
    73       * <li>If the returned value is in the onOutcomes list, then end the conversation, else</li>
    74       * <li>do not end the conversation.</li>
    75       * </ul>
    76       *
    77       * @param original is the EL expression to be invoked.
    78       *
    79       * @param errorOutcome is a JSF navigation string to be returned if the action method
    80       * throws an exception of any kind. This navigation value is checked against the onOutcomes
    81       * values just as if the action method had actually returned this value. When not specified,
    82       * then on exception the current conversation is not ended.
    83       */
    84      public _EndConversationMethodBindingFacade(
    85          String conversation,
    86          Collection onOutcomes,
    87          MethodBinding original,
    88          String errorOutcome)
    89      {
    90          this.original = original;
    91          this.conversationName = conversation;
    92          this.onOutcomes = onOutcomes;
    93          this.errorOutcome = errorOutcome;
    94      }
    95
    96      public String getConversationName()
    97      {
    98          return conversationName;
    99      }
    100
    101     public String getExpressionString()
    102     {
    103         if (original == null)
    104         {
    105             return null;
    106         }
    107         return original.getExpressionString();
    108     }
    109
    110     public Class getType(FacesContext context) throws MethodNotFoundException
    111     {
    112         if (original == null)
    113         {
    114             return null;
    115         }
    116         return original.getType(context);
    117     }
    118
    119     public Object invoke(FacesContext context, Object[] values) throws EvaluationException, MethodNotFoundException
    120     {
    121         Object returnValue = null;
    122         //noinspection CatchGenericClass
    123         try
    124         {
    125             if (original != null)
    126             {
    127                 returnValue = original.invoke(context, values);
    128             }
    129         }
    130         catch (Throwable t)
    131         {
    132             ConversationManager conversationManager = ConversationManager.getInstance();
    133
    134             if (errorOutcome != null)
    135             {
    136                 // Suppress the exception, and act as if errorOutcome had been returned.
    137
    138                 conversationManager.getMessager().setConversationException(t);
    139                 returnValue = errorOutcome;
    140             }
    141             else
    142             {
    143                 // When no errorOutcomes are specified, then there is nothing to check against
    144                 // the onOutcomes list.
    145                 //
    146                 // Note that in this case the conversation is NEVER ended. It is debatable what
    147                 // the correct behaviour should be here. If this action wrapper was not present
    148                 // then the conversation would not be terminated, so an instance with no
    149                 // errorOutcomes specified is consistent with that. In any case, the user can
    150                 // easily get the alternate behaviour by simply specifying an errorOutcome and
    151                 // adding that to the onOutcomes list.
    152
    153                 returnValue = null; // do not end conversation
    154                 throw new FacesException(t);
    155             }
    156         }
    157         finally
    158         {
    159             boolean endConversation;
    160             if (original == null)
    161             {
    162                 endConversation = true;
    163             }
    164             else if (returnValue == null)
    165             {
    166                 endConversation = false;
    167             }
    168             else if (onOutcomes == null || onOutcomes.isEmpty())
    169             {
    170                 endConversation = true;
    171             }
    172             else
    173             {
    174                 endConversation = onOutcomes.contains(returnValue);
    175             }
    176
    177             if (endConversation)
    178             {
    179                 ConversationUtils.invalidateIfExists(conversationName);
    180             }
    181         }
    182         return returnValue;
    183     }
    184
    185     /** Required by StateHolder interface. */
    186     public void setTransient(boolean newTransientValue)
    187     {
    188         _transient = newTransientValue;
    189     }
    190
    191     /** Required by StateHolder interface. */
    192     public boolean isTransient()
    193     {
    194         return _transient;
    195     }
    196
    197     public void restoreState(FacesContext context, Object states)
    198     {
    199         Object[] state = (Object[]) states;
    200
    201         original = (MethodBinding) UIComponentBase.restoreAttachedState(context, state[0]);
    202         conversationName = (String) state[1];
    203         onOutcomes = (Collection) state[2];
    204         errorOutcome = (String) state[3];
    205     }
    206
    207     public Object saveState(FacesContext context)
    208     {
    209         return new Object[]
    210             {
    211                 UIComponentBase.saveAttachedState(context, original),
    212                 conversationName,
    213                 onOutcomes,
    214                 errorOutcome
    215             };
    216     }
    217 }