From cffd1889a5611991213e933a39037fbfbbf0e9d7 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Tue, 22 Apr 2008 20:45:22 +0000 Subject: [PATCH] SWF-301 - Make JSF managed beans accessible inside a flow definition using EL expressions. --- .../faces/webflow/JsfManagedBeanResolver.java | 64 +++++++++++++------ ...nagedBeanAwareELExpressionParserTests.java | 37 +++++++++++ 2 files changed, 80 insertions(+), 21 deletions(-) create mode 100644 spring-faces/src/test/java/org/springframework/faces/webflow/JsfManagedBeanAwareELExpressionParserTests.java diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfManagedBeanResolver.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfManagedBeanResolver.java index 4c57f80c..cfb3e73d 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfManagedBeanResolver.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfManagedBeanResolver.java @@ -19,7 +19,10 @@ import java.util.Iterator; import javax.el.ELContext; import javax.el.ELResolver; +import javax.faces.context.FacesContext; +import javax.faces.el.ValueBinding; +import org.springframework.util.Assert; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.RequestContextHolder; @@ -27,8 +30,8 @@ import org.springframework.webflow.execution.RequestContextHolder; * ELResolver that checks request, session, and application scopes for existing JSF-managed beans. This allows * traditional JSF-managed beans (defined in faces-config.xml) to be resolved through expressions in a flow definition. * The preferred approach is to instead use Spring to configure such beans, but this is meant to ease migration for - * users with existing JSF artifacts. Note that this resolver does not have access to the JSF managed bean definitions - * and will not trigger the bean's instantiation if it has not already been initialized by JSF. + * users with existing JSF artifacts. This resolver will delegate to a temporary FacesContext so that JSF managed bean + * initialization will be triggered if the bean has not already been initialized by JSF. * * @author Jeremy Grelle */ @@ -44,16 +47,10 @@ public class JsfManagedBeanResolver extends ELResolver { public Class getType(ELContext context, Object base, Object property) { if (base == null) { - RequestContext requestContext = RequestContextHolder.getRequestContext(); - if (requestContext.getExternalContext().getRequestMap().contains(property.toString())) { + Object bean = getFacesBean(property); + if (bean != null) { context.setPropertyResolved(true); - return requestContext.getExternalContext().getRequestMap().get(property.toString()).getClass(); - } else if (requestContext.getExternalContext().getSessionMap().contains(property.toString())) { - context.setPropertyResolved(true); - return requestContext.getExternalContext().getSessionMap().get(property.toString()).getClass(); - } else if (requestContext.getExternalContext().getApplicationMap().contains(property.toString())) { - context.setPropertyResolved(true); - return requestContext.getExternalContext().getApplicationMap().get(property.toString()).getClass(); + return bean.getClass(); } } return null; @@ -61,16 +58,10 @@ public class JsfManagedBeanResolver extends ELResolver { public Object getValue(ELContext context, Object base, Object property) { if (base == null) { - RequestContext requestContext = RequestContextHolder.getRequestContext(); - if (requestContext.getExternalContext().getRequestMap().contains(property.toString())) { + Object bean = getFacesBean(property); + if (bean != null) { context.setPropertyResolved(true); - return requestContext.getExternalContext().getRequestMap().get(property.toString()); - } else if (requestContext.getExternalContext().getSessionMap().contains(property.toString())) { - context.setPropertyResolved(true); - return requestContext.getExternalContext().getSessionMap().get(property.toString()); - } else if (requestContext.getExternalContext().getApplicationMap().contains(property.toString())) { - context.setPropertyResolved(true); - return requestContext.getExternalContext().getApplicationMap().get(property.toString()); + return bean; } } return null; @@ -83,12 +74,14 @@ public class JsfManagedBeanResolver extends ELResolver { || requestContext.getExternalContext().getSessionMap().contains(property.toString()) || requestContext.getExternalContext().getApplicationMap().contains(property.toString())) { context.setPropertyResolved(true); - return true; } } return false; } + /** + * Sets a bean value if a corresponding key is found in one of the ExternalContext scopes. + */ public void setValue(ELContext context, Object base, Object property, Object value) { if (base == null) { RequestContext requestContext = RequestContextHolder.getRequestContext(); @@ -105,4 +98,33 @@ public class JsfManagedBeanResolver extends ELResolver { } } + /** + * This resolver is only meant to be called from the Flow Execution, thus it assumes that the FacesContext will not + * be available and creates a temporary one on the fly. + * @return The initialized FacesContext. + */ + private FacesContext getFacesContext() { + RequestContext requestContext = RequestContextHolder.getRequestContext(); + Assert.notNull(requestContext, "RequestContext cannot be null - This resolver is only intended to be invoked " + + "from an active Flow Execution."); + FacesContext facesContext = FlowFacesContext.newInstance(requestContext, FlowLifecycle.newInstance()); + return facesContext; + } + + /** + * Uses a temporary FacesContext to try and resolve a JSF Managed Bean + * @param beanName - The name of the bean to resolve. + * @return The JSF Managed Bean instance if found. + */ + private Object getFacesBean(Object beanName) { + FacesContext facesContext = getFacesContext(); + Object result = null; + try { + ValueBinding vb = facesContext.getApplication().createValueBinding("#{" + beanName + "}"); + result = vb.getValue(facesContext); + } finally { + facesContext.release(); + } + return result; + } } diff --git a/spring-faces/src/test/java/org/springframework/faces/webflow/JsfManagedBeanAwareELExpressionParserTests.java b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfManagedBeanAwareELExpressionParserTests.java new file mode 100644 index 00000000..a9cfd000 --- /dev/null +++ b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfManagedBeanAwareELExpressionParserTests.java @@ -0,0 +1,37 @@ +package org.springframework.faces.webflow; + +import junit.framework.TestCase; + +import org.jboss.el.ExpressionFactoryImpl; +import org.springframework.binding.expression.Expression; +import org.springframework.binding.expression.ExpressionParser; +import org.springframework.binding.expression.support.FluentParserContext; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.RequestContextHolder; +import org.springframework.webflow.test.MockRequestContext; + +public class JsfManagedBeanAwareELExpressionParserTests extends TestCase { + + JSFMockHelper jsfMock = new JSFMockHelper(); + + RequestContext requestContext = new MockRequestContext(); + + ExpressionParser parser; + + protected void setUp() throws Exception { + jsfMock.setUp(); + RequestContextHolder.setRequestContext(requestContext); + parser = new JsfManagedBeanAwareELExpressionParser(new ExpressionFactoryImpl()); + } + + protected void tearDown() throws Exception { + jsfMock.tearDown(); + } + + public void testGetJSFBean() { + jsfMock.externalContext().getRequestMap().put("myJsfBean", new Object()); + Expression expr = parser.parseExpression("myJsfBean", new FluentParserContext().evaluate(RequestContext.class)); + Object result = expr.getValue(requestContext); + assertNotNull("The JSF Bean should not be null.", result); + } +}