View Javadoc

1   /*
2    Spring-Annotation, an easy way to configre Spring-Framework without all that XML.
3    Copyright (C) 2007 Spring-Annotation Development Team (http://www.urubatan.com.br)
4   
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9   
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Lesser General Public License for more details.
14  
15   You should have received a copy of the GNU Lesser General Public
16   License along with this library; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  
19   <a href="http://www.gnu.org/licenses/lgpl.html">http://www.gnu.org/licenses/lgpl.html</a>
20   */
21  package net.sourceforge.sannotations.jsf.utils;
22  
23  import javax.faces.context.FacesContext;
24  import javax.faces.el.EvaluationException;
25  import javax.faces.el.MethodBinding;
26  import javax.faces.el.VariableResolver;
27  
28  import net.sourceforge.sannotations.scopes.ConversationScope;
29  import net.sourceforge.sannotations.scopes.FlashScope;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.springframework.beans.factory.BeanFactory;
34  import org.springframework.util.Assert;
35  import org.springframework.web.context.WebApplicationContext;
36  import org.springframework.web.jsf.FacesContextUtils;
37  import org.springframework.web.jsf.WebApplicationContextVariableResolver;
38  
39  /***
40   * JSF VariableResolver that first delegates to the original resolver of the underlying JSF implementation, then to the Spring root WebApplicationContext.
41   * <p>
42   * Configure this resolver in your <code>faces-config.xml</code> file as follows:
43   * 
44   * <pre>
45   *   &lt;application&gt;
46   *     ...
47   *     &lt;variable-resolver&gt;org.springframework.web.jsf.DelegatingVariableResolver&lt;/variable-resolver&gt;
48   *   &lt;/application&gt;
49   * </pre>
50   * 
51   * All your JSF expressions can then implicitly refer to the names of Spring-managed service layer beans, for example in property values of JSF-managed beans:
52   * 
53   * <pre>
54   *   &lt;managed-bean&gt;
55   *     &lt;managed-bean-name&gt;myJsfManagedBean&lt;/managed-bean-name&gt;
56   *     &lt;managed-bean-class&gt;example.MyJsfManagedBean&lt;/managed-bean-class&gt;
57   *     &lt;managed-bean-scope&gt;session&lt;/managed-bean-scope&gt;
58   *     &lt;managed-property&gt;
59   *       &lt;property-name&gt;mySpringManagedBusinessObject&lt;/property-name&gt;
60   *       &lt;value&gt;#{mySpringManagedBusinessObject}&lt;/value&gt;
61   *     &lt;/managed-property&gt;
62   *   &lt;/managed-bean&gt;
63   * </pre>
64   * 
65   * with "mySpringManagedBusinessObject" defined as Spring bean in applicationContext.xml:
66   * 
67   * <pre>
68   *   &lt;bean id=&quot;mySpringManagedBusinessObject&quot; class=&quot;example.MySpringManagedBusinessObject&quot;&gt;
69   *     ...
70   *   &lt;/bean&gt;
71   * </pre>
72   * 
73   * @author Juergen Hoeller
74   * @author Rodrigo Urubatan
75   * @since 1.1
76   * @see WebApplicationContextVariableResolver
77   * @see FacesContextUtils#getRequiredWebApplicationContext
78   */
79  public class DelegatingVariableResolver extends VariableResolver {
80  	/*** Logger available to subclasses */
81  	protected final Log					logger	= LogFactory.getLog(this.getClass());
82  	protected final VariableResolver	originalVariableResolver;
83  	private DataModelFactoryRegistry	dataModelfactoryRegistry;
84  
85  	/***
86  	 * Create a new DelegatingVariableResolver, using the given original VariableResolver.
87  	 * <p>
88  	 * A JSF implementation will automatically pass its original resolver into the constructor of a configured resolver, provided that there is a corresponding constructor
89  	 * argument.
90  	 * 
91  	 * @param originalVariableResolver
92  	 *            the original VariableResolver
93  	 */
94  	public DelegatingVariableResolver(final VariableResolver originalVariableResolver) {
95  		Assert.notNull(originalVariableResolver, "Original JSF VariableResolver must not be null");
96  		this.originalVariableResolver = originalVariableResolver;
97  	}
98  
99  	/***
100 	 * Return the original JSF VariableResolver that this resolver delegates to. Used to resolve standard JSF-managed beans.
101 	 */
102 	protected final VariableResolver getOriginalVariableResolver() {
103 		return this.originalVariableResolver;
104 	}
105 
106 	/***
107 	 * Delegate to the original VariableResolver first, then try to resolve the variable as Spring bean in the root WebApplicationContext.
108 	 */
109 	@Override
110 	public Object resolveVariable(final FacesContext facesContext, final String name) throws EvaluationException {
111 		Object result = null;
112 		final BeanFactory bf = this.getBeanFactory(facesContext);
113 		result = this.resolve(facesContext, name, bf);
114 		if (result == null) {
115 			if (this.dataModelfactoryRegistry == null) {
116 				this.dataModelfactoryRegistry = (DataModelFactoryRegistry) bf.getBean("dataModelfactoryRegistry");
117 			}
118 			if (this.dataModelfactoryRegistry.hasFactory(name)) {
119 				if (this.logger.isDebugEnabled()) {
120 					this.logger.debug("Attempting to call factory method for '" + name + "'");
121 				}
122 				final String fstring = this.dataModelfactoryRegistry.getFactory(name);
123 				final MethodBinding factory = facesContext.getApplication().createMethodBinding(fstring, null);
124 				factory.invoke(facesContext, null);
125 				result = this.resolve(facesContext, name, bf);
126 			}
127 		}
128 		return result;
129 	}
130 
131 	private Object resolve(final FacesContext facesContext, final String name, BeanFactory bf) {
132 		// Ask Spring root application context.
133 		if (this.logger.isDebugEnabled()) {
134 			this.logger.debug("Attempting to resolve variable '" + name + "' in root WebApplicationContext");
135 		}
136 		if (bf.containsBean(name)) {
137 			if (this.logger.isDebugEnabled()) {
138 				this.logger.debug("Successfully resolved variable '" + name + "' in root WebApplicationContext");
139 			}
140 			return bf.getBean(name);
141 		}
142 		/*
143 		 * if (bf.containsBean(JSFBeanFactoryPostProcessor.SCOPEDPREFIX + name)) { if (logger.isDebugEnabled()) { logger.debug("Successfully resolved variable '" +
144 		 * JSFBeanFactoryPostProcessor.SCOPEDPREFIX +name + "' in root WebApplicationContext"); } return bf.getBean(JSFBeanFactoryPostProcessor.SCOPEDPREFIX +name); }
145 		 */
146 		final ConversationScope conversationScope = (ConversationScope) bf.getBean("conversationScopeBean");
147 		Object object = conversationScope.get(name, null);
148 		if (object != null) {
149 			if (this.logger.isDebugEnabled()) {
150 				this.logger.debug("Successfully resolved variable '" + name + "' in root Conversation Scope");
151 			}
152 			return object;
153 		}
154 		final FlashScope flashScope = (FlashScope) bf.getBean("flashScopeBean");
155 		object = flashScope.get(name, null);
156 		if (object != null) {
157 			if (this.logger.isDebugEnabled()) {
158 				this.logger.debug("Successfully resolved variable '" + name + "' in root Flash Scope");
159 			}
160 			return object;
161 		}
162 		// Ask original JSF variable resolver.
163 		if (this.logger.isDebugEnabled()) {
164 			this.logger.debug("Attempting to resolve variable '" + name + "' in via original VariableResolver");
165 		}
166 		final Object originalResult = this.getOriginalVariableResolver().resolveVariable(facesContext, name);
167 		if (originalResult != null) {
168 			return originalResult;
169 		}
170 		if (this.logger.isDebugEnabled()) {
171 			this.logger.debug("Could not resolve variable '" + name + "'");
172 		}
173 		return null;
174 	}
175 
176 	/***
177 	 * Retrieve the Spring BeanFactory to delegate bean name resolution to.
178 	 * <p>
179 	 * Default implementation delegates to <code>getWebApplicationContext</code>. Can be overridden to provide an arbitrary BeanFactory reference to resolve against; usually,
180 	 * this will be a full Spring ApplicationContext.
181 	 * 
182 	 * @param facesContext
183 	 *            the current JSF context
184 	 * @return the Spring BeanFactory (never <code>null</code>)
185 	 * @see #getWebApplicationContext
186 	 */
187 	protected BeanFactory getBeanFactory(final FacesContext facesContext) {
188 		return this.getWebApplicationContext(facesContext);
189 	}
190 
191 	/***
192 	 * Retrieve the web application context to delegate bean name resolution to.
193 	 * <p>
194 	 * Default implementation delegates to FacesContextUtils.
195 	 * 
196 	 * @param facesContext
197 	 *            the current JSF context
198 	 * @return the Spring web application context (never <code>null</code>)
199 	 * @see FacesContextUtils#getRequiredWebApplicationContext
200 	 */
201 	protected WebApplicationContext getWebApplicationContext(final FacesContext facesContext) {
202 		return FacesContextUtils.getRequiredWebApplicationContext(facesContext);
203 	}
204 }