KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > webflow > engine > SubflowState


1 /*
2  * Copyright 2002-2006 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.springframework.webflow.engine;
17
18 import org.springframework.core.style.ToStringCreator;
19 import org.springframework.util.Assert;
20 import org.springframework.webflow.core.collection.AttributeMap;
21 import org.springframework.webflow.core.collection.MutableAttributeMap;
22 import org.springframework.webflow.execution.FlowExecutionException;
23 import org.springframework.webflow.execution.RequestContext;
24 import org.springframework.webflow.execution.ViewSelection;
25
26 /**
27  * A transitionable state that spawns a subflow when executed. When the subflow
28  * this state spawns ends, the ending result is used as grounds for a state
29  * transition out of this state.
30  * <p>
31  * A subflow state may be configured to map input data from its flow -- acting
32  * as the parent flow -- down to the subflow when the subflow is spawned. In
33  * addition, output data produced by the subflow may be mapped up to the parent
34  * flow when the subflow ends and the parent flow resumes. See the
35  * {@link FlowAttributeMapper} interface definition for more information on how
36  * to do this. The logic for ending a subflow is located in the {@link EndState}
37  * implementation.
38  *
39  * @see org.springframework.webflow.engine.FlowAttributeMapper
40  * @see org.springframework.webflow.engine.EndState
41  *
42  * @author Keith Donald
43  * @author Erwin Vervaet
44  */

45 public class SubflowState extends TransitionableState {
46
47     /**
48      * The subflow that should be spawned when this subflow state is entered.
49      */

50     private Flow subflow;
51
52     /**
53      * The attribute mapper that should map attributes from the parent flow down
54      * to the spawned subflow and visa versa.
55      */

56     private FlowAttributeMapper attributeMapper;
57
58     /**
59      * Create a new subflow state.
60      * @param flow the owning flow
61      * @param id the state identifier (must be unique to the flow)
62      * @param subflow the subflow to spawn
63      * @throws IllegalArgumentException when this state cannot be added to given
64      * flow, e.g. because the id is not unique
65      * @see #setAttributeMapper(FlowAttributeMapper)
66      */

67     public SubflowState(Flow flow, String JavaDoc id, Flow subflow) throws IllegalArgumentException JavaDoc {
68         super(flow, id);
69         setSubflow(subflow);
70     }
71
72     /**
73      * Returns the subflow spawned by this state.
74      */

75     public Flow getSubflow() {
76         return subflow;
77     }
78
79     /**
80      * Set the subflow that will be spawned by this state.
81      * @param subflow the subflow to spawn
82      */

83     private void setSubflow(Flow subflow) {
84         Assert.notNull(subflow, "A subflow state must have a subflow; the subflow is required");
85         this.subflow = subflow;
86     }
87
88     /**
89      * Returns the attribute mapper used to map data between the parent and child
90      * flow, or null if no mapping is needed.
91      */

92     public FlowAttributeMapper getAttributeMapper() {
93         return attributeMapper;
94     }
95
96     /**
97      * Set the attribute mapper used to map model data between the parent and
98      * child flow. Can be null if no mapping is needed.
99      */

100     public void setAttributeMapper(FlowAttributeMapper attributeMapper) {
101         this.attributeMapper = attributeMapper;
102     }
103
104     /**
105      * Specialization of State's <code>doEnter</code> template method that
106      * executes behaviour specific to this state type in polymorphic fashion.
107      * <p>
108      * Entering this state, creates the subflow input map and spawns the subflow
109      * in the current flow execution.
110      * @param context the control context for the currently executing flow, used
111      * by this state to manipulate the flow execution
112      * @return a view selection containing model and view information needed to
113      * render the results of the state execution
114      * @throws FlowExecutionException if an exception occurs in this state
115      */

116     protected ViewSelection doEnter(RequestControlContext context) throws FlowExecutionException {
117         if (logger.isDebugEnabled()) {
118             logger.debug("Spawning subflow '" + getSubflow().getId() + "' within flow '" + getFlow().getId() + "'");
119         }
120         return context.start(getSubflow(), createSubflowInput(context));
121     }
122
123     /**
124      * Create the input data map for the spawned subflow session. The returned
125      * map will be passed to {@link Flow#start(RequestControlContext, MutableAttributeMap)}.
126      */

127     protected MutableAttributeMap createSubflowInput(RequestContext context) {
128         if (getAttributeMapper() != null) {
129             if (logger.isDebugEnabled()) {
130                 logger.debug("Messaging the configured attribute mapper to map attributes "
131                         + "down to the spawned subflow for access within the subflow");
132             }
133             return getAttributeMapper().createFlowInput(context);
134         }
135         else {
136             if (logger.isDebugEnabled()) {
137                 logger.debug("No attribute mapper configured for this subflow state '" + getId()
138                         + "' -- As a result, no attributes will be passed to the spawned subflow '"
139                         + subflow.getId() + "'");
140             }
141             return null;
142         }
143     }
144
145     /**
146      * Called on completion of the subflow to handle the subflow result event as
147      * determined by the end state reached by the subflow.
148      */

149     public ViewSelection onEvent(RequestControlContext context) {
150         mapSubflowOutput(context.getLastEvent().getAttributes(), context);
151         return super.onEvent(context);
152     }
153
154     /**
155      * Map the output data produced by the subflow back into the request context
156      * (typically flow scope).
157      */

158     private void mapSubflowOutput(AttributeMap subflowOutput, RequestContext context) {
159         if (getAttributeMapper() != null) {
160             if (logger.isDebugEnabled()) {
161                 logger.debug("Messaging the configured attribute mapper to map subflow result attributes to the "
162                         + "resuming parent flow -- It will have access to attributes passed up by the completed subflow");
163             }
164             attributeMapper.mapFlowOutput(subflowOutput, context);
165         }
166         else {
167             if (logger.isDebugEnabled()) {
168                 logger.debug("No attribute mapper is configured for the resuming subflow state '" + getId()
169                         + "' -- As a result, no attributes of the ending flow will be passed to the resuming parent flow");
170             }
171         }
172     }
173
174     protected void appendToString(ToStringCreator creator) {
175         creator.append("subflow", subflow.getId()).append("attributeMapper", attributeMapper);
176         super.appendToString(creator);
177     }
178 }
Popular Tags