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 }