0001: /*
0002: * Copyright 2004-2007 the original author or authors.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: package org.springframework.webflow.engine.builder;
0017:
0018: import org.springframework.binding.expression.Expression;
0019: import org.springframework.binding.expression.SettableExpression;
0020: import org.springframework.binding.mapping.AttributeMapper;
0021: import org.springframework.binding.mapping.Mapping;
0022: import org.springframework.binding.mapping.MappingBuilder;
0023: import org.springframework.binding.method.MethodSignature;
0024: import org.springframework.core.style.ToStringCreator;
0025: import org.springframework.webflow.action.AbstractBeanInvokingAction;
0026: import org.springframework.webflow.action.ActionResultExposer;
0027: import org.springframework.webflow.action.BeanInvokingActionFactory;
0028: import org.springframework.webflow.action.EvaluateAction;
0029: import org.springframework.webflow.action.MultiAction;
0030: import org.springframework.webflow.core.collection.AttributeMap;
0031: import org.springframework.webflow.core.collection.CollectionUtils;
0032: import org.springframework.webflow.engine.AnnotatedAction;
0033: import org.springframework.webflow.engine.Flow;
0034: import org.springframework.webflow.engine.FlowAttributeMapper;
0035: import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
0036: import org.springframework.webflow.engine.State;
0037: import org.springframework.webflow.engine.TargetStateResolver;
0038: import org.springframework.webflow.engine.Transition;
0039: import org.springframework.webflow.engine.TransitionCriteria;
0040: import org.springframework.webflow.engine.ViewSelector;
0041: import org.springframework.webflow.engine.support.ActionTransitionCriteria;
0042: import org.springframework.webflow.execution.Action;
0043: import org.springframework.webflow.execution.ScopeType;
0044: import org.springframework.webflow.execution.support.EventFactorySupport;
0045:
0046: /**
0047: * Base class for flow builders that programmatically build flows in Java
0048: * configuration code.
0049: * <p>
0050: * To give you an example of what a simple Java-based web flow builder
0051: * definition might look like, the following example defines the 'dynamic' web
0052: * flow roughly equivalent to the work flow statically implemented in Spring
0053: * MVC's simple form controller:
0054: *
0055: * <pre class="code">
0056: * public class CustomerDetailFlowBuilder extends AbstractFlowBuilder {
0057: * public void buildStates() {
0058: * // get customer information
0059: * addActionState("getDetails", action("customerAction"), transition(on(success()), to("displayDetails")));
0060: *
0061: * // view customer information
0062: * addViewState("displayDetails", "customerDetails", transition(on(submit()), to("bindAndValidate")));
0063: *
0064: * // bind and validate customer information updates
0065: * addActionState("bindAndValidate", action("customerAction"), new Transition[] {
0066: * transition(on(error()), to("displayDetails")), transition(on(success()), to("finish")) });
0067: *
0068: * // finish
0069: * addEndState("finish");
0070: * }
0071: * }
0072: * </pre>
0073: *
0074: * What this Java-based FlowBuilder implementation does is add four states to a
0075: * flow. These include a "get" <code>ActionState</code> (the start state), a
0076: * <code>ViewState</code> state, a "bind and validate"
0077: * <code>ActionState</code>, and an end marker state (<code>EndState</code>).
0078: * <p>
0079: * The first state, an action state, will be assigned the indentifier
0080: * <code>getDetails</code>. This action state will automatically be
0081: * configured with the following defaults:
0082: * <ol>
0083: * <li>The action instance with id <code>customerAction</code>. This is the
0084: * <code>Action</code> implementation that will execute when this state is
0085: * entered. In this example, that <code>Action</code> will go out to the DB,
0086: * load the Customer, and put it in the Flow's request context.
0087: * <li>A <code>success</code> transition to a default view state, called
0088: * <code>displayDetails</code>. This means when the <code>Action</code>
0089: * returns a <code>success</code> result event (aka outcome), the
0090: * <code>displayDetails</code> state will be entered.
0091: * <li>It will act as the start state for this flow (by default, the first
0092: * state added to a flow during the build process is treated as the start
0093: * state).
0094: * </ol>
0095: * <p>
0096: * The second state, a view state, will be identified as
0097: * <code>displayDetails</code>. This view state will automatically be
0098: * configured with the following defaults:
0099: * <ol>
0100: * <li>A view name called <code>customerDetails</code>. This is the logical
0101: * name of a view resource. This logical view name gets mapped to a physical
0102: * view resource (jsp, etc.) by the calling front controller (via a Spring view
0103: * resolver, or a Struts action forward, for example).
0104: * <li>A <code>submit</code> transition to a bind and validate action state,
0105: * indentified by the default id <code>bindAndValidate</code>. This means
0106: * when a <code>submit</code> event is signaled by the view (for example, on a
0107: * submit button click), the bindAndValidate action state will be entered and
0108: * the <code>bindAndValidate</code> method of the
0109: * <code>customerAction</code> <code>Action</code> implementation will be
0110: * executed.
0111: * </ol>
0112: * <p>
0113: * The third state, an action state, will be indentified as
0114: * <code>bindAndValidate</code>. This action state will automatically be
0115: * configured with the following defaults:
0116: * <ol>
0117: * <li>An action bean named <code>customerAction</code> -- this is the name
0118: * of the <code>Action</code> implementation exported in the application
0119: * context that will execute when this state is entered. In this example, the
0120: * <code>Action</code> has a "bindAndValidate" method that will bind form
0121: * input in the HTTP request to a backing Customer form object, validate it, and
0122: * update the DB.
0123: * <li>A <code>success</code> transition to a default end state, called
0124: * <code>finish</code>. This means if the <code>Action</code> returns a
0125: * <code>success</code> result, the <code>finish</code> end state will be
0126: * transitioned to and the flow will terminate.
0127: * <li>An <code>error</code> transition back to the form view. This means if
0128: * the <code>Action</code> returns an <code>error</code> event, the <code>
0129: * displayDetails</code> view state will be transitioned back to.
0130: * </ol>
0131: * <p>
0132: * The fourth and last state, an end state, will be indentified with the default
0133: * end state id <code>finish</code>. This end state is a marker that signals
0134: * the end of the flow. When entered, the flow session terminates, and if this
0135: * flow is acting as a root flow in the current flow execution, any
0136: * flow-allocated resources will be cleaned up. An end state can optionally be
0137: * configured with a logical view name to forward to when entered. It will also
0138: * trigger a state transition in a resuming parent flow if this flow was
0139: * participating as a spawned 'subflow' within a suspended parent flow.
0140: *
0141: * @author Keith Donald
0142: * @author Erwin Vervaet
0143: */
0144: public abstract class AbstractFlowBuilder extends BaseFlowBuilder {
0145:
0146: /**
0147: * A helper for creating commonly used event identifiers that drive
0148: * transitions created by this builder.
0149: */
0150: private EventFactorySupport eventFactorySupport = new EventFactorySupport();
0151:
0152: /**
0153: * Default constructor for subclassing.
0154: */
0155: protected AbstractFlowBuilder() {
0156: super ();
0157: }
0158:
0159: /**
0160: * Create an instance of an abstract flow builder, using the specified
0161: * locator to obtain needed flow services at build time.
0162: * @param flowServiceLocator the locator for services needed by this builder
0163: * to build its Flow
0164: */
0165: protected AbstractFlowBuilder(FlowServiceLocator flowServiceLocator) {
0166: super (flowServiceLocator);
0167: }
0168:
0169: /**
0170: * Returns the configured event factory support helper for creating commonly
0171: * used event identifiers that drive transitions created by this builder.
0172: */
0173: public EventFactorySupport getEventFactorySupport() {
0174: return eventFactorySupport;
0175: }
0176:
0177: /**
0178: * Sets the event factory support helper to use to create commonly used
0179: * event identifiers that drive transitions created by this builder.
0180: */
0181: public void setEventFactorySupport(
0182: EventFactorySupport eventFactorySupport) {
0183: this .eventFactorySupport = eventFactorySupport;
0184: }
0185:
0186: public void init(String flowId, AttributeMap attributes)
0187: throws FlowBuilderException {
0188: setFlow(getFlowArtifactFactory().createFlow(flowId,
0189: flowAttributes().union(attributes)));
0190: initBuilder();
0191: }
0192:
0193: /**
0194: * Hook subclasses may override to provide additional properties for the
0195: * flow built by this builder. Returns a empty collection by default.
0196: * @return additional properties describing the flow being built, should not
0197: * return null
0198: */
0199: protected AttributeMap flowAttributes() {
0200: return CollectionUtils.EMPTY_ATTRIBUTE_MAP;
0201: }
0202:
0203: /**
0204: * Hook method subclasses can override to initialize the flow builder.
0205: * Will be called by {@link #init(String, AttributeMap)} after
0206: * creating the initial Flow object. As a consequence, {@link #getFlow()}
0207: * can be called to retrieve the Flow object under construction.
0208: */
0209: protected void initBuilder() {
0210: }
0211:
0212: // view state
0213:
0214: /**
0215: * Adds a view state to the flow built by this builder.
0216: * @param stateId the state identifier
0217: * @param viewName the string-encoded view selector
0218: * @param transition the sole transition (path) out of this state
0219: * @return the fully constructed view state instance
0220: */
0221: protected State addViewState(String stateId, String viewName,
0222: Transition transition) {
0223: return getFlowArtifactFactory().createViewState(stateId,
0224: getFlow(), null, viewSelector(viewName), null,
0225: new Transition[] { transition }, null, null, null);
0226: }
0227:
0228: /**
0229: * Adds a view state to the flow built by this builder.
0230: * @param stateId the state identifier
0231: * @param viewName the string-encoded view selector
0232: * @param transitions the transitions (paths) out of this state
0233: * @return the fully constructed view state instance
0234: */
0235: protected State addViewState(String stateId, String viewName,
0236: Transition[] transitions) {
0237: return getFlowArtifactFactory().createViewState(stateId,
0238: getFlow(), null, viewSelector(viewName), null,
0239: transitions, null, null, null);
0240: }
0241:
0242: /**
0243: * Adds a view state to the flow built by this builder.
0244: * @param stateId the state identifier
0245: * @param viewName the string-encoded view selector
0246: * @param renderAction the action to execute on state entry and refresh; may
0247: * be null
0248: * @param transition the sole transition (path) out of this state
0249: * @return the fully constructed view state instance
0250: */
0251: protected State addViewState(String stateId, String viewName,
0252: Action renderAction, Transition transition) {
0253: return getFlowArtifactFactory().createViewState(stateId,
0254: getFlow(), null, viewSelector(viewName),
0255: new Action[] { renderAction },
0256: new Transition[] { transition }, null, null, null);
0257: }
0258:
0259: /**
0260: * Adds a view state to the flow built by this builder.
0261: * @param stateId the state identifier
0262: * @param viewName the string-encoded view selector
0263: * @param renderAction the action to execute on state entry and refresh; may
0264: * be null
0265: * @param transitions the transitions (paths) out of this state
0266: * @return the fully constructed view state instance
0267: */
0268: protected State addViewState(String stateId, String viewName,
0269: Action renderAction, Transition[] transitions) {
0270: return getFlowArtifactFactory().createViewState(stateId,
0271: getFlow(), null, viewSelector(viewName),
0272: new Action[] { renderAction }, transitions, null, null,
0273: null);
0274: }
0275:
0276: /**
0277: * Adds a view state to the flow built by this builder.
0278: * @param stateId the state identifier
0279: * @param entryActions the actions to execute when the state is entered
0280: * @param viewSelector the view selector that will make the view selection
0281: * when the state is entered
0282: * @param renderActions any 'render actions' to execute on state entry and
0283: * refresh; may be null
0284: * @param transitions the transitions (path) out of this state
0285: * @param exceptionHandlers any exception handlers to attach to the state
0286: * @param exitActions the actions to execute when the state exits
0287: * @param attributes attributes to assign to the state that may be used to
0288: * affect state construction and execution
0289: * @return the fully constructed view state instance
0290: */
0291: protected State addViewState(String stateId, Action[] entryActions,
0292: ViewSelector viewSelector, Action[] renderActions,
0293: Transition[] transitions,
0294: FlowExecutionExceptionHandler[] exceptionHandlers,
0295: Action[] exitActions, AttributeMap attributes) {
0296: return getFlowArtifactFactory()
0297: .createViewState(stateId, getFlow(), entryActions,
0298: viewSelector, renderActions, transitions,
0299: exceptionHandlers, exitActions, attributes);
0300: }
0301:
0302: // action state
0303:
0304: /**
0305: * Adds an action state to the flow built by this builder.
0306: * @param stateId the state identifier
0307: * @param action the single action to execute when the state is entered
0308: * @param transition the single transition (path) out of this state
0309: * @return the fully constructed action state instance
0310: */
0311: protected State addActionState(String stateId, Action action,
0312: Transition transition) {
0313: return getFlowArtifactFactory().createActionState(stateId,
0314: getFlow(), null, new Action[] { action },
0315: new Transition[] { transition }, null, null, null);
0316: }
0317:
0318: /**
0319: * Adds an action state to the flow built by this builder.
0320: * @param stateId the state identifier
0321: * @param action the single action to execute when the state is entered
0322: * @param transitions the transitions (paths) out of this state
0323: * @return the fully constructed action state instance
0324: */
0325: protected State addActionState(String stateId, Action action,
0326: Transition[] transitions) {
0327: return getFlowArtifactFactory().createActionState(stateId,
0328: getFlow(), null, new Action[] { action }, transitions,
0329: null, null, null);
0330: }
0331:
0332: /**
0333: * Adds an action state to the flow built by this builder.
0334: * @param stateId the state identifier
0335: * @param action the single action to execute when the state is entered
0336: * @param transition the single transition (path) out of this state
0337: * @param exceptionHandler the exception handler to handle exceptions thrown
0338: * by the action
0339: * @return the fully constructed action state instance
0340: */
0341: protected State addActionState(String stateId, Action action,
0342: Transition transition,
0343: FlowExecutionExceptionHandler exceptionHandler) {
0344: return getFlowArtifactFactory()
0345: .createActionState(
0346: stateId,
0347: getFlow(),
0348: null,
0349: new Action[] { action },
0350: new Transition[] { transition },
0351: new FlowExecutionExceptionHandler[] { exceptionHandler },
0352: null, null);
0353: }
0354:
0355: /**
0356: * Adds an action state to the flow built by this builder.
0357: * @param stateId the state identifier
0358: * @param entryActions any generic entry actions to add to the state
0359: * @param actions the actions to execute in a chain when the state is
0360: * entered
0361: * @param transitions the transitions (paths) out of this state
0362: * @param exceptionHandlers the exception handlers to handle exceptions
0363: * thrown by the actions
0364: * @param exitActions the exit actions to execute when the state exits
0365: * @param attributes attributes to assign to the state that may be used to
0366: * affect state construction and execution
0367: * @return the fully constructed action state instance
0368: */
0369: protected State addActionState(String stateId,
0370: Action[] entryActions, Action[] actions,
0371: Transition[] transitions,
0372: FlowExecutionExceptionHandler[] exceptionHandlers,
0373: Action[] exitActions, AttributeMap attributes) {
0374: return getFlowArtifactFactory().createActionState(stateId,
0375: getFlow(), entryActions, actions, transitions,
0376: exceptionHandlers, exitActions, attributes);
0377: }
0378:
0379: // decision state
0380:
0381: /**
0382: * Adds a decision state to the flow built by this builder.
0383: * @param stateId the state identifier
0384: * @param transitions the transitions (paths) out of this state
0385: * @return the fully constructed decision state instance
0386: */
0387: protected State addDecisionState(String stateId,
0388: Transition[] transitions) {
0389: return getFlowArtifactFactory().createDecisionState(stateId,
0390: getFlow(), null, transitions, null, null, null);
0391: }
0392:
0393: /**
0394: * Adds a decision state to the flow built by this builder.
0395: * @param stateId the state identifier
0396: * @param decisionCriteria the criteria that defines the decision
0397: * @param trueStateId the target state on a "true" decision
0398: * @param falseStateId the target state on a "false" decision
0399: * @return the fully constructed decision state instance
0400: */
0401: protected State addDecisionState(String stateId,
0402: TransitionCriteria decisionCriteria, String trueStateId,
0403: String falseStateId) {
0404: Transition thenTransition = getFlowArtifactFactory()
0405: .createTransition(to(trueStateId), decisionCriteria,
0406: null, null);
0407: Transition elseTransition = getFlowArtifactFactory()
0408: .createTransition(to(falseStateId), null, null, null);
0409: return getFlowArtifactFactory().createDecisionState(stateId,
0410: getFlow(), null,
0411: new Transition[] { thenTransition, elseTransition },
0412: null, null, null);
0413: }
0414:
0415: /**
0416: * Adds a decision state to the flow built by this builder.
0417: * @param stateId the state identifier
0418: * @param entryActions the entry actions to execute when the state enters
0419: * @param transitions the transitions (paths) out of this state
0420: * @param exceptionHandlers the exception handlers to handle exceptions
0421: * thrown by the state
0422: * @param exitActions the exit actions to execute when the state exits
0423: * @param attributes attributes to assign to the state that may be used to
0424: * affect state construction and execution
0425: * @return the fully constructed decision state instance
0426: */
0427: protected State addDecisionState(String stateId,
0428: Action[] entryActions, Transition[] transitions,
0429: FlowExecutionExceptionHandler[] exceptionHandlers,
0430: Action[] exitActions, AttributeMap attributes) {
0431: return getFlowArtifactFactory().createDecisionState(stateId,
0432: getFlow(), entryActions, transitions,
0433: exceptionHandlers, exitActions, attributes);
0434: }
0435:
0436: // subflow state
0437:
0438: /**
0439: * Adds a subflow state to the flow built by this builder.
0440: * @param stateId the state identifier
0441: * @param subflow the flow that will act as the subflow
0442: * @param attributeMapper the mapper to map subflow input and output
0443: * attributes
0444: * @param transition the single transition (path) out of the state
0445: * @return the fully constructed subflow state instance
0446: */
0447: protected State addSubflowState(String stateId, Flow subflow,
0448: FlowAttributeMapper attributeMapper, Transition transition) {
0449: return getFlowArtifactFactory().createSubflowState(stateId,
0450: getFlow(), null, subflow, attributeMapper,
0451: new Transition[] { transition }, null, null, null);
0452: }
0453:
0454: /**
0455: * Adds a subflow state to the flow built by this builder.
0456: * @param stateId the state identifier
0457: * @param subflow the flow that will act as the subflow
0458: * @param attributeMapper the mapper to map subflow input and output
0459: * attributes
0460: * @param transitions the transitions (paths) out of the state
0461: * @return the fully constructed subflow state instance
0462: */
0463: protected State addSubflowState(String stateId, Flow subflow,
0464: FlowAttributeMapper attributeMapper,
0465: Transition[] transitions) {
0466: return getFlowArtifactFactory().createSubflowState(stateId,
0467: getFlow(), null, subflow, attributeMapper, transitions,
0468: null, null, null);
0469: }
0470:
0471: /**
0472: * Adds a subflow state to the flow built by this builder.
0473: * @param stateId the state identifier
0474: * @param entryActions the entry actions to execute when the state enters
0475: * @param subflow the flow that will act as the subflow
0476: * @param attributeMapper the mapper to map subflow input and output
0477: * attributes
0478: * @param transitions the transitions (paths) out of this state
0479: * @param exceptionHandlers the exception handlers to handle exceptions
0480: * thrown by the state
0481: * @param exitActions the exit actions to execute when the state exits
0482: * @param attributes attributes to assign to the state that may be used to
0483: * affect state construction and execution
0484: * @return the fully constructed subflow state instance
0485: */
0486: protected State addSubflowState(String stateId,
0487: Action[] entryActions, Flow subflow,
0488: FlowAttributeMapper attributeMapper,
0489: Transition[] transitions,
0490: FlowExecutionExceptionHandler[] exceptionHandlers,
0491: Action[] exitActions, AttributeMap attributes) {
0492: return getFlowArtifactFactory()
0493: .createSubflowState(stateId, getFlow(), entryActions,
0494: subflow, attributeMapper, transitions,
0495: exceptionHandlers, exitActions, attributes);
0496: }
0497:
0498: // end state
0499:
0500: /**
0501: * Adds an end state to the flow built by this builder.
0502: * @param stateId the state identifier
0503: * @return the fully constructed end state instance
0504: */
0505: protected State addEndState(String stateId) {
0506: return getFlowArtifactFactory().createEndState(stateId,
0507: getFlow(), null, null, null, null, null);
0508: }
0509:
0510: /**
0511: * Adds an end state to the flow built by this builder.
0512: * @param stateId the state identifier
0513: * @param viewName the string-encoded view selector
0514: * @return the fully constructed end state instance
0515: */
0516: protected State addEndState(String stateId, String viewName) {
0517: return getFlowArtifactFactory().createEndState(stateId,
0518: getFlow(), null, viewSelector(viewName), null, null,
0519: null);
0520: }
0521:
0522: /**
0523: * Adds an end state to the flow built by this builder.
0524: * @param stateId the state identifier
0525: * @param viewName the string-encoded view selector
0526: * @param outputMapper the output mapper to map output attributes for the
0527: * end state (a flow outcome)
0528: * @return the fully constructed end state instance
0529: */
0530: protected State addEndState(String stateId, String viewName,
0531: AttributeMapper outputMapper) {
0532: return getFlowArtifactFactory().createEndState(stateId,
0533: getFlow(), null, viewSelector(viewName), outputMapper,
0534: null, null);
0535: }
0536:
0537: /**
0538: * Adds an end state to the flow built by this builder.
0539: * @param stateId the state identifier
0540: * @param entryActions the actions to execute when the state is entered
0541: * @param viewSelector the view selector that will make the view selection
0542: * when the state is entered
0543: * @param outputMapper the output mapper to map output attributes for the
0544: * end state (a flow outcome)
0545: * @param exceptionHandlers any exception handlers to attach to the state
0546: * @param attributes attributes to assign to the state that may be used to
0547: * affect state construction and execution
0548: * @return the fully constructed end state instance
0549: */
0550: protected State addEndState(String stateId, Action[] entryActions,
0551: ViewSelector viewSelector, AttributeMapper outputMapper,
0552: FlowExecutionExceptionHandler[] exceptionHandlers,
0553: AttributeMap attributes) {
0554: return getFlowArtifactFactory().createEndState(stateId,
0555: getFlow(), entryActions, viewSelector, outputMapper,
0556: exceptionHandlers, attributes);
0557: }
0558:
0559: // helpers to create misc. flow artifacts
0560:
0561: /**
0562: * Factory method that creates a view selector from an encoded
0563: * view name. See {@link TextToViewSelector} for information on the
0564: * conversion rules.
0565: * @param viewName the encoded view selector
0566: * @return the view selector
0567: */
0568: public ViewSelector viewSelector(String viewName) {
0569: return (ViewSelector) fromStringTo(ViewSelector.class).execute(
0570: viewName);
0571: }
0572:
0573: /**
0574: * Resolves the action with the specified id. Simply looks the action up by
0575: * id and returns it.
0576: * @param id the action id
0577: * @return the action
0578: * @throws FlowArtifactLookupException the action could not be resolved
0579: */
0580: protected Action action(String id)
0581: throws FlowArtifactLookupException {
0582: return getFlowServiceLocator().getAction(id);
0583: }
0584:
0585: /**
0586: * Creates a bean invoking action that invokes the method identified by the
0587: * signature on the bean associated with the action identifier.
0588: * @param beanId the id identifying an arbitrary
0589: * <code>java.lang.Object</code> to be used as an action
0590: * @param methodSignature the signature of the method to invoke on the POJO
0591: * @return the adapted bean invoking action
0592: * @throws FlowArtifactLookupException the action could not be resolved
0593: */
0594: protected Action action(String beanId,
0595: MethodSignature methodSignature)
0596: throws FlowArtifactLookupException {
0597: return getBeanInvokingActionFactory().createBeanInvokingAction(
0598: beanId, getFlowServiceLocator().getBeanFactory(),
0599: methodSignature, null,
0600: getFlowServiceLocator().getConversionService(), null);
0601: }
0602:
0603: /**
0604: * Creates a bean invoking action that invokes the method identified by the
0605: * signature on the bean associated with the action identifier.
0606: * @param beanId the id identifying an arbitrary
0607: * <code>java.lang.Object</code> to be used as an action
0608: * @param methodSignature the signature of the method to invoke on the POJO
0609: * @return the adapted bean invoking action
0610: * @throws FlowArtifactLookupException the action could not be resolved
0611: */
0612: protected Action action(String beanId,
0613: MethodSignature methodSignature,
0614: ActionResultExposer resultExposer)
0615: throws FlowArtifactLookupException {
0616: return getBeanInvokingActionFactory().createBeanInvokingAction(
0617: beanId, getFlowServiceLocator().getBeanFactory(),
0618: methodSignature, resultExposer,
0619: getFlowServiceLocator().getConversionService(), null);
0620: }
0621:
0622: /**
0623: * Creates an evaluate action that evaluates the expression when executed.
0624: * @param expression the expression to evaluate
0625: */
0626: protected Action action(Expression expression) {
0627: return action(expression, null);
0628: }
0629:
0630: /**
0631: * Creates an evaluate action that evaluates the expression when executed.
0632: * @param expression the expression to evaluate
0633: * @param resultExposer the evaluation result exposer
0634: */
0635: protected Action action(Expression expression,
0636: ActionResultExposer resultExposer) {
0637: return new EvaluateAction(expression, resultExposer);
0638: }
0639:
0640: /**
0641: * Parses the expression string into an evaluatable {@link Expression} object.
0642: * @param expressionString the expression string, e.g. flowScope.order.number
0643: * @return the evaluatable expression
0644: */
0645: protected Expression expression(String expressionString) {
0646: return getFlowServiceLocator().getExpressionParser()
0647: .parseExpression(expressionString);
0648: }
0649:
0650: /**
0651: * Parses the expression string into a settable {@link Expression} object.
0652: * @param expressionString the expression string, e.g. flowScope.order.number
0653: * @return the evaluatable expression
0654: * @since 1.0.2
0655: */
0656: protected SettableExpression settableExpression(
0657: String expressionString) {
0658: return getFlowServiceLocator().getExpressionParser()
0659: .parseSettableExpression(expressionString);
0660: }
0661:
0662: /**
0663: * Convert the encoded method signature string to a {@link MethodSignature}
0664: * object. Method signatures are used to match methods on POJO services to
0665: * invoke on a {@link AbstractBeanInvokingAction bean invoking action}.
0666: * <p>
0667: * Encoded method signature format:
0668: *
0669: * Method without arguments:
0670: * <pre>
0671: * ${methodName}
0672: * </pre>
0673: *
0674: * Method with arguments:
0675: * <pre>
0676: * ${methodName}(${arg1}, ${arg2}, ${arg n})
0677: * </pre>
0678: *
0679: * @param method the encoded method signature
0680: * @return the method signature
0681: * @see #action(String, MethodSignature, ActionResultExposer)
0682: */
0683: protected MethodSignature method(String method) {
0684: return (MethodSignature) fromStringTo(MethodSignature.class)
0685: .execute(method);
0686: }
0687:
0688: /**
0689: * Factory method for a {@link ActionResultExposer result exposer}. A
0690: * result exposer is used to expose an action result such as a method return
0691: * value or expression evaluation result to the calling flow.
0692: * @param resultName the result name
0693: * @return the result exposer
0694: * @see #action(String, MethodSignature, ActionResultExposer)
0695: */
0696: protected ActionResultExposer result(String resultName) {
0697: return result(resultName, ScopeType.REQUEST);
0698: }
0699:
0700: /**
0701: * Factory method for a {@link ActionResultExposer result exposer}. A
0702: * result exposer is used to expose an action result such as a method return
0703: * value or expression evaluation result to the calling flow.
0704: * @param resultName the result name
0705: * @param resultScope the scope of the result
0706: * @return the result exposer
0707: * @see #action(String, MethodSignature, ActionResultExposer)
0708: */
0709: protected ActionResultExposer result(String resultName,
0710: ScopeType resultScope) {
0711: return new ActionResultExposer(resultName, resultScope);
0712: }
0713:
0714: /**
0715: * Wrap given action in an {@link AnnotatedAction}} to be able
0716: * to annotate it with attributes.
0717: * @param action the action to annotate
0718: * @return the wrapped action
0719: * @since 1.0.4
0720: */
0721: protected AnnotatedAction annotate(Action action) {
0722: if (action instanceof AnnotatedAction) {
0723: return (AnnotatedAction) action;
0724: } else {
0725: return new AnnotatedAction(action);
0726: }
0727: }
0728:
0729: /**
0730: * Creates an annotated action decorator that instructs the specified method
0731: * be invoked on the multi action when it is executed. Use this when working
0732: * with MultiActions to specify the method on the MultiAction to invoke for
0733: * a particular usage scenario. Use the {@link #method(String)} factory
0734: * method when working with
0735: * {@link AbstractBeanInvokingAction bean invoking actions}.
0736: * @param methodName the name of the method on the multi action instance
0737: * @param multiAction the multi action
0738: * @return the annotated action that when invoked sets up a context property
0739: * used by the multi action to instruct it with what method to invoke
0740: * @since 1.0.4
0741: */
0742: protected AnnotatedAction invoke(String methodName,
0743: Action multiAction) {
0744: AnnotatedAction annotatedAction;
0745: if (multiAction instanceof AnnotatedAction) {
0746: // already wrapped in an AnnotatedAction
0747: annotatedAction = (AnnotatedAction) multiAction;
0748: } else {
0749: annotatedAction = new AnnotatedAction(multiAction);
0750: }
0751: annotatedAction.setMethod(methodName);
0752: return annotatedAction;
0753: }
0754:
0755: /**
0756: * Creates an annotated action decorator that instructs the specified method
0757: * be invoked on the multi action when it is executed. Use this when working
0758: * with MultiActions to specify the method on the MultiAction to invoke for
0759: * a particular usage scenario. Use the {@link #method(String)} factory
0760: * method when working with
0761: * {@link AbstractBeanInvokingAction bean invoking actions}.
0762: * @param methodName the name of the method on the multi action instance
0763: * @param multiAction the multi action
0764: * @return the annotated action that when invoked sets up a context property
0765: * used by the multi action to instruct it with what method to invoke
0766: */
0767: protected AnnotatedAction invoke(String methodName,
0768: MultiAction multiAction) throws FlowArtifactLookupException {
0769: return invoke(methodName, (Action) multiAction);
0770: }
0771:
0772: /**
0773: * Creates an annotated action decorator that makes the given action
0774: * an named action.
0775: * @param name the action name
0776: * @param action the action to name
0777: * @return the annotated named action
0778: */
0779: protected AnnotatedAction name(String name, Action action) {
0780: AnnotatedAction annotatedAction;
0781: if (action instanceof AnnotatedAction) {
0782: // already wrapped in an AnnotatedAction
0783: annotatedAction = (AnnotatedAction) action;
0784: } else {
0785: annotatedAction = new AnnotatedAction(action);
0786: }
0787: annotatedAction.setName(name);
0788: return annotatedAction;
0789: }
0790:
0791: /**
0792: * Request that the attribute mapper with the specified name be used to map
0793: * attributes between a parent flow and a spawning subflow when the subflow
0794: * state being constructed is entered.
0795: * @param id the id of the attribute mapper that will map attributes between
0796: * the flow built by this builder and the subflow
0797: * @return the attribute mapper
0798: * @throws FlowArtifactLookupException no FlowAttributeMapper implementation
0799: * was exported with the specified id
0800: */
0801: protected FlowAttributeMapper attributeMapper(String id)
0802: throws FlowArtifactLookupException {
0803: return getFlowServiceLocator().getAttributeMapper(id);
0804: }
0805:
0806: /**
0807: * Request that the <code>Flow</code> with the specified flowId be spawned
0808: * as a subflow when the subflow state being built is entered. Simply
0809: * resolves the subflow definition by id and returns it; throwing a
0810: * fail-fast exception if it does not exist.
0811: * @param id the flow definition id
0812: * @return the flow to be used as a subflow, this should be passed to a
0813: * addSubflowState call
0814: * @throws FlowArtifactLookupException when the flow cannot be resolved
0815: */
0816: protected Flow flow(String id) throws FlowArtifactLookupException {
0817: return getFlowServiceLocator().getSubflow(id);
0818: }
0819:
0820: /**
0821: * Creates a transition criteria that is used to match a Transition. The
0822: * criteria is based on the provided expression string.
0823: * @param transitionCriteriaExpression the transition criteria expression,
0824: * typically simply a static event identifier (e.g. "submit")
0825: * @return the transition criteria
0826: * @see TextToTransitionCriteria
0827: */
0828: protected TransitionCriteria on(String transitionCriteriaExpression) {
0829: return (TransitionCriteria) fromStringTo(
0830: TransitionCriteria.class).execute(
0831: transitionCriteriaExpression);
0832: }
0833:
0834: /**
0835: * Creates a target state resolver for the given state id expression.
0836: * @param targetStateIdExpression the target state id expression
0837: * @return the target state resolver
0838: * @see TextToTargetStateResolver
0839: */
0840: protected TargetStateResolver to(String targetStateIdExpression) {
0841: return (TargetStateResolver) fromStringTo(
0842: TargetStateResolver.class).execute(
0843: targetStateIdExpression);
0844: }
0845:
0846: /**
0847: * Creates a new transition.
0848: * @param matchingCriteria the criteria that determines when the transition
0849: * matches
0850: * @param targetStateResolver the resolver of the transition's target state
0851: * @return the transition
0852: */
0853: protected Transition transition(
0854: TransitionCriteria matchingCriteria,
0855: TargetStateResolver targetStateResolver) {
0856: return getFlowArtifactFactory().createTransition(
0857: targetStateResolver, matchingCriteria, null, null);
0858: }
0859:
0860: /**
0861: * Creates a new transition.
0862: * @param matchingCriteria the criteria that determines when the transition
0863: * matches
0864: * @param targetStateResolver the resolver of the transition's target state
0865: * @param executionCriteria the criteria that determines if a matched
0866: * transition is allowed to execute
0867: * @return the transition
0868: */
0869: protected Transition transition(
0870: TransitionCriteria matchingCriteria,
0871: TargetStateResolver targetStateResolver,
0872: TransitionCriteria executionCriteria) {
0873: return getFlowArtifactFactory().createTransition(
0874: targetStateResolver, matchingCriteria,
0875: executionCriteria, null);
0876: }
0877:
0878: /**
0879: * Creates a new transition.
0880: * @param matchingCriteria the criteria that determines when the transition
0881: * matches
0882: * @param targetStateResolver the resolver of the transition's target state
0883: * @param executionCriteria the criteria that determines if a matched
0884: * transition is allowed to execute
0885: * @param attributes transition attributes
0886: * @return the transition
0887: */
0888: protected Transition transition(
0889: TransitionCriteria matchingCriteria,
0890: TargetStateResolver targetStateResolver,
0891: TransitionCriteria executionCriteria,
0892: AttributeMap attributes) {
0893: return getFlowArtifactFactory().createTransition(
0894: targetStateResolver, matchingCriteria,
0895: executionCriteria, attributes);
0896: }
0897:
0898: /**
0899: * Creates a <code>TransitionCriteria</code> that will execute the
0900: * specified action when the Transition is executed but before the
0901: * transition's target state is entered.
0902: * <p>
0903: * This criteria will only allow the Transition to complete execution if the
0904: * Action completes successfully.
0905: * @param action the action to execute after a transition is matched but
0906: * before it transitions to its target state
0907: * @return the transition execution criteria
0908: */
0909: protected TransitionCriteria ifReturnedSuccess(Action action) {
0910: return new ActionTransitionCriteria(action);
0911: }
0912:
0913: /**
0914: * Creates the <code>success</code> event id. "Success" indicates that an
0915: * action completed successfuly.
0916: * @return the event id
0917: */
0918: protected String success() {
0919: return eventFactorySupport.getSuccessEventId();
0920: }
0921:
0922: /**
0923: * Creates the <code>error</code> event id. "Error" indicates that an
0924: * action completed with an error status.
0925: * @return the event id
0926: */
0927: protected String error() {
0928: return eventFactorySupport.getErrorEventId();
0929: }
0930:
0931: /**
0932: * Creates the <code>submit</code> event id. "Submit" indicates the user
0933: * submitted a request (form) for processing.
0934: * @return the event id
0935: */
0936: protected String submit() {
0937: return "submit";
0938: }
0939:
0940: /**
0941: * Creates the <code>back</code> event id. "Back" indicates the user wants
0942: * to go to the previous step in the flow.
0943: * @return the event id
0944: */
0945: protected String back() {
0946: return "back";
0947: }
0948:
0949: /**
0950: * Creates the <code>cancel</code> event id. "Cancel" indicates the flow
0951: * was aborted because the user changed their mind.
0952: * @return the event id
0953: */
0954: protected String cancel() {
0955: return "cancel";
0956: }
0957:
0958: /**
0959: * Creates the <code>finish</code> event id. "Finish" indicates the flow
0960: * has finished processing.
0961: * @return the event id
0962: */
0963: protected String finish() {
0964: return "finish";
0965: }
0966:
0967: /**
0968: * Creates the <code>select</code> event id. "Select" indicates an object
0969: * was selected for processing or display.
0970: * @return the event id
0971: */
0972: protected String select() {
0973: return "select";
0974: }
0975:
0976: /**
0977: * Creates the <code>edit</code> event id. "Edit" indicates an object was
0978: * selected for creation or updating.
0979: * @return the event id
0980: */
0981: protected String edit() {
0982: return "edit";
0983: }
0984:
0985: /**
0986: * Creates the <code>add</code> event id. "Add" indicates a child object
0987: * is being added to a parent collection.
0988: * @return the event id
0989: */
0990: protected String add() {
0991: return "add";
0992: }
0993:
0994: /**
0995: * Creates the <code>delete</code> event id. "Delete" indicates a object
0996: * is being removed.
0997: * @return the event id
0998: */
0999: protected String delete() {
1000: return "delete";
1001: }
1002:
1003: /**
1004: * Creates the <code>yes</code> event id. "Yes" indicates a true result
1005: * was returned.
1006: * @return the event id
1007: */
1008: protected String yes() {
1009: return eventFactorySupport.getYesEventId();
1010: }
1011:
1012: /**
1013: * Creates the <code>no</code> event id. "False" indicates a false result
1014: * was returned.
1015: * @return the event id
1016: */
1017: protected String no() {
1018: return eventFactorySupport.getNoEventId();
1019: }
1020:
1021: /**
1022: * Factory method that returns a new, fully configured mapping builder to
1023: * assist with building {@link Mapping} objects used by a
1024: * {@link FlowAttributeMapper} to map attributes.
1025: * @return the mapping builder
1026: */
1027: protected MappingBuilder mapping() {
1028: MappingBuilder mapping = new MappingBuilder(
1029: getFlowServiceLocator().getExpressionParser());
1030: mapping.setConversionService(getFlowServiceLocator()
1031: .getConversionService());
1032: return mapping;
1033: }
1034:
1035: public String toString() {
1036: return new ToStringCreator(this ).toString();
1037: }
1038:
1039: // internal helpers
1040:
1041: private FlowArtifactFactory getFlowArtifactFactory() {
1042: return getFlowServiceLocator().getFlowArtifactFactory();
1043: }
1044:
1045: private BeanInvokingActionFactory getBeanInvokingActionFactory() {
1046: return getFlowServiceLocator().getBeanInvokingActionFactory();
1047: }
1048: }
|