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 package org.apache.myfaces.orchestra.flow.config;
20
21 import java.io.Serializable;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import javax.faces.context.FacesContext;
28
29 import org.apache.myfaces.orchestra.lib.OrchestraException;
30
31 /**
32 * Contains static metadata information about a possible call to a flow.
33 */
34 public class FlowCall implements Serializable
35 {
36 // For serialization. IMPORTANT; update this when changing the
37 // binary format of this class (eg adding fields).
38 private static final long serialVersionUID = 1L;
39
40 private String outcome;
41 private String viewId;
42 private String service;
43
44 private List<FlowParamSend> params = new ArrayList<FlowParamSend>(4);
45 private List<FlowReturnAccept> returns = new ArrayList<FlowReturnAccept>(4);
46
47 private FlowOnCommit onCommit;
48
49 /**
50 * Constructor.
51 */
52 public FlowCall()
53 {
54 }
55
56 /**
57 * Check that all the properties of this object have valid values, ie
58 * whether the configuration specified by the user is valid.
59 * <p>
60 * Throws an OrchestraException if a mandatory parameter is missing.
61 * <p>
62 * The outcome property is mandatory; it is always necessary to specify
63 * what navigation outcome will trigger this flow call.
64 * <p>
65 * The service property is mandatory; it is always necessary to specify
66 * what logical service (function) this callable flow implements.
67 * <p>
68 * There must be at least one input parameter passed to the called flow.
69 * There are so few sensible flows that take no input parameters at all
70 * that is is just not supported; it almost certainly means the user has
71 * made a configuration error.
72 * <p>
73 * There must be at least one output parameter from this flow. A
74 * sensible flow that returns no values to its caller is so rare that
75 * this is just not supported; it almost certainly means the user has
76 * made a configuration error.
77 */
78 public void validate()
79 {
80 if (outcome == null)
81 {
82 throw new OrchestraException("outcome is null");
83 }
84
85 // viewId is optional
86
87 if (service == null)
88 {
89 throw new OrchestraException("service is null");
90 }
91
92 if (params.isEmpty())
93 {
94 throw new OrchestraException("params is empty");
95 }
96
97 if (returns.isEmpty())
98 {
99 throw new OrchestraException("returns is empty");
100 }
101
102 for(FlowParamSend p : params)
103 {
104 p.validate();
105 }
106
107 for(FlowReturnAccept p : returns)
108 {
109 p.validate();
110 }
111 }
112
113 /**
114 * Process all the configured "input param" expressions, fetching the data
115 * from the calling environment into a temporary map.
116 * <p>
117 * The data in this map is then expected to be imported into beans belonging
118 * to the called flow
119 */
120 public Map<String, Object> readSendParams(FacesContext facesContext)
121 {
122 Map<String, Object> data = new HashMap<String, Object>();
123 for(FlowParamSend param : params)
124 {
125 String paramName = param.getName();
126 Object paramValue = param.getSrcValue(facesContext);
127 data.put(paramName, paramValue);
128 }
129 return data;
130 }
131
132 /**
133 * Process all the configured "return" expressions, fetching the data
134 * from a map and writing it into beans associated with the calling page.
135 * <p>
136 * The map is expected to have been populated by executing the
137 * "return param" expressions of the called flow.
138 */
139 public void writeAcceptParams(FacesContext facesContext, Map<String, Object> data)
140 {
141 for(FlowReturnAccept param : returns)
142 {
143 String paramName = param.getName();
144 Object newValue = data.get(paramName);
145 param.setDstValue(facesContext, newValue);
146 }
147 }
148
149 /**
150 * Return the JSF navigation outcome which will trigger this flowCall.
151 */
152 public String getOutcome()
153 {
154 return outcome;
155 }
156
157 /** For use only during object initialization. */
158 public void setOutcome(String outcome)
159 {
160 this.outcome = outcome;
161 }
162
163 /**
164 * Return the viewId to navigate to when this flowCall is triggered.
165 * <p>
166 * Optional; when null then the normal JSF navigation rules apply.
167 * <p>
168 * This can be useful when the JSF command-component that triggers the call is a view "fragment" that
169 * is included into many pages, eg via jsp:include or facelets templating. In this case, specifying
170 * a separate navigation-case for each view that includes the fragment is unreasonable. One option
171 * is to specify the navigation case as a global rule, but it also can be reasonable to hard-wire it
172 * in the "included" page.
173 * <p>
174 * For other cases
175 */
176 public String getViewId()
177 {
178 return viewId;
179 }
180
181 /** For use only during object initialization. */
182 public void setViewId(String viewId)
183 {
184 this.viewId = viewId;
185 }
186
187 /**
188 * Return the logical function that this flow implements.
189 * <p>
190 * This is a free-form text string, but it is recommended that the contents
191 * look like a fully-qualified java interface name. A called flow is really
192 * like invoking a service implementation object via a "service interface"
193 * that has only one method.
194 */
195 public String getService()
196 {
197 return service;
198 }
199
200 /** For use only during object initialization. */
201 public void setService(String service)
202 {
203 this.service = service;
204 }
205
206 /**
207 * Return list of FlowParamSend objects which defines what values the flow
208 * caller passes to the called flow when this FlowCall is triggered.
209 */
210 public List<FlowParamSend> getParams()
211 {
212 return params;
213 }
214
215 /** For use only during object initialization. */
216 public void addParam(FlowParamSend param)
217 {
218 this.params.add(param);
219 }
220
221 /**
222 * Return list of FlowReturnAccept objects which defines what values the called
223 * flow is expected to return on commit, and where they should be stored
224 * within the caller's environment.
225 */
226 public List<FlowReturnAccept> getReturns()
227 {
228 return returns;
229 }
230
231 /** For use only during object initialization. */
232 public void addReturn(FlowReturnAccept returnParam)
233 {
234 this.returns.add(returnParam);
235 }
236
237 /**
238 * Return an object that wraps an EL expression which should be executed
239 * within the caller environment on successful commit of a called flow.
240 * <p>
241 * This gives the caller a chance to process the returned values before
242 * it is re-rendered.
243 */
244 public FlowOnCommit getOnCommit()
245 {
246 return onCommit;
247 }
248
249 /** For use only during object initialization. */
250 public void setOnCommit(FlowOnCommit onCommit)
251 {
252 this.onCommit = onCommit;
253 }
254
255 /**
256 * Custom string format to improve log messages.
257 */
258 public String toString()
259 {
260 return "flowCall: outcome=" + outcome + ",service=" + service;
261 }
262 }