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.apache.commons.logging.Log;
019: import org.apache.commons.logging.LogFactory;
020: import org.springframework.beans.factory.BeanInitializationException;
021: import org.springframework.beans.factory.InitializingBean;
022: import org.springframework.util.ClassUtils;
023: import org.springframework.webflow.core.collection.AttributeMap;
024: import org.springframework.webflow.execution.Action;
025: import org.springframework.webflow.execution.Event;
026: import org.springframework.webflow.execution.RequestContext;
027: import org.springframework.webflow.execution.support.EventFactorySupport;
028:
029: /**
030: * Base action that provides assistance commonly needed by action
031: * implementations. This includes:
032: * <ul>
033: * <li>Implementing {@link InitializingBean} to receive an init callback
034: * when deployed within a Spring bean factory.
035: * <li>Exposing convenient event factory methods to create common result
036: * {@link Event} objects such as "success" and "error".
037: * <li>A hook for inserting action pre and post execution logic.
038: * </ul>
039: *
040: * @author Keith Donald
041: * @author Erwin Vervaet
042: */
043: public abstract class AbstractAction implements Action,
044: InitializingBean {
045:
046: /**
047: * Logger, usable in subclasses.
048: */
049: protected final Log logger = LogFactory.getLog(getClass());
050:
051: /**
052: * Returns the helper delegate for creating action execution result events.
053: * @return the event factory support
054: */
055: public EventFactorySupport getEventFactorySupport() {
056: return new EventFactorySupport();
057: }
058:
059: public void afterPropertiesSet() throws Exception {
060: try {
061: initAction();
062: } catch (Exception ex) {
063: throw new BeanInitializationException(
064: "Initialization of this Action failed: "
065: + ex.getMessage(), ex);
066: }
067: }
068:
069: /**
070: * Action initializing callback, may be overriden by subclasses to perform
071: * custom initialization logic.
072: * <p>
073: * Keep in mind that this hook will only be invoked when this action is
074: * deployed in a Spring application context since it uses the Spring
075: * {@link InitializingBean} mechanism to trigger action initialisation.
076: */
077: protected void initAction() throws Exception {
078: }
079:
080: /**
081: * Returns a "success" result event.
082: */
083: protected Event success() {
084: return getEventFactorySupport().success(this );
085: }
086:
087: /**
088: * Returns a "success" result event with the provided result object as a
089: * parameter.
090: * @param result the action success result
091: */
092: protected Event success(Object result) {
093: return getEventFactorySupport().success(this , result);
094: }
095:
096: /**
097: * Returns an "error" result event.
098: */
099: protected Event error() {
100: return getEventFactorySupport().error(this );
101: }
102:
103: /**
104: * Returns an "error" result event caused by the provided exception.
105: * @param e the exception that caused the error event, to be configured as
106: * an event attribute
107: */
108: protected Event error(Exception e) {
109: return getEventFactorySupport().error(this , e);
110: }
111:
112: /**
113: * Returns a "yes" result event.
114: */
115: protected Event yes() {
116: return getEventFactorySupport().yes(this );
117: }
118:
119: /**
120: * Returns a "no" result event.
121: */
122: protected Event no() {
123: return getEventFactorySupport().no(this );
124: }
125:
126: /**
127: * Returns yes() if the boolean result is true, no() if false.
128: * @param booleanResult the boolean
129: * @return yes or no
130: */
131: protected Event result(boolean booleanResult) {
132: return getEventFactorySupport().event(this , booleanResult);
133: }
134:
135: /**
136: * Returns a result event for this action with the specified identifier.
137: * Typically called as part of return, for example:
138: *
139: * <pre>
140: * protected Event doExecute(RequestContext context) {
141: * // do some work
142: * if (some condition) {
143: * return result("success");
144: * } else {
145: * return result("error");
146: * }
147: * }
148: * </pre>
149: *
150: * Consider calling the error() or success() factory methods for returning
151: * common results.
152: * @param eventId the result event identifier
153: * @return the action result event
154: */
155: protected Event result(String eventId) {
156: return getEventFactorySupport().event(this , eventId);
157: }
158:
159: /**
160: * Returns a result event for this action with the specified identifier and
161: * the specified set of attributes. Typically called as part of return, for
162: * example:
163: *
164: * <pre>
165: * protected Event doExecute(RequestContext context) {
166: * // do some work
167: * AttributeMap resultAttributes = new AttributeMap();
168: * resultAttributes.put("name", "value");
169: * if (some condition) {
170: * return result("success", resultAttributes);
171: * } else {
172: * return result("error", resultAttributes);
173: * }
174: * }
175: * </pre>
176: *
177: * Consider calling the error() or success() factory methods for returning
178: * common results.
179: * @param eventId the result event identifier
180: * @param resultAttributes the event attributes
181: * @return the action result event
182: */
183: protected Event result(String eventId, AttributeMap resultAttributes) {
184: return getEventFactorySupport().event(this , eventId,
185: resultAttributes);
186: }
187:
188: /**
189: * Returns a result event for this action with the specified identifier and
190: * a single attribute.
191: * @param eventId the result id
192: * @param resultAttributeName the attribute name
193: * @param resultAttributeValue the attribute value
194: * @return the action result event
195: */
196: protected Event result(String eventId, String resultAttributeName,
197: Object resultAttributeValue) {
198: return getEventFactorySupport().event(this , eventId,
199: resultAttributeName, resultAttributeValue);
200: }
201:
202: public final Event execute(RequestContext context) throws Exception {
203: if (logger.isDebugEnabled()) {
204: logger.debug("Action '" + getActionNameForLogging()
205: + "' beginning execution");
206: }
207: Event result = doPreExecute(context);
208: if (result == null) {
209: result = doExecute(context);
210: if (logger.isDebugEnabled()) {
211: if (result != null) {
212: logger.debug("Action '" + getActionNameForLogging()
213: + "' completed execution; result is '"
214: + result.getId() + "'");
215: } else {
216: logger
217: .debug("Action '"
218: + getActionNameForLogging()
219: + "' completed execution; result is [null]");
220: }
221: }
222: doPostExecute(context);
223: } else {
224: if (logger.isInfoEnabled()) {
225: logger
226: .info("Action execution disallowed; pre-execution result is '"
227: + result.getId() + "'");
228: }
229: }
230: return result;
231: }
232:
233: // subclassing hooks
234:
235: /**
236: * Internal helper to return the name of this action for logging
237: * purposes. Defaults to the short class name.
238: * @see ClassUtils#getShortName(java.lang.Class)
239: */
240: protected String getActionNameForLogging() {
241: return ClassUtils.getShortName(getClass());
242: }
243:
244: /**
245: * Pre-action-execution hook, subclasses may override. If this method
246: * returns a non-<code>null</code> event, the <code>doExecute()</code>
247: * method will <b>not</b> be called and the returned event will be used to
248: * select a transition to trigger in the calling action state. If this
249: * method returns <code>null</code>, <code>doExecute()</code> will be
250: * called to obtain an action result event.
251: * <p>
252: * This implementation just returns <code>null</code>.
253: * @param context the action execution context, for accessing and setting
254: * data in "flow scope" or "request scope"
255: * @return the non-<code>null</code> action result, in which case the
256: * <code>doExecute()</code> will not be called, or <code>null</code> if
257: * the <code>doExecute()</code> method should be called to obtain the
258: * action result
259: * @throws Exception an <b>unrecoverable</b> exception occured, either
260: * checked or unchecked
261: */
262: protected Event doPreExecute(RequestContext context)
263: throws Exception {
264: return null;
265: }
266:
267: /**
268: * Template hook method subclasses should override to encapsulate their
269: * specific action execution logic.
270: * @param context the action execution context, for accessing and setting
271: * data in "flow scope" or "request scope"
272: * @return the action result event
273: * @throws Exception an <b>unrecoverable</b> exception occured, either
274: * checked or unchecked
275: */
276: protected abstract Event doExecute(RequestContext context)
277: throws Exception;
278:
279: /**
280: * Post-action execution hook, subclasses may override. Will only be called
281: * if <code>doExecute()</code> was called, e.g. when <code>doPreExecute()</code>
282: * returned <code>null</code>.
283: * <p>
284: * This implementation does nothing.
285: * @param context the action execution context, for accessing and setting
286: * data in "flow scope" or "request scope"
287: * @throws Exception an <b>unrecoverable</b> exception occured, either
288: * checked or unchecked
289: */
290: protected void doPostExecute(RequestContext context)
291: throws Exception {
292: }
293: }
|