All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.webflow.engine.ActionState Maven / Gradle / Ivy

There is a newer version: 1.0.6
Show newest version
/*
 * Copyright 2004-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.webflow.engine;

import java.util.Iterator;

import org.springframework.core.style.StylerUtils;
import org.springframework.core.style.ToStringCreator;
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.ViewSelection;

/**
 * A transitionable state that executes one or more actions when entered. When
 * the action(s) are executed this state responds to their result(s) to decide
 * what state to transition to next.
 * 

* If more than one action is configured they are executed in an ordered chain * until one returns a result event that matches a state transition out of * this state. This is a form of the Chain of Responsibility (CoR) pattern. *

* The result of an action's execution is typically the criteria for a * transition out of this state. Additional information in the current * {@link RequestContext} may also be tested as part of custom transitional * criteria, allowing for sophisticated transition expressions that reason on * contextual state. *

* Each action executed by this action state may be provisioned with a set of * arbitrary execution properties. These properties are made available to the * action at execution time and may be used to influence action execution * behavior. *

* Common action execution properties include: *

*

* * * * * * * * * *
PropertyDescription
nameThe 'name' property is used as a qualifier for an action's result event, * and is typically used to allow the flow to respond to a specific action's * outcome within a larger action chain. For example, if an action named * myAction returns a success result, a transition * that matches on event myAction.success will be searched, and * if found, executed. If this action is not assigned a name a transition for * the base success event will be searched and if found, * executed.
* This is useful in situations where you want to execute actions in an ordered * chain as part of one action state, and wish to transition on the result of * the last one in the chain. For example: * *
 *     <action-state id="setupForm"> 
 *         <action name="setup" bean="myAction" method="setupForm"/> 
 *         <action name="referenceData" bean="myAction" method="setupReferenceData"/> 
 *         <transition on="referenceData.success" to="displayForm"/> 
 *     </action-state>
 * 
* * When the 'setupForm' state above is entered, the 'setup' action will execute, * followed by the 'referenceData' action. After 'referenceData' execution, the * flow will then respond to the 'referenceData.success' event by transitioning * to the 'displayForm' state. The 'setup.success' event that was signaled by * the 'setup' action will effectively be ignored.
methodThe 'method' property is the name of a target method on a * {@link org.springframework.webflow.action.MultiAction} to * execute. In the MultiAction scenario the named method must have the signature * public Event ${method}(RequestContext) throws Exception. * As an example of this scenario, a method property with value setupForm * would bind to a method on a MultiAction instance with the signature: * public Event setupForm(RequestContext context).
* As an alternative to a MultiAction method binding, this action state may * excute a * {@link org.springframework.webflow.action.AbstractBeanInvokingAction bean invoking action} * that invokes a method on a POJO (Plain Old Java Object). If the method * signature accepts arguments those arguments may be specified by using the * format: * *
 *      methodName(${arg1}, ${arg2}, ...)
 * 
* * Argument ${expressions} are evaluated against the current * RequestContext, allowing for data stored in flow scope or * request scope to be passed as arguments to the POJO. In addition, POJO return * values may be exposed to the flow automatically. See the bean invoking action * type hierarchy for more information.
* * @see org.springframework.webflow.execution.Action * @see org.springframework.webflow.action.MultiAction * @see org.springframework.webflow.action.AbstractBeanInvokingAction * * @author Keith Donald * @author Erwin Vervaet */ public class ActionState extends TransitionableState { /** * The list of actions to be executed when this state is entered. */ private ActionList actionList = new ActionList(); /** * Creates a new action state. * @param flow the owning flow * @param id the state identifier (must be unique to the flow) * @throws IllegalArgumentException when this state cannot be added to given flow, * e.g. beasue the id is not unique * @see #getActionList() */ public ActionState(Flow flow, String id) throws IllegalArgumentException { super(flow, id); } /** * Returns the list of actions executable by this action state. The * returned list is mutable. * @return the state action list */ public ActionList getActionList() { return actionList; } /* * Overrides getRequiredTransition(RequestContext) to throw a local * NoMatchingActionResultTransitionException if a transition on the * occurence of an action result event cannot be matched. Used to facilitate * an action invocation chain. *

Note that we cannot catch NoMatchingTransitionException since that could lead to unwanted * situations where we're catching an exception that's generated by another * state, e.g. because of a configuration error! */ public Transition getRequiredTransition(RequestContext context) throws NoMatchingTransitionException { Transition transition = getTransitionSet().getTransition(context); if (transition == null) { throw new NoMatchingActionResultTransitionException(this, context.getLastEvent()); } return transition; } /** * Specialization of State's doEnter template method that * executes behaviour specific to this state type in polymorphic fashion. *

* This implementation iterates over each configured Action * instance and executes it. Execution continues until an * Action returns a result event that matches a transition in * this request context, or the set of all actions is exhausted. * @param context the control context for the currently executing flow, used * by this state to manipulate the flow execution * @return a view selection signaling that control should be returned to the * client and a view rendered * @throws FlowExecutionException if an exception occurs in this state */ protected ViewSelection doEnter(RequestControlContext context) throws FlowExecutionException { int executionCount = 0; String[] eventIds = new String[actionList.size()]; Iterator it = actionList.iterator(); while (it.hasNext()) { Action action = (Action)it.next(); Event event = ActionExecutor.execute(action, context); if (event != null) { eventIds[executionCount] = event.getId(); try { // will check both local state transitions and global transitions return context.signalEvent(event); } catch (NoMatchingActionResultTransitionException e) { if (logger.isDebugEnabled()) { logger.debug("Action execution [" + (executionCount + 1) + "] resulted in no matching transition on event '" + event.getId() + "'" + (it.hasNext() ? ": proceeding to the next action in the list" : ": action list exhausted")); } } } else { if (logger.isDebugEnabled()) { logger.debug("Action execution [" + (executionCount + 1) + "] returned a [null] event" + (it.hasNext() ? ": proceeding to the next action in the list" : ": action list exhausted")); } eventIds[executionCount] = null; } executionCount++; } if (executionCount > 0) { throw new NoMatchingTransitionException(getFlow().getId(), getId(), context.getLastEvent(), "No transition was matched on the event(s) signaled by the [" + executionCount + "] action(s) that executed in this action state '" + getId() + "' of flow '" + getFlow().getId() + "'; transitions must be defined to handle action result outcomes -- " + "possible flow configuration error? Note: the eventIds signaled were: '" + StylerUtils.style(eventIds) + "', while the supported set of transitional criteria for this action state is '" + StylerUtils.style(getTransitionSet().getTransitionCriterias()) + "'"); } else { throw new IllegalStateException( "No actions were executed, thus I cannot execute any state transition " + "-- programmer configuration error; make sure you add at least one action to this state's action list"); } } protected void appendToString(ToStringCreator creator) { creator.append("actionList", actionList); super.appendToString(creator); } /** * Local "no transition found" exception used to report that an action * result could not be mapped to a state transition. * * @author Keith Donald * @author Erwin Vervaet */ private static class NoMatchingActionResultTransitionException extends NoMatchingTransitionException { /** * Creates a new exception. * @param state the action state * @param resultEvent the action result event */ public NoMatchingActionResultTransitionException(ActionState state, Event resultEvent) { super(state.getFlow().getId(), state.getId(), resultEvent, "Cannot find a transition matching an action result event; continuing with next action..."); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy