001: /*
002: * Copyright 2004-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.springframework.webflow.engine.impl;
017:
018: import org.apache.commons.logging.Log;
019: import org.apache.commons.logging.LogFactory;
020: import org.springframework.core.style.ToStringCreator;
021: import org.springframework.util.Assert;
022: import org.springframework.webflow.context.ExternalContext;
023: import org.springframework.webflow.core.collection.AttributeMap;
024: import org.springframework.webflow.core.collection.CollectionUtils;
025: import org.springframework.webflow.core.collection.LocalAttributeMap;
026: import org.springframework.webflow.core.collection.MutableAttributeMap;
027: import org.springframework.webflow.core.collection.ParameterMap;
028: import org.springframework.webflow.definition.FlowDefinition;
029: import org.springframework.webflow.definition.StateDefinition;
030: import org.springframework.webflow.definition.TransitionDefinition;
031: import org.springframework.webflow.engine.Flow;
032: import org.springframework.webflow.engine.RequestControlContext;
033: import org.springframework.webflow.engine.State;
034: import org.springframework.webflow.engine.Transition;
035: import org.springframework.webflow.execution.Event;
036: import org.springframework.webflow.execution.FlowExecutionContext;
037: import org.springframework.webflow.execution.FlowExecutionException;
038: import org.springframework.webflow.execution.FlowSession;
039: import org.springframework.webflow.execution.FlowSessionStatus;
040: import org.springframework.webflow.execution.ViewSelection;
041:
042: /**
043: * Default request control context implementation used internally by the web
044: * flow system. This class is closely coupled with
045: * <code>FlowExecutionImpl</code> and <code>FlowSessionImpl</code>. The
046: * three classes work together to form a complete flow execution implementation
047: * based on a finite state machine.
048: *
049: * @see FlowExecutionImpl
050: * @see FlowSessionImpl
051: *
052: * @author Keith Donald
053: * @author Erwin Vervaet
054: */
055: class RequestControlContextImpl implements RequestControlContext {
056:
057: private static final Log logger = LogFactory
058: .getLog(RequestControlContextImpl.class);
059:
060: /**
061: * The owning flow execution.
062: */
063: private FlowExecutionImpl flowExecution;
064:
065: /**
066: * The request scope data map.
067: */
068: private LocalAttributeMap requestScope = new LocalAttributeMap();
069:
070: /**
071: * A source context for the caller who initiated this request.
072: */
073: private ExternalContext externalContext;
074:
075: /**
076: * The last event that occured in this request context.
077: */
078: private Event lastEvent;
079:
080: /**
081: * The last transition that executed in this request context.
082: */
083: private Transition lastTransition;
084:
085: /**
086: * Holder for contextual execution properties.
087: */
088: private AttributeMap attributes;
089:
090: /**
091: * Create a new request context.
092: * @param flowExecution the owning flow execution
093: * @param externalContext the external context that originated the flow
094: * execution request
095: */
096: public RequestControlContextImpl(FlowExecutionImpl flowExecution,
097: ExternalContext externalContext) {
098: Assert.notNull(flowExecution,
099: "The owning flow execution is required");
100: this .externalContext = externalContext;
101: this .flowExecution = flowExecution;
102: }
103:
104: // implementing RequestContext
105:
106: public FlowDefinition getActiveFlow() {
107: return flowExecution.getActiveSession().getDefinition();
108: }
109:
110: public StateDefinition getCurrentState() {
111: return flowExecution.getActiveSession().getState();
112: }
113:
114: public MutableAttributeMap getRequestScope() {
115: return requestScope;
116: }
117:
118: public MutableAttributeMap getFlashScope() {
119: return flowExecution.getActiveSession().getFlashMap();
120: }
121:
122: public MutableAttributeMap getFlowScope() {
123: return flowExecution.getActiveSession().getScope();
124: }
125:
126: public MutableAttributeMap getConversationScope() {
127: return flowExecution.getConversationScope();
128: }
129:
130: public ParameterMap getRequestParameters() {
131: return externalContext.getRequestParameterMap();
132: }
133:
134: public ExternalContext getExternalContext() {
135: return externalContext;
136: }
137:
138: public FlowExecutionContext getFlowExecutionContext() {
139: return flowExecution;
140: }
141:
142: public Event getLastEvent() {
143: return lastEvent;
144: }
145:
146: public TransitionDefinition getLastTransition() {
147: return lastTransition;
148: }
149:
150: public AttributeMap getAttributes() {
151: return attributes;
152: }
153:
154: public void setAttributes(AttributeMap attributes) {
155: if (attributes == null) {
156: this .attributes = CollectionUtils.EMPTY_ATTRIBUTE_MAP;
157: } else {
158: this .attributes = attributes;
159: }
160: }
161:
162: public AttributeMap getModel() {
163: return getConversationScope().union(getFlowScope()).union(
164: getFlashScope()).union(getRequestScope());
165: }
166:
167: // implementing RequestControlContext
168:
169: public void setLastEvent(Event lastEvent) {
170: this .lastEvent = lastEvent;
171: }
172:
173: public void setLastTransition(Transition lastTransition) {
174: this .lastTransition = lastTransition;
175: }
176:
177: public void setCurrentState(State state) {
178: getExecutionListeners().fireStateEntering(this , state);
179: State previousState = getCurrentStateInternal();
180: flowExecution.setCurrentState(state);
181: if (previousState == null) {
182: getActiveSession().setStatus(FlowSessionStatus.ACTIVE);
183: }
184: getExecutionListeners().fireStateEntered(this , previousState);
185: }
186:
187: public ViewSelection start(Flow flow, MutableAttributeMap input)
188: throws FlowExecutionException {
189: if (input == null) {
190: // create a mutable map so entries can be added by listeners!
191: input = new LocalAttributeMap();
192: }
193: if (logger.isDebugEnabled()) {
194: logger.debug("Activating new session for flow '"
195: + flow.getId() + "' in state '"
196: + flow.getStartState().getId() + "' with input "
197: + input);
198: }
199: getExecutionListeners().fireSessionStarting(this , flow, input);
200: FlowSession session = flowExecution.activateSession(flow);
201: getExecutionListeners().fireSessionCreated(this , session);
202: ViewSelection selectedView = flow.start(this , input);
203: getExecutionListeners().fireSessionStarted(this , session);
204: return selectedView;
205: }
206:
207: public ViewSelection signalEvent(Event event)
208: throws FlowExecutionException {
209: if (logger.isDebugEnabled()) {
210: logger.debug("Signaling event '" + event.getId()
211: + "' in state '" + getCurrentState().getId()
212: + "' of flow '" + getActiveFlow().getId() + "'");
213: }
214: setLastEvent(event);
215: getExecutionListeners().fireEventSignaled(this , event);
216: ViewSelection selectedView = getActiveFlowInternal().onEvent(
217: this );
218: return selectedView;
219: }
220:
221: public FlowSession endActiveFlowSession(MutableAttributeMap output)
222: throws IllegalStateException {
223: FlowSession session = getFlowExecutionContext()
224: .getActiveSession();
225: getExecutionListeners()
226: .fireSessionEnding(this , session, output);
227: getActiveFlowInternal().end(this , output);
228: if (logger.isDebugEnabled()) {
229: logger.debug("Ending active session " + session
230: + "; exposed session output is " + output);
231: }
232: session = flowExecution.endActiveFlowSession();
233: getExecutionListeners().fireSessionEnded(this , session, output);
234: return session;
235: }
236:
237: public ViewSelection execute(Transition transition) {
238: return transition.execute(getCurrentStateInternal(), this );
239: }
240:
241: // internal helpers
242:
243: /**
244: * Returns the execution listerns for the flow execution of this request
245: * context.
246: */
247: protected FlowExecutionListeners getExecutionListeners() {
248: return flowExecution.getListeners();
249: }
250:
251: /**
252: * Returns the active flow in the flow execution of this request context.
253: */
254: protected Flow getActiveFlowInternal() {
255: return (Flow) getActiveSession().getDefinition();
256: }
257:
258: /**
259: * Returns the current state in the flow execution of this request context.
260: */
261: protected State getCurrentStateInternal() {
262: return (State) getActiveSession().getState();
263: }
264:
265: /**
266: * Returns the active flow session in the flow execution of this request
267: * context.
268: */
269: protected FlowSessionImpl getActiveSession() {
270: return flowExecution.getActiveSessionInternal();
271: }
272:
273: public String toString() {
274: return new ToStringCreator(this ).append("externalContext",
275: externalContext).append("requestScope", requestScope)
276: .append("attributes", attributes).append(
277: "flowExecution", flowExecution).toString();
278: }
279: }
|