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.action;
017:
018: import org.springframework.util.Assert;
019: import org.springframework.webflow.execution.Event;
020: import org.springframework.webflow.execution.RequestContext;
021: import org.springframework.webflow.util.DispatchMethodInvoker;
022:
023: /**
024: * Action implementation that bundles two or more action execution methods into
025: * a single class. Action execution methods defined by subclasses must adhere to
026: * the following signature:
027: *
028: * <pre>
029: * public Event ${method}(RequestContext context) throws Exception;
030: * </pre>
031: *
032: * When this action is invoked, by default the <code>id</code> of the calling
033: * action state state is treated as the action execution method name.
034: * Alternatively, the execution method name may be explicitly specified as a
035: * attribute of the calling action state.
036: * <p>
037: * For example, the following action state definition:
038: *
039: * <pre>
040: * <action-state id="search">
041: * <action bean="searchAction"/>
042: * <transition on="success" to="results"/>
043: * </action-state>
044: * </pre>
045: *
046: * ... when entered, executes the method:
047: *
048: * <pre>
049: * public Event search(RequestContext context) throws Exception;
050: * </pre>
051: *
052: * Alternatively (and typically recommended), you may explictly specify the method name:
053: *
054: * <pre>
055: * <action-state id="search">
056: * <action bean="searchAction" method="executeSearch"/>
057: * <transition on="success" to="results"/>
058: * </action-state>
059: * </pre>
060: *
061: * <p>
062: * A typical use of the MultiAction is to centralize all command logic for a
063: * flow in one place. Another common use is to centralize form setup and submit
064: * logic in one place, or CRUD (create/read/update/delete) operations for a
065: * single domain object in one place.
066: *
067: * @see MultiAction.MethodResolver
068: * @see org.springframework.webflow.action.DefaultMultiActionMethodResolver
069: *
070: * @author Keith Donald
071: * @author Erwin Vervaet
072: */
073: public class MultiAction extends AbstractAction {
074:
075: /**
076: * A cache for dispatched action execute methods. The default signature is
077: * <code>public Event ${method}(RequestContext context) throws Exception;</code>.
078: */
079: private DispatchMethodInvoker methodInvoker;
080:
081: /**
082: * The action method resolver strategy.
083: */
084: private MethodResolver methodResolver = new DefaultMultiActionMethodResolver();
085:
086: /**
087: * Protected default constructor; not invokable for direct MultiAction instantiation.
088: * Intended for use by subclasses.
089: * <p>
090: * Sets the target to this multi action instance.
091: * @see #setTarget(Object)
092: */
093: protected MultiAction() {
094: setTarget(this );
095: }
096:
097: /**
098: * Constructs a multi action that invokes methods on the specified target
099: * object. Note: invokable methods on the target must conform to the multi action
100: * method signature:
101: * <pre>
102: * public Event ${method}(RequestContext context) throws Exception;
103: * </pre>
104: * @param target the target of this multi action's invocations
105: */
106: public MultiAction(Object target) {
107: setTarget(target);
108: }
109:
110: /**
111: * Sets the target of this multi action's invocations.
112: * @param target the target
113: */
114: protected final void setTarget(Object target) {
115: methodInvoker = new DispatchMethodInvoker(target,
116: new Class[] { RequestContext.class });
117: }
118:
119: /**
120: * Get the strategy used to resolve action execution method names.
121: */
122: public MethodResolver getMethodResolver() {
123: return methodResolver;
124: }
125:
126: /**
127: * Set the strategy used to resolve action execution method names.
128: * Allows full control over the method resolution algorithm.
129: * Defaults to {@link DefaultMultiActionMethodResolver}.
130: */
131: public void setMethodResolver(MethodResolver methodResolver) {
132: this .methodResolver = methodResolver;
133: }
134:
135: protected final Event doExecute(RequestContext context)
136: throws Exception {
137: String method = getMethodResolver().resolveMethod(context);
138: Object obj = methodInvoker.invoke(method,
139: new Object[] { context });
140: if (obj != null) {
141: Assert
142: .isInstanceOf(
143: Event.class,
144: obj,
145: "The '"
146: + method
147: + "' action execution method on target object '"
148: + methodInvoker.getTarget()
149: + "' did not return an Event object but '"
150: + obj
151: + "' of type "
152: + obj.getClass().getName()
153: + " -- "
154: + "Programmer error; make sure the method signature conforms to "
155: + "'public Event ${method}(RequestContext context) throws Exception;'.");
156: }
157: return (Event) obj;
158: }
159:
160: /**
161: * Strategy interface used by the MultiAction to map a request context to
162: * the name of an action execution method.
163: *
164: * @author Keith Donald
165: * @author Erwin Vervaet
166: */
167: public interface MethodResolver {
168:
169: /**
170: * Resolve a method name from given flow execution request context.
171: * @param context the flow execution request context
172: * @return the name of the method that should handle action
173: * execution
174: */
175: public String resolveMethod(RequestContext context);
176: }
177: }
|