001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.jsf.el;
018
019import java.beans.FeatureDescriptor;
020import java.util.Iterator;
021
022import javax.el.ELContext;
023import javax.el.ELException;
024import javax.el.ELResolver;
025import javax.el.PropertyNotWritableException;
026import javax.faces.context.FacesContext;
027
028import org.springframework.lang.Nullable;
029import org.springframework.web.context.WebApplicationContext;
030import org.springframework.web.jsf.FacesContextUtils;
031
032/**
033 * JSF {@code ELResolver} that delegates to the Spring root {@code WebApplicationContext},
034 * resolving name references to Spring-defined beans.
035 *
036 * <p>Configure this resolver in your {@code faces-config.xml} file as follows:
037 *
038 * <pre class="code">
039 * &lt;application>
040 *   ...
041 *   &lt;el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver&lt;/el-resolver>
042 * &lt;/application></pre>
043 *
044 * All your JSF expressions can then implicitly refer to the names of
045 * Spring-managed service layer beans, for example in property values of
046 * JSF-managed beans:
047 *
048 * <pre class="code">
049 * &lt;managed-bean>
050 *   &lt;managed-bean-name>myJsfManagedBean&lt;/managed-bean-name>
051 *   &lt;managed-bean-class>example.MyJsfManagedBean&lt;/managed-bean-class>
052 *   &lt;managed-bean-scope>session&lt;/managed-bean-scope>
053 *   &lt;managed-property>
054 *     &lt;property-name>mySpringManagedBusinessObject&lt;/property-name>
055 *     &lt;value>#{mySpringManagedBusinessObject}&lt;/value>
056 *   &lt;/managed-property>
057 * &lt;/managed-bean></pre>
058 *
059 * with "mySpringManagedBusinessObject" defined as Spring bean in
060 * applicationContext.xml:
061 *
062 * <pre class="code">
063 * &lt;bean id="mySpringManagedBusinessObject" class="example.MySpringManagedBusinessObject">
064 *   ...
065 * &lt;/bean></pre>
066 *
067 * @author Juergen Hoeller
068 * @since 2.5
069 * @see WebApplicationContextFacesELResolver
070 * @see org.springframework.web.jsf.FacesContextUtils#getRequiredWebApplicationContext
071 */
072public class SpringBeanFacesELResolver extends ELResolver {
073
074        @Override
075        @Nullable
076        public Object getValue(ELContext elContext, @Nullable Object base, Object property) throws ELException {
077                if (base == null) {
078                        String beanName = property.toString();
079                        WebApplicationContext wac = getWebApplicationContext(elContext);
080                        if (wac.containsBean(beanName)) {
081                                elContext.setPropertyResolved(true);
082                                return wac.getBean(beanName);
083                        }
084                }
085                return null;
086        }
087
088        @Override
089        @Nullable
090        public Class<?> getType(ELContext elContext, @Nullable Object base, Object property) throws ELException {
091                if (base == null) {
092                        String beanName = property.toString();
093                        WebApplicationContext wac = getWebApplicationContext(elContext);
094                        if (wac.containsBean(beanName)) {
095                                elContext.setPropertyResolved(true);
096                                return wac.getType(beanName);
097                        }
098                }
099                return null;
100        }
101
102        @Override
103        public void setValue(ELContext elContext, @Nullable Object base, Object property, Object value) throws ELException {
104                if (base == null) {
105                        String beanName = property.toString();
106                        WebApplicationContext wac = getWebApplicationContext(elContext);
107                        if (wac.containsBean(beanName)) {
108                                if (value == wac.getBean(beanName)) {
109                                        // Setting the bean reference to the same value is alright - can simply be ignored...
110                                        elContext.setPropertyResolved(true);
111                                }
112                                else {
113                                        throw new PropertyNotWritableException(
114                                                        "Variable '" + beanName + "' refers to a Spring bean which by definition is not writable");
115                                }
116                        }
117                }
118        }
119
120        @Override
121        public boolean isReadOnly(ELContext elContext, @Nullable Object base, Object property) throws ELException {
122                if (base == null) {
123                        String beanName = property.toString();
124                        WebApplicationContext wac = getWebApplicationContext(elContext);
125                        if (wac.containsBean(beanName)) {
126                                return true;
127                        }
128                }
129                return false;
130        }
131
132        @Override
133        @Nullable
134        public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, @Nullable Object base) {
135                return null;
136        }
137
138        @Override
139        public Class<?> getCommonPropertyType(ELContext elContext, @Nullable Object base) {
140                return Object.class;
141        }
142
143        /**
144         * Retrieve the web application context to delegate bean name resolution to.
145         * <p>The default implementation delegates to FacesContextUtils.
146         * @param elContext the current JSF ELContext
147         * @return the Spring web application context (never {@code null})
148         * @see org.springframework.web.jsf.FacesContextUtils#getRequiredWebApplicationContext
149         */
150        protected WebApplicationContext getWebApplicationContext(ELContext elContext) {
151                FacesContext facesContext = FacesContext.getCurrentInstance();
152                return FacesContextUtils.getRequiredWebApplicationContext(facesContext);
153        }
154
155}