DispatcherPortlet does not forward event exceptions to the render phase by default

Issue: SPR-9287
This commit is contained in:
Juergen Hoeller 2012-07-05 00:19:01 +02:00
parent 8bd1fd3715
commit 596571059e
3 changed files with 147 additions and 50 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -123,7 +123,7 @@ import org.springframework.web.servlet.ViewResolver;
* as loaded by {@link org.springframework.web.context.ContextLoaderListener}, * as loaded by {@link org.springframework.web.context.ContextLoaderListener},
* if any, will be shared. * if any, will be shared.
* *
* <p>Thanks to Rainer Schmitz and Nick Lothian for their suggestions! * <p>Thanks to Rainer Schmitz, Nick Lothian and Eric Dalquist for their suggestions!
* *
* @author William G. Thompson, Jr. * @author William G. Thompson, Jr.
* @author John A. Lewis * @author John A. Lewis
@ -241,6 +241,12 @@ public class DispatcherPortlet extends FrameworkPortlet {
/** Detect all ViewResolvers or just expect "viewResolver" bean? */ /** Detect all ViewResolvers or just expect "viewResolver" bean? */
private boolean detectAllViewResolvers = true; private boolean detectAllViewResolvers = true;
/** Whether exceptions thrown during doAction should be forwarded to doRender */
private boolean forwardActionException = true;
/** Whether exceptions thrown during doEvent should be forwarded to doRender */
private boolean forwardEventException = false;
/** URL that points to the ViewRendererServlet */ /** URL that points to the ViewRendererServlet */
private String viewRendererUrl = DEFAULT_VIEW_RENDERER_URL; private String viewRendererUrl = DEFAULT_VIEW_RENDERER_URL;
@ -305,6 +311,28 @@ public class DispatcherPortlet extends FrameworkPortlet {
this.detectAllViewResolvers = detectAllViewResolvers; this.detectAllViewResolvers = detectAllViewResolvers;
} }
/**
* Set whether to forward exceptions thrown during the action phase
* to the render phase via a session attribute.
* <p>Default is true. Turn this off if you want the portlet container
* to provide immediate exception handling for action requests.
* @see #exposeActionException(javax.portlet.PortletRequest, javax.portlet.StateAwareResponse, Exception)
*/
public void setForwardActionException(boolean forwardActionException) {
this.forwardActionException = forwardActionException;
}
/**
* Set whether to forward exceptions thrown during the event phase
* to the render phase via a session attribute.
* <p>Default is false. Turn this on if you want the {@link DispatcherPortlet}
* to forward the exception to the render phase, similar to what it does
* for {@link #setForwardActionException action exceptions} by default.
*/
public void setForwardEventException(boolean forwardEventException) {
this.forwardEventException = forwardEventException;
}
/** /**
* Set the URL to the ViewRendererServlet. That servlet is used to * Set the URL to the ViewRendererServlet. That servlet is used to
* ultimately render all views in the portlet application. * ultimately render all views in the portlet application.
@ -648,6 +676,7 @@ public class DispatcherPortlet extends FrameworkPortlet {
// Trigger after-completion for thrown exception. // Trigger after-completion for thrown exception.
triggerAfterActionCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); triggerAfterActionCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
// Forward the exception to the render phase to be displayed. // Forward the exception to the render phase to be displayed.
if (this.forwardActionException) {
try { try {
exposeActionException(request, response, ex); exposeActionException(request, response, ex);
logger.debug("Caught exception during action phase - forwarding to render phase", ex); logger.debug("Caught exception during action phase - forwarding to render phase", ex);
@ -657,6 +686,10 @@ public class DispatcherPortlet extends FrameworkPortlet {
throw ex; throw ex;
} }
} }
else {
throw ex;
}
}
catch (Error err) { catch (Error err) {
PortletException ex = PortletException ex =
new PortletException("Error occured during request processing: " + err.getMessage(), err); new PortletException("Error occured during request processing: " + err.getMessage(), err);
@ -921,6 +954,7 @@ public class DispatcherPortlet extends FrameworkPortlet {
// Trigger after-completion for thrown exception. // Trigger after-completion for thrown exception.
triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, ex); triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, ex);
// Forward the exception to the render phase to be displayed. // Forward the exception to the render phase to be displayed.
if (this.forwardEventException) {
try { try {
exposeActionException(request, response, ex); exposeActionException(request, response, ex);
logger.debug("Caught exception during event phase - forwarding to render phase", ex); logger.debug("Caught exception during event phase - forwarding to render phase", ex);
@ -930,6 +964,10 @@ public class DispatcherPortlet extends FrameworkPortlet {
throw ex; throw ex;
} }
} }
else {
throw ex;
}
}
catch (Error err) { catch (Error err) {
PortletException ex = PortletException ex =
new PortletException("Error occured during request processing: " + err.getMessage(), err); new PortletException("Error occured during request processing: " + err.getMessage(), err);
@ -963,7 +1001,7 @@ public class DispatcherPortlet extends FrameworkPortlet {
* Return the HandlerExecutionChain for this request. * Return the HandlerExecutionChain for this request.
* Try all handler mappings in order. * Try all handler mappings in order.
* @param request current portlet request * @param request current portlet request
* @return the HandlerExceutionChain, or null if no handler could be found * @return the HandlerExecutionChain, or null if no handler could be found
*/ */
protected HandlerExecutionChain getHandler(PortletRequest request) throws Exception { protected HandlerExecutionChain getHandler(PortletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) { for (HandlerMapping hm : this.handlerMappings) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2008 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,7 +24,6 @@ import javax.portlet.PortletException;
import javax.portlet.PortletMode; import javax.portlet.PortletMode;
import javax.portlet.PortletSecurityException; import javax.portlet.PortletSecurityException;
import javax.portlet.PortletSession; import javax.portlet.PortletSession;
import javax.portlet.UnavailableException;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -35,6 +34,9 @@ import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.mock.web.portlet.MockActionRequest; import org.springframework.mock.web.portlet.MockActionRequest;
import org.springframework.mock.web.portlet.MockActionResponse; import org.springframework.mock.web.portlet.MockActionResponse;
import org.springframework.mock.web.portlet.MockEvent;
import org.springframework.mock.web.portlet.MockEventRequest;
import org.springframework.mock.web.portlet.MockEventResponse;
import org.springframework.mock.web.portlet.MockPortletConfig; import org.springframework.mock.web.portlet.MockPortletConfig;
import org.springframework.mock.web.portlet.MockPortletContext; import org.springframework.mock.web.portlet.MockPortletContext;
import org.springframework.mock.web.portlet.MockPortletSession; import org.springframework.mock.web.portlet.MockPortletSession;
@ -139,6 +141,56 @@ public class DispatcherPortletTests extends TestCase {
assertTrue(exceptionParam.startsWith(NoHandlerFoundException.class.getName())); assertTrue(exceptionParam.startsWith(NoHandlerFoundException.class.getName()));
} }
public void testSimpleInvalidActionRequestWithoutHandling() throws Exception {
MockActionRequest request = new MockActionRequest();
MockActionResponse response = new MockActionResponse();
request.setParameter("action", "invalid");
simpleDispatcherPortlet.setForwardActionException(false);
try {
simpleDispatcherPortlet.processAction(request, response);
fail("Should have thrown a " + NoHandlerFoundException.class);
}
catch (NoHandlerFoundException ex) {
// expected
}
}
public void testSimpleValidEventRequest() throws Exception {
MockEvent event = new MockEvent("test-event");
MockEventRequest request = new MockEventRequest(event);
MockEventResponse response = new MockEventResponse();
request.setParameter("action", "form");
simpleDispatcherPortlet.processEvent(request, response);
assertEquals("test-event", response.getRenderParameter("event"));
}
public void testSimpleInvalidEventRequest() throws Exception {
MockEvent event = new MockEvent("test-event");
MockEventRequest request = new MockEventRequest(event);
MockEventResponse response = new MockEventResponse();
request.setParameter("action", "invalid");
try {
simpleDispatcherPortlet.processEvent(request, response);
fail("Should have thrown a " + NoHandlerFoundException.class);
}
catch (NoHandlerFoundException ex) {
// expected
}
}
public void testSimpleInvalidEventRequestWithHandling() throws Exception {
MockEvent event = new MockEvent("event");
MockEventRequest request = new MockEventRequest(event);
MockEventResponse response = new MockEventResponse();
request.setParameter("action", "invalid");
simpleDispatcherPortlet.setForwardEventException(true);
simpleDispatcherPortlet.processEvent(request, response);
String exceptionParam = response.getRenderParameter(DispatcherPortlet.ACTION_EXCEPTION_RENDER_PARAMETER);
assertNotNull(exceptionParam);
assertTrue(exceptionParam.startsWith(NoHandlerFoundException.class.getName()));
}
public void testSimpleFormViewNoBindOnNewForm() throws Exception { public void testSimpleFormViewNoBindOnNewForm() throws Exception {
MockRenderRequest request = new MockRenderRequest(); MockRenderRequest request = new MockRenderRequest();
MockRenderResponse response = new MockRenderResponse(); MockRenderResponse response = new MockRenderResponse();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2007 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,6 +19,8 @@ package org.springframework.web.portlet;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.RenderRequest; import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse; import javax.portlet.RenderResponse;
@ -31,6 +33,7 @@ import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import org.springframework.web.portlet.context.StaticPortletApplicationContext; import org.springframework.web.portlet.context.StaticPortletApplicationContext;
import org.springframework.web.portlet.handler.ParameterHandlerMapping; import org.springframework.web.portlet.handler.ParameterHandlerMapping;
import org.springframework.web.portlet.mvc.EventAwareController;
import org.springframework.web.portlet.mvc.SimpleFormController; import org.springframework.web.portlet.mvc.SimpleFormController;
/** /**
@ -86,7 +89,7 @@ public class SimplePortletApplicationContext extends StaticPortletApplicationCon
} }
public static class TestFormController extends SimpleFormController { public static class TestFormController extends SimpleFormController implements EventAwareController {
TestFormController() { TestFormController() {
super(); super();
@ -124,6 +127,10 @@ public class SimplePortletApplicationContext extends StaticPortletApplicationCon
private void writeResponse(RenderResponse response, TestBean testBean, boolean finished) throws IOException { private void writeResponse(RenderResponse response, TestBean testBean, boolean finished) throws IOException {
response.getWriter().write((finished ? "finished" : "") + (testBean.getAge() + 5)); response.getWriter().write((finished ? "finished" : "") + (testBean.getAge() + 5));
} }
public void handleEventRequest(EventRequest request, EventResponse response) throws Exception {
response.setRenderParameter("event", request.getEvent().getName());
}
} }
} }