Skip to content

Commit 5965710

Browse files
committed
DispatcherPortlet does not forward event exceptions to the render phase by default
Issue: SPR-9287
1 parent 8bd1fd3 commit 5965710

File tree

3 files changed

+147
-50
lines changed

3 files changed

+147
-50
lines changed

spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.java

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2010 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -123,7 +123,7 @@
123123
* as loaded by {@link org.springframework.web.context.ContextLoaderListener},
124124
* if any, will be shared.
125125
*
126-
* <p>Thanks to Rainer Schmitz and Nick Lothian for their suggestions!
126+
* <p>Thanks to Rainer Schmitz, Nick Lothian and Eric Dalquist for their suggestions!
127127
*
128128
* @author William G. Thompson, Jr.
129129
* @author John A. Lewis
@@ -241,6 +241,12 @@ public class DispatcherPortlet extends FrameworkPortlet {
241241
/** Detect all ViewResolvers or just expect "viewResolver" bean? */
242242
private boolean detectAllViewResolvers = true;
243243

244+
/** Whether exceptions thrown during doAction should be forwarded to doRender */
245+
private boolean forwardActionException = true;
246+
247+
/** Whether exceptions thrown during doEvent should be forwarded to doRender */
248+
private boolean forwardEventException = false;
249+
244250
/** URL that points to the ViewRendererServlet */
245251
private String viewRendererUrl = DEFAULT_VIEW_RENDERER_URL;
246252

@@ -305,6 +311,28 @@ public void setDetectAllViewResolvers(boolean detectAllViewResolvers) {
305311
this.detectAllViewResolvers = detectAllViewResolvers;
306312
}
307313

314+
/**
315+
* Set whether to forward exceptions thrown during the action phase
316+
* to the render phase via a session attribute.
317+
* <p>Default is true. Turn this off if you want the portlet container
318+
* to provide immediate exception handling for action requests.
319+
* @see #exposeActionException(javax.portlet.PortletRequest, javax.portlet.StateAwareResponse, Exception)
320+
*/
321+
public void setForwardActionException(boolean forwardActionException) {
322+
this.forwardActionException = forwardActionException;
323+
}
324+
325+
/**
326+
* Set whether to forward exceptions thrown during the event phase
327+
* to the render phase via a session attribute.
328+
* <p>Default is false. Turn this on if you want the {@link DispatcherPortlet}
329+
* to forward the exception to the render phase, similar to what it does
330+
* for {@link #setForwardActionException action exceptions} by default.
331+
*/
332+
public void setForwardEventException(boolean forwardEventException) {
333+
this.forwardEventException = forwardEventException;
334+
}
335+
308336
/**
309337
* Set the URL to the ViewRendererServlet. That servlet is used to
310338
* ultimately render all views in the portlet application.
@@ -648,12 +676,17 @@ protected void doActionService(ActionRequest request, ActionResponse response) t
648676
// Trigger after-completion for thrown exception.
649677
triggerAfterActionCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
650678
// Forward the exception to the render phase to be displayed.
651-
try {
652-
exposeActionException(request, response, ex);
653-
logger.debug("Caught exception during action phase - forwarding to render phase", ex);
679+
if (this.forwardActionException) {
680+
try {
681+
exposeActionException(request, response, ex);
682+
logger.debug("Caught exception during action phase - forwarding to render phase", ex);
683+
}
684+
catch (IllegalStateException ex2) {
685+
// Probably sendRedirect called... need to rethrow exception immediately.
686+
throw ex;
687+
}
654688
}
655-
catch (IllegalStateException ex2) {
656-
// Probably sendRedirect called... need to rethrow exception immediately.
689+
else {
657690
throw ex;
658691
}
659692
}
@@ -921,12 +954,17 @@ protected void doEventService(EventRequest request, EventResponse response) thro
921954
// Trigger after-completion for thrown exception.
922955
triggerAfterEventCompletion(mappedHandler, interceptorIndex, request, response, ex);
923956
// Forward the exception to the render phase to be displayed.
924-
try {
925-
exposeActionException(request, response, ex);
926-
logger.debug("Caught exception during event phase - forwarding to render phase", ex);
957+
if (this.forwardEventException) {
958+
try {
959+
exposeActionException(request, response, ex);
960+
logger.debug("Caught exception during event phase - forwarding to render phase", ex);
961+
}
962+
catch (IllegalStateException ex2) {
963+
// Probably sendRedirect called... need to rethrow exception immediately.
964+
throw ex;
965+
}
927966
}
928-
catch (IllegalStateException ex2) {
929-
// Probably sendRedirect called... need to rethrow exception immediately.
967+
else {
930968
throw ex;
931969
}
932970
}
@@ -963,7 +1001,7 @@ protected ActionRequest checkMultipart(ActionRequest request) throws MultipartEx
9631001
* Return the HandlerExecutionChain for this request.
9641002
* Try all handler mappings in order.
9651003
* @param request current portlet request
966-
* @return the HandlerExceutionChain, or null if no handler could be found
1004+
* @return the HandlerExecutionChain, or null if no handler could be found
9671005
*/
9681006
protected HandlerExecutionChain getHandler(PortletRequest request) throws Exception {
9691007
for (HandlerMapping hm : this.handlerMappings) {

spring-webmvc-portlet/src/test/java/org/springframework/web/portlet/DispatcherPortletTests.java

Lines changed: 87 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
/*
2-
* Copyright 2002-2008 the original author or authors.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
2+
* Copyright 2002-2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
1616

1717
package org.springframework.web.portlet;
1818

@@ -24,7 +24,6 @@
2424
import javax.portlet.PortletMode;
2525
import javax.portlet.PortletSecurityException;
2626
import javax.portlet.PortletSession;
27-
import javax.portlet.UnavailableException;
2827

2928
import junit.framework.TestCase;
3029

@@ -35,6 +34,9 @@
3534
import org.springframework.context.i18n.LocaleContextHolder;
3635
import org.springframework.mock.web.portlet.MockActionRequest;
3736
import org.springframework.mock.web.portlet.MockActionResponse;
37+
import org.springframework.mock.web.portlet.MockEvent;
38+
import org.springframework.mock.web.portlet.MockEventRequest;
39+
import org.springframework.mock.web.portlet.MockEventResponse;
3840
import org.springframework.mock.web.portlet.MockPortletConfig;
3941
import org.springframework.mock.web.portlet.MockPortletContext;
4042
import org.springframework.mock.web.portlet.MockPortletSession;
@@ -106,7 +108,7 @@ public void testDispatcherPortlets() {
106108
(FrameworkPortlet.PORTLET_CONTEXT_PREFIX + "simple").equals(simpleDispatcherPortlet.getPortletContextAttributeName()));
107109
assertTrue("Context published",
108110
simpleDispatcherPortlet.getPortletApplicationContext() ==
109-
getPortletContext().getAttribute(FrameworkPortlet.PORTLET_CONTEXT_PREFIX + "simple"));
111+
getPortletContext().getAttribute(FrameworkPortlet.PORTLET_CONTEXT_PREFIX + "simple"));
110112

111113
assertTrue("Correct namespace", "test".equals(complexDispatcherPortlet.getNamespace()));
112114
assertTrue("Correct attribute",
@@ -139,6 +141,56 @@ public void testSimpleInvalidActionRequest() throws Exception {
139141
assertTrue(exceptionParam.startsWith(NoHandlerFoundException.class.getName()));
140142
}
141143

144+
145+
public void testSimpleInvalidActionRequestWithoutHandling() throws Exception {
146+
MockActionRequest request = new MockActionRequest();
147+
MockActionResponse response = new MockActionResponse();
148+
request.setParameter("action", "invalid");
149+
simpleDispatcherPortlet.setForwardActionException(false);
150+
try {
151+
simpleDispatcherPortlet.processAction(request, response);
152+
fail("Should have thrown a " + NoHandlerFoundException.class);
153+
}
154+
catch (NoHandlerFoundException ex) {
155+
// expected
156+
}
157+
}
158+
159+
public void testSimpleValidEventRequest() throws Exception {
160+
MockEvent event = new MockEvent("test-event");
161+
MockEventRequest request = new MockEventRequest(event);
162+
MockEventResponse response = new MockEventResponse();
163+
request.setParameter("action", "form");
164+
simpleDispatcherPortlet.processEvent(request, response);
165+
assertEquals("test-event", response.getRenderParameter("event"));
166+
}
167+
168+
public void testSimpleInvalidEventRequest() throws Exception {
169+
MockEvent event = new MockEvent("test-event");
170+
MockEventRequest request = new MockEventRequest(event);
171+
MockEventResponse response = new MockEventResponse();
172+
request.setParameter("action", "invalid");
173+
try {
174+
simpleDispatcherPortlet.processEvent(request, response);
175+
fail("Should have thrown a " + NoHandlerFoundException.class);
176+
}
177+
catch (NoHandlerFoundException ex) {
178+
// expected
179+
}
180+
}
181+
182+
public void testSimpleInvalidEventRequestWithHandling() throws Exception {
183+
MockEvent event = new MockEvent("event");
184+
MockEventRequest request = new MockEventRequest(event);
185+
MockEventResponse response = new MockEventResponse();
186+
request.setParameter("action", "invalid");
187+
simpleDispatcherPortlet.setForwardEventException(true);
188+
simpleDispatcherPortlet.processEvent(request, response);
189+
String exceptionParam = response.getRenderParameter(DispatcherPortlet.ACTION_EXCEPTION_RENDER_PARAMETER);
190+
assertNotNull(exceptionParam);
191+
assertTrue(exceptionParam.startsWith(NoHandlerFoundException.class.getName()));
192+
}
193+
142194
public void testSimpleFormViewNoBindOnNewForm() throws Exception {
143195
MockRenderRequest request = new MockRenderRequest();
144196
MockRenderResponse response = new MockRenderResponse();
@@ -202,7 +254,7 @@ public void testSimpleRequiredSessionFormWithoutSession() throws Exception {
202254
fail("Should have thrown PortletSessionRequiredException");
203255
}
204256
catch (PortletSessionRequiredException ex) {
205-
// expected
257+
// expected
206258
}
207259
}
208260

@@ -247,7 +299,7 @@ public void testSimpleInvalidRenderRequest() throws Exception {
247299
fail("Should have thrown UnavailableException");
248300
}
249301
catch (NoHandlerFoundException ex) {
250-
// expected
302+
// expected
251303
}
252304
}
253305

@@ -431,7 +483,7 @@ public void testExistingMultipartRequest() throws Exception {
431483
request.setPortletMode(PortletMode.EDIT);
432484
ComplexPortletApplicationContext.MockMultipartResolver multipartResolver =
433485
(ComplexPortletApplicationContext.MockMultipartResolver)
434-
complexDispatcherPortlet.getPortletApplicationContext().getBean("portletMultipartResolver");
486+
complexDispatcherPortlet.getPortletApplicationContext().getBean("portletMultipartResolver");
435487
MultipartActionRequest multipartRequest = multipartResolver.resolveMultipart(request);
436488
complexDispatcherPortlet.processAction(multipartRequest, response);
437489
multipartResolver.cleanupMultipart(multipartRequest);
@@ -455,7 +507,7 @@ public void testActionRequestHandledEvent() throws Exception {
455507
complexDispatcherPortlet.processAction(request, response);
456508
ComplexPortletApplicationContext.TestApplicationListener listener =
457509
(ComplexPortletApplicationContext.TestApplicationListener)
458-
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
510+
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
459511
assertEquals(1, listener.counter);
460512
}
461513

@@ -465,7 +517,7 @@ public void testRenderRequestHandledEvent() throws Exception {
465517
complexDispatcherPortlet.doDispatch(request, response);
466518
ComplexPortletApplicationContext.TestApplicationListener listener =
467519
(ComplexPortletApplicationContext.TestApplicationListener)
468-
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
520+
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
469521
assertEquals(1, listener.counter);
470522
}
471523

@@ -477,7 +529,7 @@ public void testPublishEventsOff() throws Exception {
477529
complexDispatcherPortlet.processAction(request, response);
478530
ComplexPortletApplicationContext.TestApplicationListener listener =
479531
(ComplexPortletApplicationContext.TestApplicationListener)
480-
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
532+
complexDispatcherPortlet.getPortletApplicationContext().getBean("testListener");
481533
assertEquals(0, listener.counter);
482534
}
483535

@@ -611,7 +663,7 @@ public void testPortletHandlerAdapterActionRequest() throws Exception {
611663
complexDispatcherPortlet.processAction(request, response);
612664
assertEquals("myPortlet action called", response.getRenderParameter("result"));
613665
ComplexPortletApplicationContext.MyPortlet myPortlet =
614-
(ComplexPortletApplicationContext.MyPortlet) complexDispatcherPortlet.getPortletApplicationContext().getBean("myPortlet");
666+
(ComplexPortletApplicationContext.MyPortlet) complexDispatcherPortlet.getPortletApplicationContext().getBean("myPortlet");
615667
assertEquals("complex", myPortlet.getPortletConfig().getPortletName());
616668
assertEquals(getPortletContext(), myPortlet.getPortletConfig().getPortletContext());
617669
assertEquals(complexDispatcherPortlet.getPortletContext(), myPortlet.getPortletConfig().getPortletContext());
@@ -626,12 +678,12 @@ public void testPortletHandlerAdapterRenderRequest() throws Exception {
626678
complexDispatcherPortlet.doDispatch(request, response);
627679
assertEquals("myPortlet was here", response.getContentAsString());
628680
ComplexPortletApplicationContext.MyPortlet myPortlet =
629-
(ComplexPortletApplicationContext.MyPortlet)
630-
complexDispatcherPortlet.getPortletApplicationContext().getBean("myPortlet");
681+
(ComplexPortletApplicationContext.MyPortlet)
682+
complexDispatcherPortlet.getPortletApplicationContext().getBean("myPortlet");
631683
assertEquals("complex", myPortlet.getPortletConfig().getPortletName());
632684
assertEquals(getPortletContext(), myPortlet.getPortletConfig().getPortletContext());
633685
assertEquals(complexDispatcherPortlet.getPortletContext(),
634-
myPortlet.getPortletConfig().getPortletContext());
686+
myPortlet.getPortletConfig().getPortletContext());
635687
complexDispatcherPortlet.destroy();
636688
assertNull(myPortlet.getPortletConfig());
637689
}
@@ -769,18 +821,18 @@ public void testRuntimeExceptionInUnmappedHandler() throws Exception {
769821
}
770822

771823
public void testGetMessage() {
772-
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test", null, Locale.ENGLISH);
824+
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test", null, Locale.ENGLISH);
773825
assertEquals("test message", message);
774826
}
775827

776828
public void testGetMessageOtherLocale() {
777-
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test", null, Locale.CANADA);
829+
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test", null, Locale.CANADA);
778830
assertEquals("Canadian & test message", message);
779831
}
780832

781833
public void testGetMessageWithArgs() {
782834
Object[] args = new String[] {"this", "that"};
783-
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test.args", args, Locale.ENGLISH);
835+
String message = complexDispatcherPortlet.getPortletApplicationContext().getMessage("test.args", args, Locale.ENGLISH);
784836
assertEquals("test this and that", message);
785837
}
786838

@@ -793,7 +845,7 @@ public void testPortletApplicationContextLookup() {
793845
fail("Should have thrown IllegalStateException");
794846
}
795847
catch (IllegalStateException ex) {
796-
// expected
848+
// expected
797849
}
798850
portletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
799851
new StaticWebApplicationContext());
@@ -813,7 +865,7 @@ public void testValidActionRequestWithExistingThreadLocalRequestContext() throws
813865
request.setParameter("action", "form");
814866
request.setParameter("age", "29");
815867

816-
// see RequestContextListener.requestInitialized()
868+
// see RequestContextListener.requestInitialized()
817869
try {
818870
LocaleContextHolder.setLocale(request.getLocale());
819871
RequestContextHolder.setRequestAttributes(new PortletRequestAttributes(request));
@@ -837,7 +889,7 @@ public void testValidRenderRequestWithExistingThreadLocalRequestContext() throws
837889
MockRenderResponse response = new MockRenderResponse();
838890
request.addPreferredLocale(Locale.GERMAN);
839891

840-
// see RequestContextListener.requestInitialized()
892+
// see RequestContextListener.requestInitialized()
841893
try {
842894
LocaleContextHolder.setLocale(request.getLocale());
843895
RequestContextHolder.setRequestAttributes(new PortletRequestAttributes(request));
@@ -863,7 +915,7 @@ public void testInvalidActionRequestWithExistingThreadLocalRequestContext() thro
863915
MockActionResponse response = new MockActionResponse();
864916
request.addPreferredLocale(Locale.GERMAN);
865917

866-
// see RequestContextListener.requestInitialized()
918+
// see RequestContextListener.requestInitialized()
867919
try {
868920
LocaleContextHolder.setLocale(request.getLocale());
869921
RequestContextHolder.setRequestAttributes(new PortletRequestAttributes(request));
@@ -890,7 +942,7 @@ public void testInvalidRenderRequestWithExistingThreadLocalRequestContext() thro
890942
MockRenderResponse response = new MockRenderResponse();
891943
request.addPreferredLocale(Locale.GERMAN);
892944

893-
// see RequestContextListener.requestInitialized()
945+
// see RequestContextListener.requestInitialized()
894946
try {
895947
LocaleContextHolder.setLocale(request.getLocale());
896948
RequestContextHolder.setRequestAttributes(new PortletRequestAttributes(request));
@@ -903,7 +955,7 @@ public void testInvalidRenderRequestWithExistingThreadLocalRequestContext() thro
903955
fail("should have failed to find a handler and raised an NoHandlerFoundExceptionException");
904956
}
905957
catch (NoHandlerFoundException ex) {
906-
// expected
958+
// expected
907959
}
908960

909961
assertSame(servletLocaleContext, LocaleContextHolder.getLocaleContext());

0 commit comments

Comments
 (0)