diff --git a/build.gradle b/build.gradle index 06d8a024..ee311eda 100644 --- a/build.gradle +++ b/build.gradle @@ -208,8 +208,8 @@ project("spring-faces") { provided("javax.el:el-api:1.0") provided("javax.servlet:javax.servlet-api:3.0.1") provided("javax.portlet:portlet-api:2.0") - provided("com.sun.faces:jsf-api:2.1.7") - provided("com.sun.faces:jsf-impl:2.1.7") + provided("com.sun.faces:jsf-api:2.2.5") + provided("com.sun.faces:jsf-impl:2.2.5") provided("org.apache.myfaces.core:myfaces-impl:2.1.7") optional("com.sun.facelets:jsf-facelets:1.1.14") optional("org.richfaces.framework:richfaces-api:3.1.4.GA") diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContextLifecycleListener.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContextLifecycleListener.java index 8932832f..c24d38de 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContextLifecycleListener.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContextLifecycleListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2010 the original author or authors. + * Copyright 2004-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,18 +22,35 @@ import org.springframework.webflow.execution.FlowExecutionListenerAdapter; import org.springframework.webflow.execution.RequestContext; /** - * A {@link FlowExecutionListener} that creates a {@link FlowFacesContext} instance when a flow request is submitted and - * releases it when the request has been processed. + * A {@link FlowExecutionListener} that creates a {@link FlowFacesContext} + * instance when a flow request is submitted and releases it when the request + * has been processed. * * @author Rossen Stoyanchev */ public class FlowFacesContextLifecycleListener extends FlowExecutionListenerAdapter { + public static final String DEFAULT_FACES_CONTEXT = + FlowFacesContextLifecycleListener.class.getName() + ".DEFAULT_FACES_CONTEXT"; + /** * Creates a new instance of {@link FlowFacesContext} that is then available for the duration of the request. * @param context the current flow request context */ public void requestSubmitted(RequestContext context) { + + FacesContext facesContext = getRequestFacesContext(context); + if (facesContext != null) { + // FacesContext already created, just wrap it (sets "current" instance internally) + if (JsfRuntimeInformation.isAtLeastJsf20()) { + new Jsf2FlowFacesContext(context, facesContext); + } + else { + new FlowFacesContext(context, facesContext); + } + return; + } + FlowFacesContext.newInstance(context, FlowLifecycle.newInstance()); } @@ -42,7 +59,15 @@ public class FlowFacesContextLifecycleListener extends FlowExecutionListenerAdap * @param context the source of the event */ public void requestProcessed(RequestContext context) { + + if (getRequestFacesContext(context) != null) { + return; + } + FacesContext.getCurrentInstance().release(); } + private FacesContext getRequestFacesContext(RequestContext context) { + return (FacesContext) context.getExternalContext().getRequestMap().get(DEFAULT_FACES_CONTEXT); + } } diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowFacesContext.java b/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowFacesContext.java index eee23d44..7c0d89bb 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowFacesContext.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowFacesContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2010 the original author or authors. + * Copyright 2004-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ import javax.faces.context.Flash; import javax.faces.context.PartialViewContext; import javax.faces.context.PartialViewContextFactory; import javax.faces.event.PhaseId; +import javax.faces.lifecycle.ClientWindow; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -265,5 +266,23 @@ public class Jsf2FlowFacesContext extends FlowFacesContext { public void setSessionMaxInactiveInterval(int interval) { delegate.setSessionMaxInactiveInterval(interval); } + + // --------------- JSF 2.2 Pass-through delegate methods ------------------// + + public String getApplicationContextPath() { + return delegate.getApplicationContextPath(); + } + + public String getSessionId(boolean create) { + return delegate.getSessionId(create); + } + + public ClientWindow getClientWindow() { + return delegate.getClientWindow(); + } + + public void setClientWindow(ClientWindow window) { + delegate.setClientWindow(window); + } } } diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowResourceResolver.java b/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowResourceResolver.java index c3343ce4..b0f54970 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowResourceResolver.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/Jsf2FlowResourceResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2010 the original author or authors. + * Copyright 2004-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ import java.io.IOException; import java.net.URL; import javax.faces.FacesException; +import javax.faces.application.ResourceHandler; +import javax.faces.context.FacesContext; import javax.faces.view.facelets.ResourceResolver; import org.springframework.context.ApplicationContext; @@ -29,8 +31,8 @@ import org.springframework.webflow.execution.RequestContextHolder; import com.sun.faces.facelets.impl.DefaultResourceResolver; /** - * Resolves Facelets templates using Spring Resource paths such as "classpath:foo.xhtml". Configure it via a context - * parameter in web.xml: + * Resolves Facelets templates using Spring Resource paths such as "classpath:foo.xhtml". + * Configure it via a context parameter in web.xml: * *
* <context-param/>
@@ -41,11 +43,12 @@ import com.sun.faces.facelets.impl.DefaultResourceResolver;
*/
public class Jsf2FlowResourceResolver extends ResourceResolver {
- ResourceResolver delegateResolver = new DefaultResourceResolver();
+ ResourceResolver delegateResolver;
public URL resolveUrl(String path) {
if (!JsfUtils.isFlowRequest()) {
+ initDelegateIfNecessary();
return delegateResolver.resolveUrl(path);
}
@@ -61,6 +64,7 @@ public class Jsf2FlowResourceResolver extends ResourceResolver {
if (viewResource.exists()) {
return viewResource.getURL();
} else {
+ initDelegateIfNecessary();
return delegateResolver.resolveUrl(path);
}
} catch (IOException ex) {
@@ -68,4 +72,12 @@ public class Jsf2FlowResourceResolver extends ResourceResolver {
}
}
+ private void initDelegateIfNecessary() {
+ if (this.delegateResolver == null) {
+ FacesContext facesContext = FacesContext.getCurrentInstance();
+ ResourceHandler resourceHandler = facesContext.getApplication().getResourceHandler();
+ this.delegateResolver = new DefaultResourceResolver(resourceHandler);
+ }
+ }
+
}
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfFlowHandlerAdapter.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfFlowHandlerAdapter.java
index 18e368d7..f33ae6ce 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfFlowHandlerAdapter.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfFlowHandlerAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2010 the original author or authors.
+ * Copyright 2004-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package org.springframework.faces.webflow;
+import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -32,9 +33,11 @@ import org.springframework.webflow.mvc.servlet.FlowHandlerAdapter;
public class JsfFlowHandlerAdapter extends FlowHandlerAdapter {
public void afterPropertiesSet() throws Exception {
- boolean initializeAjaxHandler = getAjaxHandler() == null;
+
+ boolean isAjaxHandlerConfigured = (getAjaxHandler() != null);
super.afterPropertiesSet();
- if (initializeAjaxHandler) {
+
+ if (!isAjaxHandlerConfigured) {
if (JsfRuntimeInformation.isAtLeastJsf20()) {
JsfAjaxHandler ajaxHandler = new JsfAjaxHandler();
ajaxHandler.setApplicationContext(getApplicationContext());
@@ -45,7 +48,17 @@ public class JsfFlowHandlerAdapter extends FlowHandlerAdapter {
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
- return super.handle(request, response, handler);
+
+ FacesContextHelper helper = new FacesContextHelper();
+ try {
+ FacesContext facesContext = helper.getFacesContext(getServletContext(), request, response);
+ request.setAttribute(FlowFacesContextLifecycleListener.DEFAULT_FACES_CONTEXT, facesContext);
+ return super.handle(request, response, handler);
+
+ } finally {
+ request.removeAttribute(FlowFacesContextLifecycleListener.DEFAULT_FACES_CONTEXT);
+ helper.releaseIfNecessary();
+ }
}
}
diff --git a/spring-faces/src/test/java/org/springframework/faces/webflow/JsfAjaxHandlerTests.java b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfAjaxHandlerTests.java
index e2ea1d5d..d7ab0db2 100644
--- a/spring-faces/src/test/java/org/springframework/faces/webflow/JsfAjaxHandlerTests.java
+++ b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfAjaxHandlerTests.java
@@ -23,9 +23,7 @@ public class JsfAjaxHandlerTests extends TestCase {
public void testSendAjaxRedirect() throws Exception {
ajaxHandler.sendAjaxRedirectInternal("/target", jsfMock.request(), jsfMock.response(), false);
- assertEquals(
- "\n ",
- jsfMock.contentAsString());
+ assertTrue(this.jsfMock.contentAsString().matches(""));
assertEquals("application/xml", jsfMock.response().getContentType());
}