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.components;
    21
    22  import java.io.IOException;
    23  import java.util.Arrays;
    24  import java.util.Collection;
    25
    26  import javax.faces.component.UICommand;
    27  import javax.faces.context.FacesContext;
    28  import javax.faces.el.MethodBinding;
    29  import javax.faces.el.ValueBinding;
    30
    31  import org.apache.myfaces.orchestra.conversation.ConversationUtils;
    32  import org.apache.myfaces.orchestra.conversation.jsf._JsfConversationUtils;
    33  import org.apache.myfaces.orchestra.conversation.jsf.lib._EndConversationMethodBindingFacade;
    34  import org.apache.myfaces.shared_orchestra.util.StringUtils;
    35
    36  /**
    37   * Can be used to end a manual-scope conversation, and optionally handles exceptions thrown
    38   * by action methods.
    39   * <p>
    40   * When nested within a UICommand component (eg a commandLink or commandButton) the specified
    41   * conversation will be ended after the method invoked by the parent component is executed.
    42   * <pre>
    43   * &lt;h:commandLink action="#{backing.saveAction}"&gt;
    44   *     &lt;orchestra:endConversation name="conversation1" onOutcome="success" /&gt;
    45   * &lt;/h:commandLink&gt;
    46   * </pre>
    47   * <p>
    48   * The "name" attribute is mandatory, and specifies which conversation is to be ended.
    49   * The optional attributes are:
    50   * <ul>
    51   * <li>onOutcome</li>
    52   * <li>errorOutcome</li>
    53   * </ul>
    54   *
    55   * <h2>onOutcome</h2>
    56   *
    57   * This is a string or comma-separated list of strings. After invoking the action
    58   * associated with the nearest ancestor UICommand component, the following rules
    59   * are executed:
    60   * <ul>
    61   *   <li>If there is no ancestor UICommand component then end the conversation, else</li>
    62   *   <li>If the action returned null, then do not end the conversation, else</li>
    63   *   <li>If the onOutcomes list is null or empty then end the conversation, else</li>
    64   *   <li>If the returned value is in the onOutcomes list then end the conversation, else</li>
    65   *   <li>do not end the conversation.</li>
    66   * </ul>
    67   *
    68   * Note in particular that when this component has no enclosing UICommand component, then
    69   * the specified conversation is always terminated. This is often useful on the "confirmation"
    70   * page of a wizard-style page sequence.
    71   *
    72   * <h2>errorOutcome</h2>
    73   *
    74   * In case of an exception being thrown by the action method, use the given outcome as the
    75   * new outcome so normal navigation to a specified page can occur instead of showing the
    76   * default errorPage. This value is checked against the onOutcome list to determine whether
    77   * the specified conversation should be terminated when an exception occurs. If an exception
    78   * occurs, but no errorOutcome is specified then the conversation is never terminated.
    79   */
    80  public class UIEndConversation extends AbstractConversationComponent
    81  {
    82      public static final String COMPONENT_TYPE = "org.apache.myfaces.orchestra.EndConversation";
    83
    84      private String onOutcome;
    85      private String errorOutcome;
    86
    87      private boolean inited = false;
    88
    89      public void encodeBegin(FacesContext context) throws IOException
    90      {
    91          super.encodeBegin(context);
    92
    93          UICommand command = _JsfConversationUtils.findParentCommand(this);
    94          if (command != null)
    95          {
    96              // This component has a UICommand ancestor. Replace its "action" MethodBinding
    97              // with a proxy.
    98              if (!inited)
    99              {
    100                 MethodBinding original = command.getAction();
    101                 command.setAction(new _EndConversationMethodBindingFacade(
    102                     getName(),
    103                     getOnOutcomes(),
    104                     original,
    105                     getErrorOutcome()));
    106                 inited = true;
    107             }
    108         }
    109         else
    110         {
    111             // This component has no UICommand ancestor. Always end the conversation.
    112             ConversationUtils.invalidateIfExists(getName());
    113         }
    114     }
    115
    116     private Collection getOnOutcomes()
    117     {
    118         String onOutcome = getOnOutcome();
    119         if (onOutcome == null || onOutcome.trim().length() < 1)
    120         {
    121             return null;
    122         }
    123
    124         return Arrays.asList(StringUtils.trim(StringUtils.splitShortString(onOutcome, ',')));
    125     }
    126
    127     public void restoreState(FacesContext context, Object state)
    128     {
    129         Object[] states = (Object[]) state;
    130         super.restoreState(context, states[0]);
    131         inited = ((Boolean) states[1]).booleanValue();
    132         onOutcome = (String) states[2];
    133         errorOutcome = (String) states[3];
    134     }
    135
    136     public Object saveState(FacesContext context)
    137     {
    138         return new Object[]
    139             {
    140                 super.saveState(context),
    141                 inited ? Boolean.TRUE : Boolean.FALSE,
    142                 onOutcome,
    143                 errorOutcome
    144             };
    145     }
    146
    147     public String getOnOutcome()
    148     {
    149         if (onOutcome != null)
    150         {
    151             return onOutcome;
    152         }
    153         ValueBinding vb = getValueBinding("onOutcome");
    154         if (vb == null)
    155         {
    156             return null;
    157         }
    158         return (String) vb.getValue(getFacesContext());
    159     }
    160
    161     public void setOnOutcome(String onOutcome)
    162     {
    163         this.onOutcome = onOutcome;
    164     }
    165
    166     public String getErrorOutcome()
    167     {
    168         if (errorOutcome != null)
    169         {
    170             return errorOutcome;
    171         }
    172         ValueBinding vb = getValueBinding("errorOutcome");
    173         if (vb == null)
    174         {
    175             return null;
    176         }
    177         return (String) vb.getValue(getFacesContext());
    178     }
    179
    180     public void setErrorOutcome(String errorOutcome)
    181     {
    182         this.errorOutcome = errorOutcome;
    183     }
    184 }