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
This commit is contained in:
Scott Andrews
2008-04-01 23:37:40 +00:00
parent c579f1852b
commit 241abd4321
28 changed files with 678 additions and 48 deletions

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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());
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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());

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -64,8 +64,7 @@ public class PortletExternalContextTests extends TestCase {
}
public void testAjaxRequestAcceptHeader() {
context.setAjaxRequest(true);
assertTrue(context.isAjaxRequest());
assertFalse(context.isAjaxRequest());
}
public void testNotResponseCommitted() {

View File

@@ -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());
}
}

View File

@@ -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));
}
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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() {