From d31900e5f961828791c1e65095c3df698b327db4 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 16 Nov 2012 14:18:53 -0800 Subject: [PATCH] Prevent NPE in Mojarra when creating initial view Refactor JsfViewFactory to only attempt a restoreView when state is available. This prevents a NullPointerException from being thrown when using Mojarra with partial state saving disabled. This change also makes Web Flow behave in a similar way to the standard FacesServlet. Issue: SWF-1571 --- .../faces/webflow/FlowResponseStateManager.java | 9 +++++++++ .../springframework/faces/webflow/JsfViewFactory.java | 8 +++++++- .../faces/webflow/JsfViewFactoryTests.java | 11 +++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowResponseStateManager.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowResponseStateManager.java index 736a8065..5fbacbb7 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowResponseStateManager.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowResponseStateManager.java @@ -142,4 +142,13 @@ public class FlowResponseStateManager extends ResponseStateManagerWrapper { writer.endElement("input"); } } + + /** + * Determine if the request context has existing state. + * @param context the request context + * @return {@code true} if the context has state + */ + static boolean hasState(RequestContext context) { + return (context != null) && (context.getViewScope().contains(FACES_VIEW_STATE)); + } } diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java index 82eba121..a780cac9 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java @@ -132,7 +132,12 @@ public class JsfViewFactory implements ViewFactory { private UIViewRoot getViewStateViewRoot(RequestContext context, FacesContext facesContext, ViewHandler viewHandler, String viewName) { - UIViewRoot viewRoot = viewHandler.restoreView(facesContext, viewName); + UIViewRoot viewRoot = null; + if (FlowResponseStateManager.hasState(context)) { + // Only try an initial restore if we have state (see SWF-1571) + viewRoot = viewHandler.restoreView(facesContext, viewName); + } + if (viewRoot != null) { if (logger.isDebugEnabled()) { logger.debug("UIViewRoot restored for '" + viewName + "'"); @@ -144,6 +149,7 @@ public class JsfViewFactory implements ViewFactory { } viewRoot = viewHandler.createView(facesContext, viewName); } + return viewRoot; } diff --git a/spring-faces/src/test/java/org/springframework/faces/webflow/JsfViewFactoryTests.java b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfViewFactoryTests.java index e13bdca8..463d5e14 100644 --- a/spring-faces/src/test/java/org/springframework/faces/webflow/JsfViewFactoryTests.java +++ b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfViewFactoryTests.java @@ -72,12 +72,15 @@ public class JsfViewFactoryTests extends TestCase { private final MockHttpServletResponse response = new MockHttpServletResponse(); + private LocalAttributeMap viewScope = new LocalAttributeMap(); + protected void setUp() throws Exception { configureJsf(); this.extContext.setNativeContext(this.servletContext); this.extContext.setNativeRequest(this.request); this.extContext.setNativeResponse(this.response); RequestContextHolder.setRequestContext(this.context); + EasyMock.expect(this.context.getViewScope()).andStubReturn(this.viewScope); EasyMock.expect(this.context.getFlashScope()).andStubReturn(this.flashMap); EasyMock.expect(this.context.getExternalContext()).andStubReturn(this.extContext); EasyMock.expect(this.context.getRequestParameters()).andStubReturn( @@ -148,6 +151,7 @@ public class JsfViewFactoryTests extends TestCase { EasyMock.expectLastCall().andReturn(true); EasyMock.replay(new Object[] { this.context }); + setupViewState(); View restoredView = this.factory.getView(this.context); @@ -194,6 +198,7 @@ public class JsfViewFactoryTests extends TestCase { EasyMock.expectLastCall().andReturn(true); EasyMock.replay(new Object[] { this.context }); + setupViewState(); View restoredView = this.factory.getView(this.context); @@ -229,6 +234,7 @@ public class JsfViewFactoryTests extends TestCase { EasyMock.expectLastCall().andReturn(true); EasyMock.replay(new Object[] { this.context }); + setupViewState(); View restoredView = this.factory.getView(this.context); @@ -279,6 +285,7 @@ public class JsfViewFactoryTests extends TestCase { this.context.inViewState(); EasyMock.expectLastCall().andReturn(true); + setupViewState(); EasyMock.replay(new Object[] { this.context }); this.factory.getView(this.context); @@ -289,6 +296,10 @@ public class JsfViewFactoryTests extends TestCase { .getExceptionQueuedEventContext().getException()); } + private void setupViewState() { + this.viewScope.put(FlowResponseStateManager.FACES_VIEW_STATE, "X"); + } + private class NoExecutionLifecycle extends FlowLifecycle { public NoExecutionLifecycle(Lifecycle delegate) {