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.test.execution;
017:
018: import java.io.File;
019:
020: import org.springframework.core.io.FileSystemResource;
021: import org.springframework.core.io.Resource;
022: import org.springframework.webflow.core.collection.AttributeMap;
023: import org.springframework.webflow.definition.FlowDefinition;
024: import org.springframework.webflow.definition.registry.FlowDefinitionResource;
025: import org.springframework.webflow.engine.Flow;
026: import org.springframework.webflow.engine.builder.FlowAssembler;
027: import org.springframework.webflow.engine.builder.FlowBuilder;
028: import org.springframework.webflow.engine.builder.FlowServiceLocator;
029: import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
030: import org.springframework.webflow.execution.FlowExecutionListener;
031: import org.springframework.webflow.execution.factory.StaticFlowExecutionListenerLoader;
032: import org.springframework.webflow.test.MockFlowServiceLocator;
033:
034: /**
035: * Base class for flow integration tests that verify an externalized flow definition executes as expected. Supports
036: * caching of the flow definition built from an externalized resource to speed up test execution.
037: *
038: * @author Keith Donald
039: */
040: public abstract class AbstractExternalizedFlowExecutionTests extends
041: AbstractFlowExecutionTests {
042:
043: /**
044: * The cached flow definition.
045: */
046: private static FlowDefinition cachedFlowDefinition;
047:
048: /**
049: * The flag indicating if the flow definition built from an externalized resource as part of this test should be
050: * cached.
051: */
052: private boolean cacheFlowDefinition = false;
053:
054: /**
055: * Constructs a default externalized flow execution test.
056: * @see #setName(String)
057: */
058: public AbstractExternalizedFlowExecutionTests() {
059: super ();
060: }
061:
062: /**
063: * Constructs an externalized flow execution test with given name.
064: * @param name the name of the test
065: * @since 1.0.2
066: */
067: public AbstractExternalizedFlowExecutionTests(String name) {
068: super (name);
069: }
070:
071: /**
072: * Internal helper that returns the flow execution factory used by the test cast to a
073: * {@link FlowExecutionImplFactory}.
074: */
075: private FlowExecutionImplFactory getFlowExecutionImplFactory() {
076: return (FlowExecutionImplFactory) getFlowExecutionFactory();
077: }
078:
079: /**
080: * Returns if flow definition caching is turned on.
081: */
082: protected boolean isCacheFlowDefinition() {
083: return cacheFlowDefinition;
084: }
085:
086: /**
087: * Sets the flag indicating if the flow definition built from an externalized resource as part of this test should
088: * be cached. Default is false.
089: */
090: protected void setCacheFlowDefinition(boolean cacheFlowDefinition) {
091: this .cacheFlowDefinition = cacheFlowDefinition;
092: }
093:
094: /**
095: * Sets system attributes to be associated with the flow execution the next time one is {@link #startFlow() started}
096: * by this test. Useful for assigning attributes that influence flow execution behavior.
097: * @param executionAttributes the system attributes to assign
098: */
099: protected void setFlowExecutionAttributes(
100: AttributeMap executionAttributes) {
101: getFlowExecutionImplFactory().setExecutionAttributes(
102: executionAttributes);
103: }
104:
105: /**
106: * Set a single listener to be attached to the flow execution the next time one is {@link #startFlow() started} by
107: * this test. Useful for attaching a listener that does test assertions during the execution of the flow.
108: * @param executionListener the listener to attach
109: */
110: protected void setFlowExecutionListener(
111: FlowExecutionListener executionListener) {
112: getFlowExecutionImplFactory()
113: .setExecutionListenerLoader(
114: new StaticFlowExecutionListenerLoader(
115: executionListener));
116: }
117:
118: /**
119: * Set the listeners to be attached to the flow execution the next time one is {@link #startFlow() started} by this
120: * test. Useful for attaching listeners that do test assertions during the execution of the flow.
121: * @param executionListeners the listeners to attach
122: * @since 1.0.4
123: */
124: protected void setFlowExecutionListeners(
125: FlowExecutionListener[] executionListeners) {
126: getFlowExecutionImplFactory().setExecutionListenerLoader(
127: new StaticFlowExecutionListenerLoader(
128: executionListeners));
129: }
130:
131: /**
132: * Returns the flow definition being tested.
133: */
134: protected final FlowDefinition getFlowDefinition() {
135: if (isCacheFlowDefinition() && cachedFlowDefinition != null) {
136: return cachedFlowDefinition;
137: }
138: FlowServiceLocator flowServiceLocator = createFlowServiceLocator();
139: Flow flow = createFlow(getFlowDefinitionResource(),
140: flowServiceLocator);
141: if (isCacheFlowDefinition()) {
142: cachedFlowDefinition = flow;
143: }
144: return flow;
145: }
146:
147: /**
148: * Returns the flow service locator to use during flow definition construction time for accessing externally
149: * managed flow artifacts such as actions and flows to be used as subflows.
150: * <p>
151: * This implementation just creates a {@link MockFlowServiceLocator} and populates it with services by calling
152: * {@link #registerMockServices(MockFlowServiceLocator)}.
153: * @return the flow artifact factory
154: */
155: protected FlowServiceLocator createFlowServiceLocator() {
156: MockFlowServiceLocator serviceLocator = new MockFlowServiceLocator();
157: registerMockServices(serviceLocator);
158: return serviceLocator;
159: }
160:
161: /**
162: * Template method called by {@link #createFlowServiceLocator()} to allow registration of mock implementations of
163: * services needed to test the flow execution. Useful when testing flow definitions in execution in isolation from
164: * flows and middle-tier services. Subclasses may override.
165: * @param serviceRegistry the mock service registry (and locator)
166: */
167: protected void registerMockServices(
168: MockFlowServiceLocator serviceRegistry) {
169: }
170:
171: /**
172: * Factory method to assemble a flow definition from a resource. Called by {@link #getFlowDefinition()} to
173: * create the "main" flow to test. May also be called by subclasses to create subflow definitions whose executions
174: * should also be exercised by this test.
175: * @param resource the flow definition resource
176: * @return the built flow definition, ready for execution
177: * @see #createFlowBuilder(Resource, FlowServiceLocator)
178: */
179: protected final Flow createFlow(FlowDefinitionResource resource,
180: FlowServiceLocator serviceLocator) {
181: FlowBuilder builder = createFlowBuilder(resource.getLocation(),
182: serviceLocator);
183: FlowAssembler assembler = new FlowAssembler(resource.getId(),
184: resource.getAttributes(), builder);
185: return assembler.assembleFlow();
186: }
187:
188: /**
189: * Returns the pointer to the resource that houses the definition of the flow to be tested. Subclasses must
190: * implement.
191: * <p>
192: * Example usage:
193: * <pre class="code">
194: * protected FlowDefinitionResource getFlowDefinitionResource() {
195: * return createFlowDefinitionResource("/WEB-INF/flows/order-flow.xml");
196: * }
197: * </pre>
198: * @return the flow definition resource
199: */
200: protected abstract FlowDefinitionResource getFlowDefinitionResource();
201:
202: /**
203: * Factory method to create the builder that will build the flow definition whose execution will be tested.
204: * Subclasses must implement.
205: * <p>
206: * A subclass may return a builder that sets up mock implementations of services needed locally by the flow
207: * definition at runtime.
208: * @param resource the externalized flow definition resource location
209: * @param serviceLocator the flow service locator
210: * @return the flow builder that will build the flow to be tested
211: */
212: protected abstract FlowBuilder createFlowBuilder(Resource resource,
213: FlowServiceLocator serviceLocator);
214:
215: /**
216: * Convenient factory method that creates a {@link FlowDefinitionResource} from a file path. Typically called by
217: * subclasses overriding {@link #getFlowDefinitionResource()}.
218: * @param filePath the full path to the externalized flow definition file
219: * @return the flow definition resource
220: */
221: protected final FlowDefinitionResource createFlowDefinitionResource(
222: String filePath) {
223: return createFlowDefinitionResource(new File(filePath));
224: }
225:
226: /**
227: * Convenient factory method that creates a {@link FlowDefinitionResource} from a file in a directory. Typically
228: * called by subclasses overriding {@link #getFlowDefinitionResource()}.
229: * @param fileDirectory the directory containing the file
230: * @param fileName the short file name
231: * @return the flow definition resource pointing to the file
232: */
233: protected final FlowDefinitionResource createFlowDefinitionResource(
234: String fileDirectory, String fileName) {
235: return createFlowDefinitionResource(new File(fileDirectory,
236: fileName));
237: }
238:
239: /**
240: * Convenient factory method that creates a {@link FlowDefinitionResource} from a file. Typically
241: * called by subclasses overriding {@link #getFlowDefinitionResource()}.
242: * @param file the file
243: * @return the flow definition resource
244: */
245: protected FlowDefinitionResource createFlowDefinitionResource(
246: File file) {
247: return new FlowDefinitionResource(new FileSystemResource(file));
248: }
249: }
|