1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
137
138 conversationManager.getMessager().setConversationException(t);
139 returnValue = errorOutcome;
140 }
141 else
142 {
143
144
145
146
147
148
149
150
151
152
153 returnValue = null;
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 }