diff --git a/spring-webflow/src/main/java/org/springframework/webflow/core/expression/el/SpringBeanWebFlowELResolver.java b/spring-webflow/src/main/java/org/springframework/webflow/core/expression/el/SpringBeanWebFlowELResolver.java new file mode 100644 index 00000000..deac0c1d --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/core/expression/el/SpringBeanWebFlowELResolver.java @@ -0,0 +1,62 @@ +package org.springframework.webflow.core.expression.el; + +import javax.el.ELContext; +import javax.el.ELException; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.support.StaticListableBeanFactory; +import org.springframework.web.jsf.el.SpringBeanFacesELResolver; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.RequestContextHolder; + +/** + * EL resolver for Spring Beans accessible to the flow's local bean factory. + * + * @author Jeremy Grelle + * + * TODO - Extend SpringBeanELResolver when Spring 2.5.2 is available. + */ +public class SpringBeanWebFlowELResolver extends SpringBeanFacesELResolver { + + private static final BeanFactory EMPTY_BEAN_FACTORY = new StaticListableBeanFactory(); + + public Class getType(ELContext elContext, Object base, Object property) throws ELException { + if (base != null && base instanceof RequestContext) { + return super.getType(elContext, null, property); + } else { + return null; + } + } + + public Object getValue(ELContext elContext, Object base, Object property) throws ELException { + if (base != null && base instanceof RequestContext) { + return super.getValue(elContext, null, property); + } else { + return null; + } + } + + public boolean isReadOnly(ELContext elContext, Object base, Object property) throws ELException { + if (base != null && base instanceof RequestContext) { + return super.isReadOnly(elContext, null, property); + } else { + return false; + } + } + + public void setValue(ELContext elContext, Object base, Object property, Object value) throws ELException { + if (base != null && base instanceof RequestContext) { + super.setValue(elContext, null, property, value); + } + } + + protected BeanFactory getBeanFactory(ELContext elContext) { + RequestContext rc = RequestContextHolder.getRequestContext(); + if (rc.getActiveFlow() instanceof BeanFactory) { + return (BeanFactory) rc.getActiveFlow(); + } else { + return EMPTY_BEAN_FACTORY; + } + } + +} diff --git a/spring-webflow/src/main/java/org/springframework/webflow/core/expression/el/WebFlowELExpressionParser.java b/spring-webflow/src/main/java/org/springframework/webflow/core/expression/el/WebFlowELExpressionParser.java index feb56d17..f06d73aa 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/core/expression/el/WebFlowELExpressionParser.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/core/expression/el/WebFlowELExpressionParser.java @@ -44,6 +44,7 @@ public class WebFlowELExpressionParser extends ELExpressionParser { public ELContext getELContext(Object target) { List customResolvers = new ArrayList(); customResolvers.add(new RequestContextELResolver()); + customResolvers.add(new SpringBeanWebFlowELResolver()); customResolvers.add(new ScopeSearchingELResolver()); ELResolver resolver = new DefaultELResolver(target, customResolvers); return new WebFlowELContext(resolver); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java index 5159afc9..f7b7db97 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/Flow.java @@ -21,6 +21,10 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.support.StaticListableBeanFactory; import org.springframework.binding.mapping.AttributeMapper; import org.springframework.binding.mapping.MappingContext; import org.springframework.core.style.StylerUtils; @@ -99,8 +103,9 @@ import org.springframework.webflow.execution.RequestContext; * @author Keith Donald * @author Erwin Vervaet * @author Colin Sampaleanu + * @author Jeremy Grelle */ -public class Flow extends AnnotatedObject implements FlowDefinition { +public class Flow extends AnnotatedObject implements FlowDefinition, BeanFactory { /** * Logger, can be used in subclasses. @@ -160,6 +165,11 @@ public class Flow extends AnnotatedObject implements FlowDefinition { */ private FlowExecutionExceptionHandlerSet exceptionHandlerSet = new FlowExecutionExceptionHandlerSet(); + /** + * The local bean factory for this flow + */ + private BeanFactory localBeanFactory = new StaticListableBeanFactory(); + /** * Construct a new flow definition with the given id. The id should be unique among all flows. * @param id the flow identifier @@ -606,4 +616,44 @@ public class Flow extends AnnotatedObject implements FlowDefinition { "outputMapper", outputMapper).toString(); } + public void setLocalBeanFactory(BeanFactory localBeanFactory) { + this.localBeanFactory = localBeanFactory; + } + + public boolean containsBean(String name) { + return localBeanFactory.containsBean(name); + } + + public String[] getAliases(String name) { + return localBeanFactory.getAliases(name); + } + + public Object getBean(String name, Class requiredType) throws BeansException { + return localBeanFactory.getBean(name, requiredType); + } + + public Object getBean(String name, Object[] args) throws BeansException { + return localBeanFactory.getBean(name, args); + } + + public Object getBean(String name) throws BeansException { + return localBeanFactory.getBean(name); + } + + public Class getType(String name) throws NoSuchBeanDefinitionException { + return localBeanFactory.getType(name); + } + + public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { + return localBeanFactory.isPrototype(name); + } + + public boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return localBeanFactory.isSingleton(name); + } + + public boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException { + return localBeanFactory.isTypeMatch(name, targetType); + } + } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java index 5054fc0c..d7060d2b 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java @@ -322,7 +322,9 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } protected Flow createFlow() { - return parseFlow(getDocumentElement()); + Flow flow = parseFlow(getDocumentElement()); + flow.setLocalBeanFactory(getLocalContext().getBeanFactory()); + return flow; } public void buildVariables() throws FlowBuilderException { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java index b4a47b1c..9be2b2c2 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java @@ -36,6 +36,9 @@ public class FlowController extends AbstractController { private Map flowHandlers = new HashMap(); + /** The response header to be set on an Ajax redirect */ + private static final String FLOW_REDIRECT_URL_HEADER = "Flow-Redirect-URL"; + /** * @param flowExecutor the web flow executor service */ @@ -127,13 +130,13 @@ public class FlowController extends AbstractController { if (logger.isDebugEnabled()) { logger.debug("Sending flow execution redirect to " + url); } - response.sendRedirect(url); + sendRedirect(context, response, url); return null; } else if (context.externalRedirectRequested()) { if (logger.isDebugEnabled()) { logger.debug("Sending external redirect to " + context.getExternalRedirectUrl()); } - response.sendRedirect(context.getExternalRedirectUrl()); + sendRedirect(context, response, context.getExternalRedirectUrl()); return null; } else { // nothing to do: flow has handled the response @@ -147,13 +150,13 @@ public class FlowController extends AbstractController { if (logger.isDebugEnabled()) { logger.debug("Sending flow definition to " + url); } - response.sendRedirect(url); + sendRedirect(context, response, url); return null; } else if (context.externalRedirectRequested()) { if (logger.isDebugEnabled()) { logger.debug("Sending external redirect to " + context.getExternalRedirectUrl()); } - response.sendRedirect(context.getExternalRedirectUrl()); + sendRedirect(context, response, context.getExternalRedirectUrl()); return null; } else { return handleFlowOutcome(result.getFlowId(), result.getEndedOutcome(), result.getEndedOutput(), @@ -164,6 +167,15 @@ public class FlowController extends AbstractController { } } + private void sendRedirect(ServletExternalContext context, HttpServletResponse response, String targetUrl) + throws IOException { + if (context.isAjaxRequest()) { + context.setResponseHeader(FLOW_REDIRECT_URL_HEADER, response.encodeRedirectURL(targetUrl)); + } else { + response.sendRedirect(response.encodeRedirectURL(targetUrl)); + } + } + private MutableAttributeMap getFlowInput(String flowId, HttpServletRequest request) { FlowHandler handler = getFlowHandler(flowId); if (handler != null) { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java index 8b2ff427..78b63f4a 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java @@ -29,6 +29,9 @@ public class FlowHandlerAdapter extends WebApplicationObjectSupport implements H private FlowUrlHandler urlHandler; + /** The response header to be set on an Ajax redirect */ + private static final String FLOW_REDIRECT_URL_HEADER = "Flow-Redirect-URL"; + public FlowHandlerAdapter(FlowExecutor flowExecutor) { this.flowExecutor = flowExecutor; this.urlHandler = new DefaultFlowUrlHandler(); @@ -115,13 +118,13 @@ public class FlowHandlerAdapter extends WebApplicationObjectSupport implements H if (logger.isDebugEnabled()) { logger.debug("Sending flow execution redirect to " + url); } - response.sendRedirect(url); + sendRedirect(context, response, url); return null; } else if (context.externalRedirectRequested()) { if (logger.isDebugEnabled()) { logger.debug("Sending external redirect to " + context.getExternalRedirectUrl()); } - response.sendRedirect(context.getExternalRedirectUrl()); + sendRedirect(context, response, context.getExternalRedirectUrl()); return null; } else { return null; @@ -134,13 +137,13 @@ public class FlowHandlerAdapter extends WebApplicationObjectSupport implements H if (logger.isDebugEnabled()) { logger.debug("Sending flow definition to " + url); } - response.sendRedirect(url); + sendRedirect(context, response, url); return null; } else if (context.externalRedirectRequested()) { if (logger.isDebugEnabled()) { logger.debug("Sending external redirect to " + context.getExternalRedirectUrl()); } - response.sendRedirect(context.getExternalRedirectUrl()); + sendRedirect(context, response, context.getExternalRedirectUrl()); return null; } else { ModelAndView mv = handler.handleExecutionOutcome(result.getEndedOutcome(), result.getEndedOutput(), @@ -153,6 +156,15 @@ public class FlowHandlerAdapter extends WebApplicationObjectSupport implements H } } + private void sendRedirect(ServletExternalContext context, HttpServletResponse response, String targetUrl) + throws IOException { + if (context.isAjaxRequest()) { + context.setResponseHeader(FLOW_REDIRECT_URL_HEADER, response.encodeRedirectURL(targetUrl)); + } else { + response.sendRedirect(response.encodeRedirectURL(targetUrl)); + } + } + private ModelAndView handleFlowException(FlowException e, HttpServletRequest request, HttpServletResponse response, FlowHandler handler) throws IOException { ModelAndView result = handler.handleException(e, request, response); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java index ee884be5..25e4ab99 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java @@ -10,6 +10,7 @@ import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.core.expression.WebFlowOgnlExpressionParser; import org.springframework.webflow.core.expression.el.WebFlowELExpressionParser; import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.RequestContextHolder; import org.springframework.webflow.execution.ScopeType; import org.springframework.webflow.test.MockRequestContext; @@ -62,6 +63,7 @@ public class ActionResultExposerTests extends TestCase { ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, null); RequestContext context = new MockRequestContext(); + RequestContextHolder.setRequestContext(context); exposer.exposeResult(valueToSet, context); @@ -80,6 +82,7 @@ public class ActionResultExposerTests extends TestCase { ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, Boolean.class); RequestContext context = new MockRequestContext(); + RequestContextHolder.setRequestContext(context); exposer.exposeResult(valueToSet, context); @@ -98,6 +101,7 @@ public class ActionResultExposerTests extends TestCase { ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, null); RequestContext context = new MockRequestContext(); + RequestContextHolder.setRequestContext(context); TestBean bean = new TestBean(); context.getRequestScope().put("bean", bean); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java index 215ea069..4f5e249a 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java @@ -26,6 +26,7 @@ import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.core.expression.el.WebFlowELExpressionParser; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.RequestContextHolder; import org.springframework.webflow.execution.ScopeType; import org.springframework.webflow.test.MockRequestContext; @@ -39,6 +40,7 @@ public class EvaluateActionTests extends TestCase { private MockRequestContext context = new MockRequestContext(); protected void setUp() throws Exception { + RequestContextHolder.setRequestContext(context); context.getFlowScope().put("foo", "bar"); context.getFlowScope().put("bean", new TestBean()); } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java index 72235177..6fee07dd 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java @@ -11,6 +11,7 @@ import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.core.expression.WebFlowOgnlExpressionParser; import org.springframework.webflow.core.expression.el.WebFlowELExpressionParser; import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.RequestContextHolder; import org.springframework.webflow.execution.ScopeType; import org.springframework.webflow.test.MockRequestContext; @@ -52,6 +53,7 @@ public class SetActionTests extends TestCase { SetAction action = new SetAction(attributeExpression, scope, valueExpression); RequestContext context = new MockRequestContext(); + RequestContextHolder.setRequestContext(context); action.execute(context); assertTrue(context.getRequestScope().contains("foo")); @@ -73,6 +75,7 @@ public class SetActionTests extends TestCase { SetAction action = new SetAction(attributeExpression, scope, valueExpression); RequestContext context = new MockRequestContext(); + RequestContextHolder.setRequestContext(context); TestBean bean = new TestBean(); context.getRequestScope().put("bean", bean);