exception handling

defaults
This commit is contained in:
Keith Donald
2008-03-26 04:48:16 +00:00
parent ced7258332
commit f6b66e6e25
3 changed files with 141 additions and 25 deletions

View File

@@ -66,7 +66,7 @@ public interface FlowHandler {
/**
* Handles a flow exception that was not handled by the Web Flow system. Used by a Controller to handle a specific
* type of exception dealing with this flow in a custom manner.
* @param e the unhandled exception orignating from Spring Web Flow. May be thrown by the flow execution itself or
* @param e the unhandled exception originating from Spring Web Flow. May be thrown by the flow execution itself or
* the flow executor system if no execution could be restored.
* @param request the current request
* @param response the current response

View File

@@ -20,6 +20,7 @@ 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;
@@ -51,7 +52,26 @@ public interface FlowHandler {
*/
public MutableAttributeMap createExecutionInputMap(PortletRequest request);
public ModelAndView handleFlowOutcome(String endedOutcome, AttributeMap endedOutput, RenderRequest request,
/**
* Handles a specific flow execution outcome. Used to select a new view to render after the flow ends.
* @param outcome the outcome that was reached
* @param output the output returned by the flow execution
* @param request the current render request
* @param response the current render response
* @return the model and view to render on the occurrence of this outcome, or null if the outcome was not handled
*/
public ModelAndView handleFlowOutcome(String outcome, AttributeMap output, RenderRequest request,
RenderResponse response);
/**
* Handles a flow exception that was not handled by the Web Flow system. Used by a Controller to handle a specific
* type of exception dealing with this flow in a custom manner.
* @param e the unhandled exception originating from Spring Web Flow. May be thrown by the flow execution itself or
* 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
*/
public ModelAndView handleException(FlowException e, RenderRequest request, RenderResponse response);
}

View File

@@ -1,5 +1,9 @@
package org.springframework.webflow.portlet;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletRequest;
@@ -13,13 +17,19 @@ import org.springframework.web.portlet.ModelAndView;
import org.springframework.web.portlet.context.PortletApplicationObjectSupport;
import org.springframework.webflow.context.portlet.FlowUrlHandler;
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.repository.NoSuchFlowExecutionException;
import org.springframework.webflow.executor.FlowExecutionResult;
import org.springframework.webflow.executor.FlowExecutor;
public class FlowHandlerAdapter extends PortletApplicationObjectSupport implements HandlerAdapter {
private static final String FLOW_EXECUTION_RESULT = "flowExecutionResult";
private static final String ACTION_FLOW_EXCEPTION_ATTRIBUTE = "actionFlowException";
private static final String FLOW_EXECUTION_RESULT_ATTRIBUTE = "flowExecutionResult";
private FlowExecutor flowExecutor;
@@ -29,25 +39,46 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen
this.flowExecutor = flowExecutor;
}
public boolean supports(Object handler) {
return handler instanceof FlowHandler;
}
public ModelAndView handleRender(RenderRequest request, RenderResponse response, Object handler) throws Exception {
FlowHandler flowHandler = (FlowHandler) handler;
PortletSession session = request.getPortletSession(false);
if (session != null) {
FlowException e = (FlowException) session.getAttribute(ACTION_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);
}
}
String flowExecutionKey = urlHandler.getFlowExecutionKey(request);
if (flowExecutionKey != null) {
PortletExternalContext context = createPortletExternalContext(request, response);
flowExecutor.resumeExecution(flowExecutionKey, context);
return null;
} else {
PortletSession session = request.getPortletSession(true);
FlowExecutionResult result = (FlowExecutionResult) session.getAttribute(FLOW_EXECUTION_RESULT);
if (result != null) {
session.removeAttribute(FLOW_EXECUTION_RESULT);
return flowHandler.handleFlowOutcome(result.getEndedOutcome(), result.getEndedOutput(), request,
response);
} else {
MutableAttributeMap input = flowHandler.createExecutionInputMap(request);
PortletExternalContext context = createPortletExternalContext(request, response);
flowExecutor.launchExecution(flowHandler.getFlowId(), input, context);
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);
}
} else {
if (session != null) {
FlowExecutionResult result = (FlowExecutionResult) session
.getAttribute(FLOW_EXECUTION_RESULT_ATTRIBUTE);
if (result != null) {
session.removeAttribute(FLOW_EXECUTION_RESULT_ATTRIBUTE);
String outcome = result.getEndedOutcome();
AttributeMap output = result.getEndedOutput();
ModelAndView mv = flowHandler.handleFlowOutcome(outcome, output, request, response);
return mv != null ? mv : defaultHandleFlowOutcome(flowHandler, outcome, output, request, response);
} else {
return startFlow(request, response, flowHandler);
}
} else {
return startFlow(request, response, flowHandler);
}
}
}
@@ -55,20 +86,85 @@ public class FlowHandlerAdapter extends PortletApplicationObjectSupport implemen
public void handleAction(ActionRequest request, ActionResponse response, Object handler) throws Exception {
String flowExecutionKey = urlHandler.getFlowExecutionKey(request);
PortletExternalContext context = createPortletExternalContext(request, response);
FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context);
if (result.paused()) {
urlHandler.setFlowExecutionRenderParameter(flowExecutionKey, response);
try {
FlowExecutionResult result = flowExecutor.resumeExecution(flowExecutionKey, context);
if (result.paused()) {
urlHandler.setFlowExecutionRenderParameter(flowExecutionKey, response);
} else {
setEndResult(result, request);
}
} catch (FlowException e) {
request.getPortletSession().setAttribute(ACTION_FLOW_EXCEPTION_ATTRIBUTE, e);
}
}
// subclassing hooks
protected MutableAttributeMap defaultFlowExecutionInputMap(PortletRequest request) {
LocalAttributeMap inputMap = new LocalAttributeMap();
Map parameterMap = request.getParameterMap();
Iterator it = parameterMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String name = (String) entry.getKey();
String[] values = (String[]) entry.getValue();
if (values.length == 1) {
inputMap.put(name, values[0]);
} else {
inputMap.put(name, values);
}
}
return inputMap;
}
protected ModelAndView defaultHandleFlowOutcome(FlowHandler flowHandler, String outcome, AttributeMap output,
RenderRequest request, RenderResponse response) throws IOException {
// by default, just start the flow over passing the output as input
String flowId = flowHandler.getFlowId();
if (logger.isDebugEnabled()) {
logger.debug("Restarting a new execution of ended flow '" + flowId + "'");
}
PortletExternalContext context = createPortletExternalContext(request, response);
flowExecutor.launchExecution(flowId, new LocalAttributeMap(output.asMap()), context);
return null;
}
protected ModelAndView defaultHandleResumeFlowException(FlowHandler flowHandler, FlowException e,
RenderRequest request, RenderResponse response) throws IOException {
if (e instanceof NoSuchFlowExecutionException) {
String flowId = flowHandler.getFlowId();
if (logger.isDebugEnabled()) {
logger.debug("Restarting a new execution of previously expired/ended flow '" + flowId + "'");
}
// by default, attempt to restart the flow
PortletExternalContext context = createPortletExternalContext(request, response);
flowExecutor.launchExecution(flowId, null, context);
return null;
} else {
setEndResult(result, request);
throw e;
}
}
// helpers
private ModelAndView startFlow(RenderRequest request, RenderResponse response, FlowHandler flowHandler) {
MutableAttributeMap input = flowHandler.createExecutionInputMap(request);
PortletExternalContext context = createPortletExternalContext(request, response);
try {
flowExecutor.launchExecution(flowHandler.getFlowId(), input, context);
return null;
} catch (FlowException e) {
ModelAndView mv = flowHandler.handleException(e, request, response);
if (mv != null) {
return mv;
} else {
throw e;
}
}
}
private void setEndResult(FlowExecutionResult result, ActionRequest request) {
request.getPortletSession().setAttribute(FLOW_EXECUTION_RESULT, result);
}
public boolean supports(Object handler) {
return handler instanceof FlowHandler;
request.getPortletSession().setAttribute(FLOW_EXECUTION_RESULT_ATTRIBUTE, result);
}
private PortletExternalContext createPortletExternalContext(PortletRequest request, PortletResponse response) {