From 241abd4321da763e4616a61e015f2845c3dd7892 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Tue, 1 Apr 2008 23:37:40 +0000 Subject: [PATCH] OPEN - issue SWF-183: Complete Portlet integration for Web Flow 2.x, including JSF + SWF integration in a Portlet environment. http://jira.springframework.org/browse/SWF-183 --- .../portlet/DefaultFlowUrlHandler.java | 34 ++- .../context/portlet/FlowUrlHandler.java | 9 + .../portlet/PortletExternalContext.java | 16 +- .../mvc/portlet/AbstractFlowHandler.java | 37 +++ .../portlet}/FlowHandler.java | 2 +- .../portlet}/FlowHandlerAdapter.java | 17 +- .../webflow/mvc/portlet/PortletMvcView.java | 70 ++++++ .../{ => servlet}/AbstractFlowHandler.java | 2 +- .../mvc/{ => servlet}/AjaxHandler.java | 2 +- .../mvc/{ => servlet}/FlowController.java | 2 +- .../mvc/{ => servlet}/FlowHandler.java | 2 +- .../mvc/{ => servlet}/FlowHandlerAdapter.java | 2 +- .../webflow/mvc/servlet/ServletMvcView.java | 38 ++++ .../SpringJavascriptAjaxHandler.java | 2 +- .../webflow/mvc/{ => servlet}/package.html | 0 .../InternalFlowResourceMvcViewFactory.java | 10 +- .../webflow/mvc/view/MvcView.java | 15 +- .../mvc/view/MvcViewFactoryCreator.java | 3 +- .../mvc/view/ViewResolvingMvcViewFactory.java | 16 +- .../portlet/DefaultFlowUrlHandlerTests.java | 17 ++ .../portlet/PortletExternalContextTests.java | 3 +- .../mvc/portlet/FlowHandlerAdapterTests.java | 212 ++++++++++++++++++ .../mvc/portlet/PortletMvcViewTests.java | 60 +++++ .../{ => servlet}/FlowControllerTests.java | 4 +- .../FlowHandlerAdapterTests.java | 4 +- .../mvc/servlet/ServletMvcViewTests.java | 119 ++++++++++ .../webflow/mvc/view/MvcViewFactoryTests.java | 1 + .../webflow/mvc/view/MvcViewTests.java | 27 ++- 28 files changed, 678 insertions(+), 48 deletions(-) create mode 100644 spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/AbstractFlowHandler.java rename spring-webflow/src/main/java/org/springframework/webflow/{portletmvc => mvc/portlet}/FlowHandler.java (98%) rename spring-webflow/src/main/java/org/springframework/webflow/{portletmvc => mvc/portlet}/FlowHandlerAdapter.java (91%) create mode 100644 spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcView.java rename spring-webflow/src/main/java/org/springframework/webflow/mvc/{ => servlet}/AbstractFlowHandler.java (95%) rename spring-webflow/src/main/java/org/springframework/webflow/mvc/{ => servlet}/AjaxHandler.java (97%) rename spring-webflow/src/main/java/org/springframework/webflow/mvc/{ => servlet}/FlowController.java (99%) rename spring-webflow/src/main/java/org/springframework/webflow/mvc/{ => servlet}/FlowHandler.java (98%) rename spring-webflow/src/main/java/org/springframework/webflow/mvc/{ => servlet}/FlowHandlerAdapter.java (99%) create mode 100644 spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcView.java rename spring-webflow/src/main/java/org/springframework/webflow/mvc/{ => servlet}/SpringJavascriptAjaxHandler.java (97%) rename spring-webflow/src/main/java/org/springframework/webflow/mvc/{ => servlet}/package.html (100%) create mode 100644 spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapterTests.java create mode 100644 spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/PortletMvcViewTests.java rename spring-webflow/src/test/java/org/springframework/webflow/mvc/{ => servlet}/FlowControllerTests.java (98%) rename spring-webflow/src/test/java/org/springframework/webflow/mvc/{ => servlet}/FlowHandlerAdapterTests.java (98%) create mode 100644 spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/ServletMvcViewTests.java diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandler.java index ba2f7055..b4b94eca 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandler.java @@ -15,11 +15,17 @@ */ package org.springframework.webflow.context.portlet; +import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.PortletRequest; +import javax.portlet.PortletSession; import javax.portlet.PortletURL; +import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + /** * Default flow URL handler for SWF 2. * @@ -27,17 +33,39 @@ import javax.portlet.RenderResponse; */ public class DefaultFlowUrlHandler implements FlowUrlHandler { + private static final Log logger = LogFactory.getLog(DefaultFlowUrlHandler.class); + + private static final String EXECUTION_ATTRIBUTE = "execution"; + public String getFlowExecutionKey(PortletRequest request) { - return request.getParameter("execution"); + String flowExecutionKey = request.getParameter(EXECUTION_ATTRIBUTE); + logger.debug("flowExecutionKey '" + flowExecutionKey + "' found as request param"); + PortletSession session = request.getPortletSession(false); + if (session != null) { + if (flowExecutionKey == null && request instanceof RenderRequest) { + flowExecutionKey = (String) session.getAttribute(EXECUTION_ATTRIBUTE); + logger.debug("flowExecutionKey '" + flowExecutionKey + "' found as session param"); + } else if (flowExecutionKey != null && request instanceof ActionRequest) { + session.removeAttribute(EXECUTION_ATTRIBUTE); + } + } + return flowExecutionKey; } public void setFlowExecutionRenderParameter(String flowExecutionKey, ActionResponse response) { - response.setRenderParameter("execution", flowExecutionKey); + logger.debug("setting flowExecutionKey '" + flowExecutionKey + "' as render param"); + response.setRenderParameter(EXECUTION_ATTRIBUTE, flowExecutionKey); + } + + public void setFlowExecutionInSession(String flowExecutionKey, RenderRequest request) { + logger.debug("setting flowExecutionKey '" + flowExecutionKey + "' as session param"); + PortletSession session = request.getPortletSession(); + session.setAttribute(EXECUTION_ATTRIBUTE, flowExecutionKey); } public String createFlowExecutionUrl(String flowId, String flowExecutionKey, RenderResponse response) { PortletURL url = response.createActionURL(); - url.setParameter("execution", flowExecutionKey); + url.setParameter(EXECUTION_ATTRIBUTE, flowExecutionKey); return url.toString(); } } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/FlowUrlHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/FlowUrlHandler.java index 273dd1a8..eb0fb234 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/FlowUrlHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/FlowUrlHandler.java @@ -17,6 +17,7 @@ package org.springframework.webflow.context.portlet; import javax.portlet.ActionResponse; import javax.portlet.PortletRequest; +import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; /** @@ -41,6 +42,14 @@ public interface FlowUrlHandler { */ public void setFlowExecutionRenderParameter(String flowExecutionKey, ActionResponse response); + /** + * Set the flow execution key into the portlet session. This should only be used when the portlet is started before + * any action requests are made + * @param flowExecutionKey the key + * @param request the render request + */ + public void setFlowExecutionInSession(String flowExecutionKey, RenderRequest request); + public String createFlowExecutionUrl(String flowId, String flowExecutionKey, RenderResponse response); } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletExternalContext.java b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletExternalContext.java index f2915ba3..f24929e9 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletExternalContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/context/portlet/PortletExternalContext.java @@ -101,11 +101,6 @@ public class PortletExternalContext implements ExternalContext { */ private FlowUrlHandler flowUrlHandler; - /** - * Whether this external request context originated from an Ajax request or not. - */ - private boolean ajaxRequest; - /** * In the case where a redirect response is requested, this flag indicates if the redirect should be issued from a * popup dialog. @@ -134,15 +129,6 @@ public class PortletExternalContext implements ExternalContext { init(context, request, response, flowUrlHandler); } - /** - * Indicates if the current request from this client is an ajax request. This flag may effect the handling of - * response writing within Spring Web Flow. - * @param ajaxRequest the ajax request flag - */ - public void setAjaxRequest(boolean ajaxRequest) { - this.ajaxRequest = ajaxRequest; - } - // implementing external context public String getContextPath() { @@ -186,7 +172,7 @@ public class PortletExternalContext implements ExternalContext { } public boolean isAjaxRequest() { - return ajaxRequest; + return false; } public String getFlowExecutionUri(String flowId, String flowExecutionKey) { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/AbstractFlowHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/AbstractFlowHandler.java new file mode 100644 index 00000000..44e2cb64 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/AbstractFlowHandler.java @@ -0,0 +1,37 @@ +package org.springframework.webflow.mvc.portlet; + +import javax.portlet.PortletRequest; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; + +import org.springframework.web.portlet.ModelAndView; +import org.springframework.webflow.core.FlowException; +import org.springframework.webflow.core.collection.AttributeMap; +import org.springframework.webflow.core.collection.MutableAttributeMap; + +/** + * Trivial flow handler base class that simply returns null for all operations. Subclasses should extend and override + * which operations they need. + * + * @author Keith Donald + */ +public class AbstractFlowHandler implements FlowHandler { + + public MutableAttributeMap createExecutionInputMap(PortletRequest request) { + return null; + } + + public String getFlowId() { + return null; + } + + public ModelAndView handleException(FlowException e, RenderRequest request, RenderResponse response) { + return null; + } + + public ModelAndView handleFlowOutcome(String outcome, AttributeMap output, RenderRequest request, + RenderResponse response) { + return null; + } + +} diff --git a/spring-webflow/src/main/java/org/springframework/webflow/portletmvc/FlowHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandler.java similarity index 98% rename from spring-webflow/src/main/java/org/springframework/webflow/portletmvc/FlowHandler.java rename to spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandler.java index 4181d41e..953495c4 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/portletmvc/FlowHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.webflow.portletmvc; +package org.springframework.webflow.mvc.portlet; import javax.portlet.PortletRequest; import javax.portlet.RenderRequest; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/portletmvc/FlowHandlerAdapter.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapter.java similarity index 91% rename from spring-webflow/src/main/java/org/springframework/webflow/portletmvc/FlowHandlerAdapter.java rename to spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapter.java index f37cc38d..886724ec 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/portletmvc/FlowHandlerAdapter.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapter.java @@ -1,4 +1,4 @@ -package org.springframework.webflow.portletmvc; +package org.springframework.webflow.mvc.portlet; import java.io.IOException; import java.util.Iterator; @@ -15,6 +15,7 @@ import javax.portlet.RenderResponse; import org.springframework.web.portlet.HandlerAdapter; import org.springframework.web.portlet.ModelAndView; import org.springframework.web.portlet.context.PortletApplicationObjectSupport; +import org.springframework.webflow.context.portlet.DefaultFlowUrlHandler; import org.springframework.webflow.context.portlet.FlowUrlHandler; import org.springframework.webflow.context.portlet.PortletExternalContext; import org.springframework.webflow.core.FlowException; @@ -37,6 +38,7 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen public FlowHandlerAdapter(FlowExecutor flowExecutor) { this.flowExecutor = flowExecutor; + this.urlHandler = new DefaultFlowUrlHandler(); } public boolean supports(Object handler) { @@ -89,7 +91,7 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen try { FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context); if (result.paused()) { - urlHandler.setFlowExecutionRenderParameter(flowExecutionKey, response); + urlHandler.setFlowExecutionRenderParameter(result.getPausedKey(), response); } else { request.getPortletSession().setAttribute(FLOW_EXECUTION_RESULT_ATTRIBUTE, result); } @@ -151,11 +153,18 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen // helpers - private ModelAndView startFlow(RenderRequest request, RenderResponse response, FlowHandler flowHandler) { + private ModelAndView startFlow(RenderRequest request, RenderResponse response, FlowHandler flowHandler) + throws Exception { MutableAttributeMap input = flowHandler.createExecutionInputMap(request); + if (input == null) { + input = defaultFlowExecutionInputMap(request); + } PortletExternalContext context = createPortletExternalContext(request, response); try { - flowExecutor.launchExecution(flowHandler.getFlowId(), input, context); + FlowExecutionResult result = flowExecutor.launchExecution(flowHandler.getFlowId(), input, context); + if (result.paused()) { + urlHandler.setFlowExecutionInSession(result.getPausedKey(), request); + } return null; } catch (FlowException e) { ModelAndView mv = flowHandler.handleException(e, request, response); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcView.java new file mode 100644 index 00000000..ef63d5e6 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcView.java @@ -0,0 +1,70 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.webflow.mvc.portlet; + +import java.util.Map; + +import javax.portlet.PortletContext; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; + +import org.springframework.context.ApplicationContext; +import org.springframework.web.portlet.DispatcherPortlet; +import org.springframework.web.servlet.View; +import org.springframework.web.servlet.ViewRendererServlet; +import org.springframework.webflow.context.ExternalContext; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.mvc.view.MvcView; + +public class PortletMvcView extends MvcView { + + private ApplicationContext applicationContext; + + public PortletMvcView(org.springframework.web.servlet.View view, RequestContext context, + ApplicationContext applicationContext) { + super(view, context); + this.applicationContext = applicationContext; + } + + public void render(Map model, ExternalContext context) throws Exception { + PortletContext portletContext = (PortletContext) context.getNativeContext(); + RenderRequest request = (RenderRequest) context.getNativeRequest(); + RenderResponse response = (RenderResponse) context.getNativeResponse(); + View view = getView(); + + // Set the content type on the response if needed and if possible. + // The Portlet spec requires the content type to be set on the RenderResponse; + // it's not sufficient to let the View set it on the ServletResponse. + if (response.getContentType() == null) { + // No Portlet content type specified yet -> use the view-determined type. + String contentType = view.getContentType(); + if (contentType != null) { + response.setContentType(contentType); + } + } + + // Expose Portlet ApplicationContext to view objects. + request.setAttribute(ViewRendererServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, applicationContext); + + // These attributes are required by the ViewRendererServlet. + request.setAttribute(ViewRendererServlet.VIEW_ATTRIBUTE, view); + request.setAttribute(ViewRendererServlet.MODEL_ATTRIBUTE, model); + + // Include the content of the view in the render response. + portletContext.getRequestDispatcher(DispatcherPortlet.DEFAULT_VIEW_RENDERER_URL).include(request, response); + } + +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/AbstractFlowHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/AbstractFlowHandler.java similarity index 95% rename from spring-webflow/src/main/java/org/springframework/webflow/mvc/AbstractFlowHandler.java rename to spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/AbstractFlowHandler.java index 97d87ee6..b7cf5e70 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/AbstractFlowHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/AbstractFlowHandler.java @@ -1,4 +1,4 @@ -package org.springframework.webflow.mvc; +package org.springframework.webflow.mvc.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/AjaxHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/AjaxHandler.java similarity index 97% rename from spring-webflow/src/main/java/org/springframework/webflow/mvc/AjaxHandler.java rename to spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/AjaxHandler.java index 84534498..c7152bd1 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/AjaxHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/AjaxHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.webflow.mvc; +package org.springframework.webflow.mvc.servlet; import java.io.IOException; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowController.java similarity index 99% rename from spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java rename to spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowController.java index 271817c8..bd09162d 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowController.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowController.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.webflow.mvc; +package org.springframework.webflow.mvc.servlet; import java.io.IOException; import java.util.HashMap; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandler.java similarity index 98% rename from spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandler.java rename to spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandler.java index 78bc0ef0..35e6eb09 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.webflow.mvc; +package org.springframework.webflow.mvc.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java similarity index 99% rename from spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java rename to spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java index 8843b8f6..7ccfd6f1 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/FlowHandlerAdapter.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.webflow.mvc; +package org.springframework.webflow.mvc.servlet; import java.io.IOException; import java.util.Iterator; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcView.java new file mode 100644 index 00000000..cbe3bc3b --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcView.java @@ -0,0 +1,38 @@ +/* + * Copyright 2004-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.webflow.mvc.servlet; + +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.webflow.context.ExternalContext; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.mvc.view.MvcView; + +public class ServletMvcView extends MvcView { + + public ServletMvcView(org.springframework.web.servlet.View view, RequestContext context) { + super(view, context); + } + + public void render(Map model, ExternalContext context) throws Exception { + getView().render(model, (HttpServletRequest) context.getNativeRequest(), + (HttpServletResponse) context.getNativeResponse()); + } + +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/SpringJavascriptAjaxHandler.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/SpringJavascriptAjaxHandler.java similarity index 97% rename from spring-webflow/src/main/java/org/springframework/webflow/mvc/SpringJavascriptAjaxHandler.java rename to spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/SpringJavascriptAjaxHandler.java index cb0b7b17..f0989a26 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/SpringJavascriptAjaxHandler.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/SpringJavascriptAjaxHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.webflow.mvc; +package org.springframework.webflow.mvc.servlet; import java.io.IOException; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/package.html b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/package.html similarity index 100% rename from spring-webflow/src/main/java/org/springframework/webflow/mvc/package.html rename to spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/package.html diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/InternalFlowResourceMvcViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/InternalFlowResourceMvcViewFactory.java index 6236f26e..350cb93f 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/InternalFlowResourceMvcViewFactory.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/InternalFlowResourceMvcViewFactory.java @@ -9,9 +9,12 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.util.ClassUtils; import org.springframework.web.servlet.view.InternalResourceView; import org.springframework.web.servlet.view.JstlView; +import org.springframework.webflow.context.portlet.PortletExternalContext; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.View; import org.springframework.webflow.execution.ViewFactory; +import org.springframework.webflow.mvc.portlet.PortletMvcView; +import org.springframework.webflow.mvc.servlet.ServletMvcView; /** * View factory implementation that creates a Spring-MVC Internal Resource view to render a flow-relative view resource @@ -67,7 +70,12 @@ class InternalFlowResourceMvcViewFactory implements ViewFactory { } private MvcView createMvcView(org.springframework.web.servlet.View view, RequestContext context) { - MvcView mvcView = new MvcView(view, context); + MvcView mvcView; + if (context.getExternalContext() instanceof PortletExternalContext) { + mvcView = new PortletMvcView(view, context, applicationContext); + } else { + mvcView = new ServletMvcView(view, context); + } mvcView.setExpressionParser(expressionParser); mvcView.setFormatterRegistry(formatterRegistry); return mvcView; diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java index ec35a4e3..e8be7bea 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java @@ -22,9 +22,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.springframework.beans.factory.BeanFactory; import org.springframework.binding.collection.MapAdaptable; import org.springframework.binding.convert.ConversionException; @@ -49,6 +46,7 @@ import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; +import org.springframework.webflow.context.ExternalContext; import org.springframework.webflow.core.collection.ParameterMap; import org.springframework.webflow.definition.TransitionDefinition; import org.springframework.webflow.definition.TransitionableStateDefinition; @@ -57,7 +55,7 @@ import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.View; import org.springframework.webflow.expression.DefaultExpressionParserFactory; -class MvcView implements View { +public abstract class MvcView implements View { private static final MappingResultsCriteria PROPERTY_NOT_FOUND_ERROR = new PropertyNotFoundError(); @@ -100,8 +98,7 @@ class MvcView implements View { model.put("currentUser", context.getExternalContext().getCurrentUser()); // TODO expose flow context to mvc view try { - view.render(model, (HttpServletRequest) context.getExternalContext().getNativeRequest(), - (HttpServletResponse) context.getExternalContext().getNativeResponse()); + render(model, context.getExternalContext()); } catch (IOException e) { throw e; } catch (Exception e) { @@ -109,6 +106,8 @@ class MvcView implements View { } } + public abstract void render(Map model, ExternalContext context) throws Exception; + public void resume() { determineEventId(context); if (eventId == null) { @@ -301,6 +300,10 @@ class MvcView implements View { return null; } + protected org.springframework.web.servlet.View getView() { + return view; + } + private static class PropertyNotFoundError implements MappingResultsCriteria { public boolean test(MappingResult result) { return result.getResult().isError() && "propertyNotFound".equals(result.getResult().getErrorCode()); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcViewFactoryCreator.java index dafa2887..bd99b05f 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcViewFactoryCreator.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcViewFactoryCreator.java @@ -60,7 +60,8 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser, FormatterRegistry formatterRegistry, ResourceLoader resourceLoader) { if (viewResolvers != null) { - return new ViewResolvingMvcViewFactory(viewIdExpression, expressionParser, formatterRegistry, viewResolvers); + return new ViewResolvingMvcViewFactory(viewIdExpression, expressionParser, formatterRegistry, + viewResolvers, applicationContext); } else { return new InternalFlowResourceMvcViewFactory(viewIdExpression, expressionParser, formatterRegistry, applicationContext, resourceLoader); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewResolvingMvcViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewResolvingMvcViewFactory.java index 99c98eaa..97706101 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewResolvingMvcViewFactory.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewResolvingMvcViewFactory.java @@ -7,11 +7,15 @@ import java.util.Locale; import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.format.FormatterRegistry; +import org.springframework.context.ApplicationContext; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.web.servlet.ViewResolver; +import org.springframework.webflow.context.portlet.PortletExternalContext; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.View; import org.springframework.webflow.execution.ViewFactory; +import org.springframework.webflow.mvc.portlet.PortletMvcView; +import org.springframework.webflow.mvc.servlet.ServletMvcView; /** * View factory implementation that delegates to the Spring-configured view resolver chain to resolve the Spring MVC @@ -28,15 +32,23 @@ class ViewResolvingMvcViewFactory implements ViewFactory { private List viewResolvers; + private ApplicationContext applicationContext; + public ViewResolvingMvcViewFactory(Expression viewIdExpression, ExpressionParser expressionParser, - FormatterRegistry formatterRegistry, List viewResolvers) { + FormatterRegistry formatterRegistry, List viewResolvers, ApplicationContext context) { this.viewIdExpression = viewIdExpression; this.viewResolvers = viewResolvers; + this.applicationContext = context; } public View getView(RequestContext context) { String viewName = (String) viewIdExpression.getValue(context); - MvcView view = new MvcView(resolveView(viewName), context); + MvcView view; + if (context.getExternalContext() instanceof PortletExternalContext) { + view = new PortletMvcView(resolveView(viewName), context, applicationContext); + } else { + view = new ServletMvcView(resolveView(viewName), context); + } view.setExpressionParser(expressionParser); view.setFormatterRegistry(formatterRegistry); return view; diff --git a/spring-webflow/src/test/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandlerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandlerTests.java index f07e4183..cd439dba 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandlerTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/context/portlet/DefaultFlowUrlHandlerTests.java @@ -2,13 +2,17 @@ package org.springframework.webflow.context.portlet; import junit.framework.TestCase; +import org.springframework.mock.web.portlet.MockActionRequest; import org.springframework.mock.web.portlet.MockActionResponse; import org.springframework.mock.web.portlet.MockPortletRequest; +import org.springframework.mock.web.portlet.MockRenderRequest; import org.springframework.mock.web.portlet.MockRenderResponse; public class DefaultFlowUrlHandlerTests extends TestCase { private DefaultFlowUrlHandler urlHandler = new DefaultFlowUrlHandler(); private MockPortletRequest request = new MockPortletRequest(); + private MockActionRequest actionRequest = new MockActionRequest(); + private MockRenderRequest renderRequest = new MockRenderRequest(); private MockActionResponse actionResponse = new MockActionResponse(); private MockRenderResponse renderResponse = new MockRenderResponse(); @@ -22,6 +26,19 @@ public class DefaultFlowUrlHandlerTests extends TestCase { assertEquals("12345", actionResponse.getRenderParameter("execution")); } + public void testSetFlowExecutionInSession() { + urlHandler.setFlowExecutionInSession("12345", renderRequest); + assertEquals("12345", renderRequest.getPortletSession().getAttribute("execution")); + } + + public void testSessionFlowExecutionRemoval() { + urlHandler.setFlowExecutionInSession("12345", renderRequest); + assertEquals("12345", urlHandler.getFlowExecutionKey(renderRequest)); + actionRequest.setParameter("execution", "12345"); + assertEquals("12345", urlHandler.getFlowExecutionKey(actionRequest)); + assertNull(actionRequest.getPortletSession().getAttribute("execution")); + } + public void testCreateFlowExecutionUrl() { String url = urlHandler.createFlowExecutionUrl("foo", "12345", renderResponse); assertEquals("http://localhost/mockportlet?urlType=action;param_execution=12345", url); diff --git a/spring-webflow/src/test/java/org/springframework/webflow/context/portlet/PortletExternalContextTests.java b/spring-webflow/src/test/java/org/springframework/webflow/context/portlet/PortletExternalContextTests.java index 3ca9b88b..1b84039d 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/context/portlet/PortletExternalContextTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/context/portlet/PortletExternalContextTests.java @@ -64,8 +64,7 @@ public class PortletExternalContextTests extends TestCase { } public void testAjaxRequestAcceptHeader() { - context.setAjaxRequest(true); - assertTrue(context.isAjaxRequest()); + assertFalse(context.isAjaxRequest()); } public void testNotResponseCommitted() { diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapterTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapterTests.java new file mode 100644 index 00000000..4a203a53 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/FlowHandlerAdapterTests.java @@ -0,0 +1,212 @@ +package org.springframework.webflow.mvc.portlet; + +import javax.portlet.ActionRequest; +import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; +import javax.portlet.PortletSession; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; + +import junit.framework.TestCase; + +import org.easymock.EasyMock; +import org.springframework.mock.web.portlet.MockActionRequest; +import org.springframework.mock.web.portlet.MockActionResponse; +import org.springframework.mock.web.portlet.MockPortletContext; +import org.springframework.mock.web.portlet.MockRenderRequest; +import org.springframework.mock.web.portlet.MockRenderResponse; +import org.springframework.web.context.support.StaticWebApplicationContext; +import org.springframework.web.portlet.ModelAndView; +import org.springframework.webflow.context.portlet.DefaultFlowUrlHandler; +import org.springframework.webflow.context.portlet.PortletExternalContext; +import org.springframework.webflow.core.FlowException; +import org.springframework.webflow.core.collection.AttributeMap; +import org.springframework.webflow.core.collection.LocalAttributeMap; +import org.springframework.webflow.core.collection.MutableAttributeMap; +import org.springframework.webflow.execution.Event; +import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException; +import org.springframework.webflow.executor.FlowExecutionResult; +import org.springframework.webflow.executor.FlowExecutor; +import org.springframework.webflow.mvc.portlet.FlowHandler; +import org.springframework.webflow.mvc.portlet.FlowHandlerAdapter; +import org.springframework.webflow.test.MockFlowExecutionKey; + +public class FlowHandlerAdapterTests extends TestCase { + + private FlowHandlerAdapter controller; + private FlowExecutor executor; + private MockPortletContext portletContext; + private MockActionRequest actionRequest; + private MockActionResponse actionResponse; + private MockRenderRequest renderRequest; + private MockRenderResponse renderResponse; + private PortletExternalContext actionContext; + private PortletExternalContext renderContext; + private FlowHandler flowHandler; + private LocalAttributeMap flowInput = new LocalAttributeMap(); + private boolean handleException; + private boolean handleExecutionOutcome; + + protected void setUp() { + executor = (FlowExecutor) EasyMock.createMock(FlowExecutor.class); + controller = new FlowHandlerAdapter(executor) { + protected PortletExternalContext createPortletExternalContext(PortletRequest request, + PortletResponse response) { + if (request instanceof ActionRequest) { + return actionContext; + } else { + return renderContext; + } + } + }; + portletContext = new MockPortletContext(); + actionRequest = new MockActionRequest(); + actionResponse = new MockActionResponse(); + renderRequest = new MockRenderRequest(); + renderResponse = new MockRenderResponse(); + actionContext = new PortletExternalContext(portletContext, actionRequest, actionResponse, + new DefaultFlowUrlHandler()); + renderContext = new PortletExternalContext(portletContext, renderRequest, renderResponse, + new DefaultFlowUrlHandler()); + controller.setApplicationContext(new StaticWebApplicationContext()); + controller.setPortletContext(portletContext); + flowHandler = new FlowHandler() { + public MutableAttributeMap createExecutionInputMap(PortletRequest request) { + return null; + } + + public String getFlowId() { + return "foo"; + } + + public ModelAndView handleException(FlowException e, RenderRequest request, RenderResponse response) { + if (handleException) { + return new ModelAndView("error"); + } else { + return null; + } + } + + public ModelAndView handleFlowOutcome(String outcome, AttributeMap output, RenderRequest request, + RenderResponse response) { + if (handleExecutionOutcome) { + return new ModelAndView("redirect:/home"); + } else { + return null; + } + } + + }; + } + + public void testLaunchFlowRequest() throws Exception { + renderRequest.setContextPath("/springtravel"); + executor.launchExecution("foo", flowInput, renderContext); + FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "12345"); + EasyMock.expectLastCall().andReturn(result); + EasyMock.replay(new Object[] { executor }); + ModelAndView mv = controller.handleRender(renderRequest, renderResponse, flowHandler); + assertNull(mv); + EasyMock.verify(new Object[] { executor }); + } + + public void testResumeFlowActionRequest() throws Exception { + actionRequest.setContextPath("/springtravel"); + actionRequest.addParameter("execution", "12345"); + executor.resumeExecution("12345", actionContext); + FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "123456"); + EasyMock.expectLastCall().andReturn(result); + EasyMock.replay(new Object[] { executor }); + controller.handleAction(actionRequest, actionResponse, flowHandler); + EasyMock.verify(new Object[] { executor }); + } + + public void testResumeFlowRenderRequest() throws Exception { + renderRequest.setContextPath("/springtravel"); + renderRequest.addParameter("execution", "12345"); + executor.resumeExecution("12345", renderContext); + FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "123456"); + EasyMock.expectLastCall().andReturn(result); + EasyMock.replay(new Object[] { executor }); + controller.handleRender(renderRequest, renderResponse, flowHandler); + EasyMock.verify(new Object[] { executor }); + } + + public void testResumeFlowRenderRequestFromSession() throws Exception { + renderRequest.setContextPath("/springtravel"); + PortletSession session = renderRequest.getPortletSession(); + session.setAttribute("execution", "12345"); + executor.resumeExecution("12345", renderContext); + FlowExecutionResult result = FlowExecutionResult.createPausedResult("foo", "123456"); + EasyMock.expectLastCall().andReturn(result); + EasyMock.replay(new Object[] { executor }); + controller.handleRender(renderRequest, renderResponse, flowHandler); + EasyMock.verify(new Object[] { executor }); + } + + public void testDefaultHandleFlowException() throws Exception { + PortletSession session = renderRequest.getPortletSession(); + final FlowException flowException = new FlowException("Error") { + }; + session.setAttribute("actionFlowException", flowException); + try { + controller.handleRender(renderRequest, renderResponse, flowHandler); + fail("Should have thrown exception"); + } catch (FlowException e) { + assertEquals(flowException, e); + } + } + + public void testDefaultHandleNoSuchFlowExecutionException() throws Exception { + actionRequest.setContextPath("/springtravel"); + actionRequest.addParameter("execution", "12345"); + executor.resumeExecution("12345", actionContext); + FlowException flowException = new NoSuchFlowExecutionException(new MockFlowExecutionKey("12345"), null); + EasyMock.expectLastCall().andThrow(flowException); + EasyMock.replay(new Object[] { executor }); + controller.handleAction(actionRequest, actionResponse, flowHandler); + assertNotNull(actionRequest.getPortletSession().getAttribute("actionFlowException")); + EasyMock.verify(new Object[] { executor }); + Exception e = (Exception) actionRequest.getPortletSession().getAttribute("actionFlowException"); + assertTrue(e instanceof NoSuchFlowExecutionException); + } + + public void testHandleFlowOutcomeCustomFlowHandler() throws Exception { + handleExecutionOutcome = true; + renderRequest.setContextPath("/springtravel"); + executor.launchExecution("foo", flowInput, renderContext); + LocalAttributeMap output = new LocalAttributeMap(); + output.put("bar", "baz"); + Event outcome = new Event(this, "finish", output); + FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome); + PortletSession session = renderRequest.getPortletSession(); + session.setAttribute("flowExecutionResult", result); + ModelAndView mv = controller.handleRender(renderRequest, renderResponse, flowHandler); + assertNotNull(mv); + assertEquals("redirect:/home", mv.getViewName()); + } + + public void testHandleFlowExceptionCustomFlowHandler() throws Exception { + handleException = true; + final FlowException flowException = new FlowException("Error") { + }; + renderRequest.setContextPath("/springtravel"); + executor.launchExecution("foo", flowInput, renderContext); + EasyMock.expectLastCall().andThrow(flowException); + EasyMock.replay(new Object[] { executor }); + ModelAndView mv = controller.handleRender(renderRequest, renderResponse, flowHandler); + assertNotNull(mv); + EasyMock.verify(new Object[] { executor }); + } + + public void testHandleFlowExceptionFromSession() throws Exception { + handleException = true; + PortletSession session = renderRequest.getPortletSession(); + final FlowException flowException = new FlowException("Error") { + }; + session.setAttribute("actionFlowException", flowException); + ModelAndView mv = controller.handleRender(renderRequest, renderResponse, flowHandler); + assertEquals("error", mv.getViewName()); + } + +} diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/PortletMvcViewTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/PortletMvcViewTests.java new file mode 100644 index 00000000..fcca2ed0 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/portlet/PortletMvcViewTests.java @@ -0,0 +1,60 @@ +package org.springframework.webflow.mvc.portlet; + +import java.util.Date; +import java.util.Locale; +import java.util.Map; + +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; + +import junit.framework.TestCase; + +import org.easymock.EasyMock; +import org.springframework.binding.format.formatters.DateFormatter; +import org.springframework.binding.format.registry.DefaultFormatterRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.mock.web.portlet.MockPortletContext; +import org.springframework.mock.web.portlet.MockRenderRequest; +import org.springframework.mock.web.portlet.MockRenderResponse; +import org.springframework.web.servlet.ViewRendererServlet; +import org.springframework.webflow.mvc.portlet.PortletMvcView; +import org.springframework.webflow.mvc.view.MvcView; +import org.springframework.webflow.test.MockFlowExecutionKey; +import org.springframework.webflow.test.MockRequestContext; + +public class PortletMvcViewTests extends TestCase { + + private boolean renderCalled; + + private Map model; + + private DefaultFormatterRegistry formatterRegistry = new DefaultFormatterRegistry(); + + private ApplicationContext applicationContext; + + protected void setUp() { + DateFormatter dateFormatter = new DateFormatter(); + dateFormatter.setLocale(Locale.ENGLISH); + formatterRegistry.registerFormatter(Date.class, dateFormatter); + applicationContext = (ApplicationContext) EasyMock.createMock(ApplicationContext.class); + } + + public void testRender() throws Exception { + RenderRequest request = new MockRenderRequest(); + RenderResponse response = new MockRenderResponse(); + MockRequestContext context = new MockRequestContext(); + context.getMockExternalContext().setNativeContext(new MockPortletContext()); + context.getMockExternalContext().setNativeRequest(request); + context.getMockExternalContext().setNativeResponse(response); + context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1")); + org.springframework.web.servlet.View mvcView = (org.springframework.web.servlet.View) EasyMock + .createMock(org.springframework.web.servlet.View.class); + MvcView view = new PortletMvcView(mvcView, context, applicationContext); + view.setFormatterRegistry(formatterRegistry); + view.render(); + assertNotNull(request.getAttribute(ViewRendererServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE)); + assertNotNull(request.getAttribute(ViewRendererServlet.VIEW_ATTRIBUTE)); + assertNotNull(request.getAttribute(ViewRendererServlet.MODEL_ATTRIBUTE)); + } + +} diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/FlowControllerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowControllerTests.java similarity index 98% rename from spring-webflow/src/test/java/org/springframework/webflow/mvc/FlowControllerTests.java rename to spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowControllerTests.java index 0f254d20..32c072a5 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/FlowControllerTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowControllerTests.java @@ -1,4 +1,4 @@ -package org.springframework.webflow.mvc; +package org.springframework.webflow.mvc.servlet; import java.util.HashMap; import java.util.Map; @@ -23,6 +23,8 @@ import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException; import org.springframework.webflow.executor.FlowExecutionResult; import org.springframework.webflow.executor.FlowExecutor; +import org.springframework.webflow.mvc.servlet.FlowController; +import org.springframework.webflow.mvc.servlet.FlowHandler; import org.springframework.webflow.test.MockFlowExecutionKey; public class FlowControllerTests extends TestCase { diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/FlowHandlerAdapterTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java similarity index 98% rename from spring-webflow/src/test/java/org/springframework/webflow/mvc/FlowHandlerAdapterTests.java rename to spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java index b6fc1abe..52f5d18a 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/FlowHandlerAdapterTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/FlowHandlerAdapterTests.java @@ -1,4 +1,4 @@ -package org.springframework.webflow.mvc; +package org.springframework.webflow.mvc.servlet; import java.util.HashMap; import java.util.Map; @@ -23,6 +23,8 @@ import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.repository.NoSuchFlowExecutionException; import org.springframework.webflow.executor.FlowExecutionResult; import org.springframework.webflow.executor.FlowExecutor; +import org.springframework.webflow.mvc.servlet.FlowHandler; +import org.springframework.webflow.mvc.servlet.FlowHandlerAdapter; import org.springframework.webflow.test.MockFlowExecutionKey; public class FlowHandlerAdapterTests extends TestCase { diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/ServletMvcViewTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/ServletMvcViewTests.java new file mode 100644 index 00000000..cfc63abc --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/servlet/ServletMvcViewTests.java @@ -0,0 +1,119 @@ +package org.springframework.webflow.mvc.servlet; + +import java.security.Principal; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import junit.framework.TestCase; + +import org.springframework.binding.format.formatters.DateFormatter; +import org.springframework.binding.format.registry.DefaultFormatterRegistry; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockServletContext; +import org.springframework.validation.BindingResult; +import org.springframework.web.servlet.View; +import org.springframework.webflow.mvc.servlet.ServletMvcView; +import org.springframework.webflow.mvc.view.MvcView; +import org.springframework.webflow.test.MockFlowExecutionKey; +import org.springframework.webflow.test.MockRequestContext; + +public class ServletMvcViewTests extends TestCase { + + private boolean renderCalled; + + private Map model; + + private DefaultFormatterRegistry formatterRegistry = new DefaultFormatterRegistry(); + + protected void setUp() { + DateFormatter dateFormatter = new DateFormatter(); + dateFormatter.setLocale(Locale.ENGLISH); + formatterRegistry.registerFormatter(Date.class, dateFormatter); + } + + public void testRender() throws Exception { + MockRequestContext context = new MockRequestContext(); + context.getRequestScope().put("foo", "bar"); + context.getFlowScope().put("bar", "baz"); + context.getFlowScope().put("bindBean", new BindBean()); + context.getConversationScope().put("baz", "boop"); + context.getFlashScope().put("boop", "bing"); + context.getMockExternalContext().setCurrentUser("Keith"); + context.getMockExternalContext().setNativeContext(new MockServletContext()); + context.getMockExternalContext().setNativeRequest(new MockHttpServletRequest()); + context.getMockExternalContext().setNativeResponse(new MockHttpServletResponse()); + context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1")); + org.springframework.web.servlet.View mvcView = new MockView(); + MvcView view = new ServletMvcView(mvcView, context); + view.setFormatterRegistry(formatterRegistry); + view.render(); + assertTrue(renderCalled); + assertEquals("bar", model.get("foo")); + assertEquals("baz", model.get("bar")); + assertEquals("boop", model.get("baz")); + assertEquals("bing", model.get("boop")); + assertEquals("c1v1", model.get("flowExecutionKey")); + assertEquals("Keith", ((Principal) model.get("currentUser")).getName()); + assertEquals(context, model.get("flowRequestContext")); + assertEquals("/mockFlow?execution=c1v1", model.get("flowExecutionUrl")); + assertNull(model.get(BindingResult.MODEL_KEY_PREFIX + "bindBean")); + } + + private class MockView implements View { + + public String getContentType() { + return "text/html"; + } + + public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { + renderCalled = true; + ServletMvcViewTests.this.model = model; + } + + } + + public static class BindBean { + private String stringProperty; + private Integer integerProperty = new Integer(3); + private Date dateProperty; + + public BindBean() { + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(Calendar.YEAR, 2008); + dateProperty = cal.getTime(); + } + + public String getStringProperty() { + return stringProperty; + } + + public void setStringProperty(String stringProperty) { + this.stringProperty = stringProperty; + } + + public Integer getIntegerProperty() { + return integerProperty; + } + + public void setIntegerProperty(Integer integerProperty) { + this.integerProperty = integerProperty; + } + + public Date getDateProperty() { + return dateProperty; + } + + public void setDateProperty(Date dateProperty) { + this.dateProperty = dateProperty; + } + + } + +} diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewFactoryTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewFactoryTests.java index a688c30f..005c3316 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewFactoryTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewFactoryTests.java @@ -25,6 +25,7 @@ import org.springframework.web.servlet.ViewResolver; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.View; import org.springframework.webflow.execution.ViewFactory; +import org.springframework.webflow.mvc.view.MvcViewFactoryCreator; import org.springframework.webflow.test.GeneratedFlowExecutionKey; import org.springframework.webflow.test.MockExternalContext; import org.springframework.webflow.test.MockRequestContext; diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java index daec50d5..61b662fe 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java @@ -19,6 +19,10 @@ import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; import org.springframework.validation.BindingResult; import org.springframework.web.servlet.View; +import org.springframework.webflow.context.ExternalContext; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.mvc.view.BindingModel; +import org.springframework.webflow.mvc.view.MvcView; import org.springframework.webflow.test.MockFlowExecutionKey; import org.springframework.webflow.test.MockRequestContext; @@ -49,7 +53,7 @@ public class MvcViewTests extends TestCase { context.getMockExternalContext().setNativeResponse(new MockHttpServletResponse()); context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1")); org.springframework.web.servlet.View mvcView = new MockView(); - MvcView view = new MvcView(mvcView, context); + MvcView view = new MockMvcView(mvcView, context); view.setFormatterRegistry(formatterRegistry); view.render(); assertTrue(renderCalled); @@ -76,7 +80,7 @@ public class MvcViewTests extends TestCase { context.getMockExternalContext().setNativeResponse(new MockHttpServletResponse()); context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1")); org.springframework.web.servlet.View mvcView = new MockView(); - MvcView view = new MvcView(mvcView, context); + MvcView view = new MockMvcView(mvcView, context); view.setFormatterRegistry(formatterRegistry); view.render(); assertEquals(context.getFlowScope().get("bindBean"), model.get("bindBean")); @@ -94,7 +98,7 @@ public class MvcViewTests extends TestCase { context.getMockExternalContext().setNativeResponse(new MockHttpServletResponse()); context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1")); org.springframework.web.servlet.View mvcView = new MockView(); - MvcView view = new MvcView(mvcView, context); + MvcView view = new MockMvcView(mvcView, context); view.resume(); assertFalse(view.eventSignaled()); assertNull(view.getEvent()); @@ -108,7 +112,7 @@ public class MvcViewTests extends TestCase { context.getMockExternalContext().setNativeResponse(new MockHttpServletResponse()); context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1")); org.springframework.web.servlet.View mvcView = new MockView(); - MvcView view = new MvcView(mvcView, context); + MvcView view = new MockMvcView(mvcView, context); view.setFormatterRegistry(formatterRegistry); view.resume(); assertTrue(view.eventSignaled()); @@ -131,7 +135,7 @@ public class MvcViewTests extends TestCase { context.getMockExternalContext().setNativeResponse(new MockHttpServletResponse()); context.getMockFlowExecutionContext().setKey(new MockFlowExecutionKey("c1v1")); org.springframework.web.servlet.View mvcView = new MockView(); - MvcView view = new MvcView(mvcView, context); + MvcView view = new MockMvcView(mvcView, context); view.setFormatterRegistry(formatterRegistry); view.resume(); assertTrue(view.eventSignaled()); @@ -144,6 +148,19 @@ public class MvcViewTests extends TestCase { assertEquals(cal.getTime(), bindBean.getDateProperty()); } + private class MockMvcView extends MvcView { + + public MockMvcView(View view, RequestContext context) { + super(view, context); + } + + public void render(Map model, ExternalContext context) throws Exception { + getView().render(model, (HttpServletRequest) context.getNativeRequest(), + (HttpServletResponse) context.getNativeResponse()); + } + + } + private class MockView implements View { public String getContentType() {