portlet refinements

This commit is contained in:
Keith Donald
2008-04-23 22:58:04 +00:00
parent 36670d5f0c
commit 89242f19fc
4 changed files with 103 additions and 98 deletions

View File

@@ -15,11 +15,11 @@
*/
package org.springframework.webflow.mvc.portlet;
import javax.portlet.PortletRequest;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
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.MutableAttributeMap;
import org.springframework.webflow.execution.FlowExecutionOutcome;
@@ -32,19 +32,19 @@ import org.springframework.webflow.execution.FlowExecutionOutcome;
*/
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) {
public MutableAttributeMap createExecutionInputMap(RenderRequest request) {
return null;
}
public String handleFlowOutcome(FlowExecutionOutcome outcome, RenderRequest request, RenderResponse response) {
public boolean handleExecutionOutcome(FlowExecutionOutcome outcome, ActionRequest request, ActionResponse response) {
return false;
}
public String handleException(FlowException e, RenderRequest request, RenderResponse response) {
return null;
}

View File

@@ -15,11 +15,12 @@
*/
package org.springframework.webflow.mvc.portlet;
import javax.portlet.PortletRequest;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletModeException;
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.MutableAttributeMap;
import org.springframework.webflow.execution.FlowExecutionOutcome;
@@ -50,17 +51,18 @@ public interface FlowHandler {
* @param request the current request
* @return the input map
*/
public MutableAttributeMap createExecutionInputMap(PortletRequest request);
public MutableAttributeMap createExecutionInputMap(RenderRequest request);
/**
* Handles a specific flow execution outcome. Used to select a new view to render after the flow ends.
* Handles a specific flow execution outcome. Used to change portlet modes after the flow ends.
* @param outcome the outcome that was reached
* @param request the current render request
* @param response the current render response
* @return the id of the flow to start after handling the outcome, or null if the outcome should be handled by the
* caller
* @return whether this outcome was handled, or whether the caller should handle it
* @throws PortletModeException if this handler tries to change the portlet mode to something invalid
*/
public String handleFlowOutcome(FlowExecutionOutcome outcome, RenderRequest request, RenderResponse response);
public boolean handleExecutionOutcome(FlowExecutionOutcome outcome, ActionRequest request, ActionResponse response)
throws PortletModeException;
/**
* Handles a flow exception that was not handled by the Web Flow system. Used by a Controller to handle a specific
@@ -69,8 +71,8 @@ public interface FlowHandler {
* the flow executor system if no execution could be restored.
* @param request the current request
* @param response the current response
* @return the model and view to render on the occurrence of this exception, or null if the exception is not handled
* @return the name of a specific error view to render, or null if the exception should be handled by the caller
*/
public ModelAndView handleException(FlowException e, RenderRequest request, RenderResponse response);
public String handleException(FlowException e, RenderRequest request, RenderResponse response);
}

View File

@@ -21,6 +21,7 @@ import java.util.Map;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletModeException;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.PortletSession;
@@ -43,9 +44,7 @@ import org.springframework.webflow.executor.FlowExecutor;
public class FlowHandlerAdapter extends PortletApplicationObjectSupport implements HandlerAdapter {
private static final String ACTION_FLOW_EXCEPTION_ATTRIBUTE = "actionFlowException";
private static final String FLOW_EXECUTION_RESULT_ATTRIBUTE = "flowExecutionResult";
private static final String ACTION_REQUEST_FLOW_EXCEPTION_ATTRIBUTE = "actionRequestFlowException";
private FlowExecutor flowExecutor;
@@ -65,41 +64,22 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen
populateConveniencePortletProperties(request);
PortletSession session = request.getPortletSession(false);
if (session != null) {
FlowException e = (FlowException) session.getAttribute(ACTION_FLOW_EXCEPTION_ATTRIBUTE);
FlowException e = (FlowException) session.getAttribute(ACTION_REQUEST_FLOW_EXCEPTION_ATTRIBUTE);
if (e != null) {
session.removeAttribute(ACTION_FLOW_EXCEPTION_ATTRIBUTE);
ModelAndView mv = flowHandler.handleException(e, request, response);
return mv != null ? mv : defaultHandleResumeFlowException(flowHandler, e, request, response);
session.removeAttribute(ACTION_REQUEST_FLOW_EXCEPTION_ATTRIBUTE);
return handleException(e, flowHandler, request, response);
}
}
String flowExecutionKey = urlHandler.getFlowExecutionKey(request);
if (flowExecutionKey != null) {
PortletExternalContext context = createPortletExternalContext(request, response);
try {
flowExecutor.resumeExecution(flowExecutionKey, context);
return null;
} catch (FlowException e) {
ModelAndView mv = flowHandler.handleException(e, request, response);
return mv != null ? mv : defaultHandleResumeFlowException(flowHandler, e, request, response);
}
return resumeFlow(flowExecutionKey, flowHandler, request, response);
} else {
if (session != null) {
FlowExecutionResult result = (FlowExecutionResult) session
.getAttribute(FLOW_EXECUTION_RESULT_ATTRIBUTE);
if (result != null) {
session.removeAttribute(FLOW_EXECUTION_RESULT_ATTRIBUTE);
String flowId = flowHandler.handleFlowOutcome(result.getOutcome(), request, response);
return defaultHandleFlowOutcome(flowHandler, result.getOutcome(), flowId, request, response);
} else {
return startFlow(request, response, flowHandler);
}
} else {
return startFlow(request, response, flowHandler);
}
return startFlow(flowHandler, request, response);
}
}
public void handleAction(ActionRequest request, ActionResponse response, Object handler) throws Exception {
FlowHandler flowHandler = (FlowHandler) handler;
populateConveniencePortletProperties(request);
String flowExecutionKey = urlHandler.getFlowExecutionKey(request);
PortletExternalContext context = createPortletExternalContext(request, response);
@@ -107,11 +87,13 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen
FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context);
if (result.isPaused()) {
urlHandler.setFlowExecutionRenderParameter(result.getPausedKey(), response);
} else if (result.isEnded()) {
handleFlowExecutionOutcome(result.getOutcome(), flowHandler, request, response);
} else {
request.getPortletSession().setAttribute(FLOW_EXECUTION_RESULT_ATTRIBUTE, result);
throw new IllegalStateException("Execution result should have been one of [paused] or [ended]");
}
} catch (FlowException e) {
request.getPortletSession().setAttribute(ACTION_FLOW_EXCEPTION_ATTRIBUTE, e);
request.getPortletSession().setAttribute(ACTION_REQUEST_FLOW_EXCEPTION_ATTRIBUTE, e);
}
}
@@ -126,7 +108,7 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen
return new PortletExternalContext(getPortletContext(), request, response);
}
protected MutableAttributeMap defaultFlowExecutionInputMap(PortletRequest request) {
protected MutableAttributeMap defaultCreateFlowExecutionInputMap(PortletRequest request) {
LocalAttributeMap inputMap = new LocalAttributeMap();
Map parameterMap = request.getParameterMap();
Iterator it = parameterMap.entrySet().iterator();
@@ -143,29 +125,19 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen
return inputMap;
}
protected ModelAndView defaultHandleFlowOutcome(FlowHandler flowHandler, FlowExecutionOutcome outcome,
String nextFlowId, RenderRequest request, RenderResponse response) throws IOException {
if (nextFlowId == null) {
nextFlowId = flowHandler.getFlowId();
}
if (logger.isDebugEnabled()) {
logger.debug("Starting a new execution of flow '" + nextFlowId + "'");
}
PortletExternalContext context = createPortletExternalContext(request, response);
flowExecutor.launchExecution(nextFlowId, new LocalAttributeMap(outcome.getOutput().asMap()), context);
return null;
protected void defaultHandleExecutionOutcome(FlowExecutionOutcome outcome, FlowHandler flowHandler,
ActionRequest request, ActionResponse response) throws PortletModeException {
}
protected ModelAndView defaultHandleResumeFlowException(FlowHandler flowHandler, FlowException e,
RenderRequest request, RenderResponse response) throws IOException {
protected ModelAndView defaultHandleException(FlowHandler flowHandler, FlowException e, RenderRequest request,
RenderResponse response) {
if (e instanceof NoSuchFlowExecutionException) {
String flowId = flowHandler.getFlowId();
if (logger.isDebugEnabled()) {
logger.debug("Restarting a new execution of previously expired/ended flow '" + flowId + "'");
logger.debug("Restarting a new execution of previously expired/ended flow '" + flowHandler.getFlowId()
+ "'");
}
// by default, attempt to restart the flow
PortletExternalContext context = createPortletExternalContext(request, response);
flowExecutor.launchExecution(flowId, null, context);
startFlow(flowHandler, null, request, response);
return null;
} else {
throw e;
@@ -174,12 +146,34 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen
// helpers
private ModelAndView startFlow(RenderRequest request, RenderResponse response, FlowHandler flowHandler)
throws Exception {
private ModelAndView handleException(FlowException e, FlowHandler flowHandler, RenderRequest request,
RenderResponse response) {
String viewName = flowHandler.handleException(e, request, response);
if (viewName != null) {
return new ModelAndView(viewName);
} else {
return defaultHandleException(flowHandler, e, request, response);
}
}
private void handleFlowExecutionOutcome(FlowExecutionOutcome outcome, FlowHandler flowHandler,
ActionRequest request, ActionResponse response) throws PortletModeException {
boolean handled = flowHandler.handleExecutionOutcome(outcome, request, response);
if (!handled) {
defaultHandleExecutionOutcome(outcome, flowHandler, request, response);
}
}
private ModelAndView startFlow(FlowHandler flowHandler, RenderRequest request, RenderResponse response) {
MutableAttributeMap input = flowHandler.createExecutionInputMap(request);
if (input == null) {
input = defaultFlowExecutionInputMap(request);
input = defaultCreateFlowExecutionInputMap(request);
}
return startFlow(flowHandler, input, request, response);
}
private ModelAndView startFlow(FlowHandler flowHandler, MutableAttributeMap input, RenderRequest request,
RenderResponse response) {
PortletExternalContext context = createPortletExternalContext(request, response);
try {
FlowExecutionResult result = flowExecutor.launchExecution(flowHandler.getFlowId(), input, context);
@@ -188,12 +182,19 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen
}
return null;
} catch (FlowException e) {
ModelAndView mv = flowHandler.handleException(e, request, response);
if (mv != null) {
return mv;
} else {
throw e;
}
return handleException(e, flowHandler, request, response);
}
}
}
private ModelAndView resumeFlow(String flowExecutionKey, FlowHandler flowHandler, RenderRequest request,
RenderResponse response) throws IOException {
PortletExternalContext context = createPortletExternalContext(request, response);
try {
flowExecutor.resumeExecution(flowExecutionKey, context);
return null;
} catch (FlowException e) {
return handleException(e, flowHandler, request, response);
}
}
}

View File

@@ -1,6 +1,7 @@
package org.springframework.webflow.mvc.portlet;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.PortletSession;
@@ -43,6 +44,7 @@ public class FlowHandlerAdapterTests extends TestCase {
private LocalAttributeMap flowInput = new LocalAttributeMap();
private boolean handleException;
private boolean handleExecutionOutcome;
private boolean handleExecutionOutcomeCalled;
protected void setUp() {
executor = (FlowExecutor) EasyMock.createMock(FlowExecutor.class);
@@ -68,25 +70,27 @@ public class FlowHandlerAdapterTests extends TestCase {
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");
public MutableAttributeMap createExecutionInputMap(RenderRequest request) {
return null;
}
public boolean handleExecutionOutcome(FlowExecutionOutcome outcome, ActionRequest request,
ActionResponse response) {
handleExecutionOutcomeCalled = true;
if (handleExecutionOutcome) {
return true;
} else {
return null;
return false;
}
}
public String handleFlowOutcome(FlowExecutionOutcome outcome, RenderRequest request, RenderResponse response) {
if (handleExecutionOutcome) {
return "home";
public String handleException(FlowException e, RenderRequest request, RenderResponse response) {
if (handleException) {
return "error";
} else {
return null;
}
@@ -144,7 +148,7 @@ public class FlowHandlerAdapterTests extends TestCase {
PortletSession session = renderRequest.getPortletSession();
final FlowException flowException = new FlowException("Error") {
};
session.setAttribute("actionFlowException", flowException);
session.setAttribute("actionRequestFlowException", flowException);
try {
controller.handleRender(renderRequest, renderResponse, flowHandler);
fail("Should have thrown exception");
@@ -161,28 +165,25 @@ public class FlowHandlerAdapterTests extends TestCase {
EasyMock.expectLastCall().andThrow(flowException);
EasyMock.replay(new Object[] { executor });
controller.handleAction(actionRequest, actionResponse, flowHandler);
assertNotNull(actionRequest.getPortletSession().getAttribute("actionFlowException"));
assertNotNull(actionRequest.getPortletSession().getAttribute("actionRequestFlowException"));
EasyMock.verify(new Object[] { executor });
Exception e = (Exception) actionRequest.getPortletSession().getAttribute("actionFlowException");
Exception e = (Exception) actionRequest.getPortletSession().getAttribute("actionRequestFlowException");
assertTrue(e instanceof NoSuchFlowExecutionException);
}
public void testHandleFlowOutcomeCustomFlowHandler() throws Exception {
handleExecutionOutcome = true;
renderRequest.setContextPath("/springtravel");
actionRequest.setContextPath("/springtravel");
actionRequest.addParameter("execution", "12345");
LocalAttributeMap output = new LocalAttributeMap();
output.put("bar", "baz");
FlowExecutionOutcome outcome = new FlowExecutionOutcome("finish", output);
FlowExecutionResult result = FlowExecutionResult.createEndedResult("foo", outcome);
PortletSession session = renderRequest.getPortletSession();
session.setAttribute("flowExecutionResult", result);
executor.launchExecution("home", output, renderContext);
executor.resumeExecution("12345", actionContext);
EasyMock.expectLastCall().andReturn(FlowExecutionResult.createEndedResult("bar", outcome));
EasyMock.replay(new Object[] { executor });
ModelAndView mv = controller.handleRender(renderRequest, renderResponse, flowHandler);
assertNull(mv);
controller.handleAction(actionRequest, actionResponse, flowHandler);
assertTrue(handleExecutionOutcomeCalled);
EasyMock.verify(new Object[] { executor });
}
public void testHandleFlowExceptionCustomFlowHandler() throws Exception {
@@ -195,6 +196,7 @@ public class FlowHandlerAdapterTests extends TestCase {
EasyMock.replay(new Object[] { executor });
ModelAndView mv = controller.handleRender(renderRequest, renderResponse, flowHandler);
assertNotNull(mv);
assertEquals("error", mv.getViewName());
EasyMock.verify(new Object[] { executor });
}
@@ -203,7 +205,7 @@ public class FlowHandlerAdapterTests extends TestCase {
PortletSession session = renderRequest.getPortletSession();
final FlowException flowException = new FlowException("Error") {
};
session.setAttribute("actionFlowException", flowException);
session.setAttribute("actionRequestFlowException", flowException);
ModelAndView mv = controller.handleRender(renderRequest, renderResponse, flowHandler);
assertEquals("error", mv.getViewName());
}