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.binding.convert.ConversionService;
019: import org.springframework.binding.convert.support.DefaultConversionService;
020: import org.springframework.binding.method.MethodInvoker;
021: import org.springframework.binding.method.MethodSignature;
022: import org.springframework.util.Assert;
023: import org.springframework.webflow.execution.Action;
024: import org.springframework.webflow.execution.Event;
025: import org.springframework.webflow.execution.RequestContext;
026:
027: /**
028: * Base class for actions that delegate to methods on beans (POJOs - Plain Old
029: * Java Objects). Acts as an adapter that adapts an {@link Object} method to the
030: * Spring Web Flow {@link Action} contract.
031: * <p>
032: * Subclasses are required to implement the {@link #getBean(RequestContext)}
033: * method, returning the bean on which a method should be invoked.
034: *
035: * @see BeanInvokingActionFactory
036: *
037: * @author Keith Donald
038: */
039: public abstract class AbstractBeanInvokingAction extends AbstractAction {
040:
041: /**
042: * The signature of the method to invoke on the target bean, capable of
043: * resolving the method when used with a {@link MethodInvoker}. Required.
044: */
045: private MethodSignature methodSignature;
046:
047: /**
048: * The method invoker that performs the action->bean method binding,
049: * accepting a {@link MethodSignature} and
050: * {@link #getBean(RequestContext) target bean} instance.
051: */
052: private MethodInvoker methodInvoker = new MethodInvoker();
053:
054: /**
055: * The specification (configuration) for how bean method return values
056: * should be exposed to an executing flow that invokes this action.
057: */
058: private ActionResultExposer methodResultExposer;
059:
060: /**
061: * The strategy that adapts bean method return values to Event objects.
062: */
063: private ResultEventFactory resultEventFactory = new SuccessEventFactory();
064:
065: /**
066: * Creates a new bean invoking action.
067: * @param methodSignature the signature of the method to invoke
068: */
069: protected AbstractBeanInvokingAction(MethodSignature methodSignature) {
070: Assert
071: .notNull(methodSignature,
072: "The signature of the target method to invoke is required");
073: this .methodSignature = methodSignature;
074: }
075:
076: /**
077: * Returns the signature of the method to invoke on the target bean.
078: */
079: public MethodSignature getMethodSignature() {
080: return methodSignature;
081: }
082:
083: /**
084: * Returns the configuration for how bean method return values should be
085: * exposed to an executing flow that invokes this action.
086: */
087: public ActionResultExposer getMethodResultExposer() {
088: return methodResultExposer;
089: }
090:
091: /**
092: * Configures how bean method return values should be exposed to an
093: * executing flow that invokes this action. This is optional. By default the
094: * bean method return values do not get exposed to the executing flow.
095: */
096: public void setMethodResultExposer(
097: ActionResultExposer methodResultExposer) {
098: this .methodResultExposer = methodResultExposer;
099: }
100:
101: /**
102: * Returns the event adaption strategy used by this action.
103: */
104: protected ResultEventFactory getResultEventFactory() {
105: return resultEventFactory;
106: }
107:
108: /**
109: * Set the bean return value->event adaption strategy. Defaults to
110: * {@link SuccessEventFactory}, so all bean method return values will be
111: * interpreted as "success".
112: */
113: public void setResultEventFactory(
114: ResultEventFactory resultEventFactory) {
115: this .resultEventFactory = resultEventFactory;
116: }
117:
118: /**
119: * Set the conversion service to perform type conversion of event parameters
120: * to method arguments as neccessary.
121: * Defaults to {@link DefaultConversionService}.
122: */
123: public void setConversionService(ConversionService conversionService) {
124: methodInvoker.setConversionService(conversionService);
125: }
126:
127: /**
128: * Returns the bean method invoker helper.
129: */
130: protected MethodInvoker getMethodInvoker() {
131: return methodInvoker;
132: }
133:
134: protected Event doExecute(RequestContext context) throws Exception {
135: Object bean = getBean(context);
136: Object returnValue = getMethodInvoker().invoke(methodSignature,
137: bean, context);
138: if (methodResultExposer != null) {
139: methodResultExposer.exposeResult(returnValue, context);
140: }
141: return resultEventFactory.createResultEvent(bean, returnValue,
142: context);
143: }
144:
145: // subclassing hooks
146:
147: /**
148: * Retrieves the bean to invoke a method on. Subclasses need to implement
149: * this method.
150: * @param context the flow execution request context
151: * @return the bean on which to invoke methods
152: * @throws Exception when the bean cannot be retreived
153: */
154: protected abstract Object getBean(RequestContext context)
155: throws Exception;
156:
157: }
|