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  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 }