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;
017:
018: import org.springframework.core.style.ToStringCreator;
019: import org.springframework.util.Assert;
020: import org.springframework.util.StringUtils;
021: import org.springframework.webflow.core.collection.AttributeMap;
022: import org.springframework.webflow.execution.Action;
023: import org.springframework.webflow.execution.Event;
024: import org.springframework.webflow.execution.RequestContext;
025:
026: /**
027: * An action proxy/decorator that stores arbitrary properties about a target
028: * <code>Action</code> implementation for use within a specific Action
029: * execution context, for example an <code>ActionState</code> definition, a
030: * <code>TransitionCriteria</code> definition, or in a test environment.
031: * <p>
032: * An annotated action is an action that wraps another action (referred to as
033: * the <i>target</i> action), setting up the target action's execution attributes
034: * before invoking {@link Action#execute}.
035: *
036: * @author Keith Donald
037: * @author Erwin Vervaet
038: */
039: public class AnnotatedAction extends AnnotatedObject implements Action {
040:
041: // well known attributes
042:
043: /**
044: * The action name attribute ("name").
045: * <p>
046: * The name attribute is often used as a qualifier for an action's result
047: * event, and is typically used to allow the flow to respond to a specific
048: * action's outcome within a larger action execution chain.
049: * @see ActionState
050: */
051: public static final String NAME_ATTRIBUTE = "name";
052:
053: /**
054: * The action execution method attribute ("method").
055: * <p>
056: * The method property is a hint about what method should be invoked; for
057: * example, the name of a specific target method on a
058: * {@link org.springframework.webflow.action.MultiAction multi action}.
059: * @see ActionState
060: */
061: public static final String METHOD_ATTRIBUTE = "method";
062:
063: /**
064: * The target action to execute.
065: */
066: private Action targetAction;
067:
068: /**
069: * Creates a new annotated action object for the specified action. No
070: * contextual properties are provided.
071: * @param targetAction the action
072: */
073: public AnnotatedAction(Action targetAction) {
074: setTargetAction(targetAction);
075: }
076:
077: /**
078: * Returns the wrapped target action.
079: * @return the action
080: */
081: public Action getTargetAction() {
082: return targetAction;
083: }
084:
085: /**
086: * Set the target action wrapped by this decorator.
087: */
088: public void setTargetAction(Action targetAction) {
089: Assert.notNull(targetAction,
090: "The targetAction to annotate is required");
091: this .targetAction = targetAction;
092: }
093:
094: /**
095: * Returns the name of a named action, or <code>null</code> if the action
096: * is unnamed. Used when mapping action result events to transitions.
097: * @see #isNamed()
098: * @see #postProcessResult(Event)
099: */
100: public String getName() {
101: return getAttributeMap().getString(NAME_ATTRIBUTE);
102: }
103:
104: /**
105: * Sets the name of a named action. This is optional and can be
106: * <code>null</code>.
107: * @param name the action name
108: */
109: public void setName(String name) {
110: getAttributeMap().put(NAME_ATTRIBUTE, name);
111: }
112:
113: /**
114: * Returns whether or not the wrapped target action is a named action.
115: * @see #getName()
116: * @see #setName(String)
117: */
118: public boolean isNamed() {
119: return StringUtils.hasText(getName());
120: }
121:
122: /**
123: * Returns the name of the action method to invoke when the target action is
124: * executed.
125: */
126: public String getMethod() {
127: return getAttributeMap().getString(METHOD_ATTRIBUTE);
128: }
129:
130: /**
131: * Sets the name of the action method to invoke when the target action is
132: * executed.
133: * @param method the action method name
134: */
135: public void setMethod(String method) {
136: getAttributeMap().put(METHOD_ATTRIBUTE, method);
137: }
138:
139: /**
140: * Set an attribute on this annotated object.
141: * @param attributeName the name of the attribute to set
142: * @param attributeValue the value of the attribute
143: * @return this object, to support call chaining
144: * @since 1.0.4
145: */
146: public AnnotatedAction putAttribute(String attributeName,
147: Object attributeValue) {
148: getAttributeMap().put(attributeName, attributeValue);
149: return this ;
150: }
151:
152: public Event execute(RequestContext context) throws Exception {
153: AttributeMap originalAttributes = getAttributeMap();
154: try {
155: context.setAttributes(getAttributeMap());
156: Event result = getTargetAction().execute(context);
157: return postProcessResult(result);
158: } finally {
159: // restore original attributes
160: context.setAttributes(originalAttributes);
161: }
162: }
163:
164: /**
165: * Get the event id to be used as grounds for a transition in the containing
166: * state, based on given result returned from action execution.
167: * <p>
168: * If the wrapped action is named, the name will be used as a qualifier for
169: * the event (e.g. "myAction.success").
170: * @param resultEvent the action result event
171: */
172: protected Event postProcessResult(Event resultEvent) {
173: if (resultEvent == null) {
174: return null;
175: }
176: if (isNamed()) {
177: // qualify result event id with action name for a named action
178: String qualifiedId = getName() + "." + resultEvent.getId();
179: resultEvent = new Event(resultEvent.getSource(),
180: qualifiedId, resultEvent.getAttributes());
181: }
182: return resultEvent;
183: }
184:
185: public String toString() {
186: return new ToStringCreator(this ).append("targetAction",
187: getTargetAction()).append("attributes",
188: getAttributeMap()).toString();
189: }
190: }
|