001/*
002 * Copyright 2002-2018 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.faces.context.FacesContext;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029
030import org.springframework.beans.BeansException;
031import org.springframework.lang.Nullable;
032import org.springframework.web.context.WebApplicationContext;
033import org.springframework.web.jsf.FacesContextUtils;
034
035/**
036 * Special JSF {@code ELResolver} that exposes the Spring {@code WebApplicationContext}
037 * instance under a variable named "webApplicationContext".
038 *
039 * <p>In contrast to {@link SpringBeanFacesELResolver}, this ELResolver variant
040 * does <i>not</i> resolve JSF variable names as Spring bean names. It rather
041 * exposes Spring's root WebApplicationContext <i>itself</i> under a special name,
042 * and is able to resolve "webApplicationContext.mySpringManagedBusinessObject"
043 * dereferences to Spring-defined beans in that application context.
044 *
045 * <p>Configure this resolver in your {@code faces-config.xml} file as follows:
046 *
047 * <pre class="code">
048 * &lt;application>
049 *   ...
050 *   &lt;el-resolver>org.springframework.web.jsf.el.WebApplicationContextFacesELResolver&lt;/el-resolver>
051 * &lt;/application></pre>
052 *
053 * @author Juergen Hoeller
054 * @since 2.5
055 * @see SpringBeanFacesELResolver
056 * @see org.springframework.web.jsf.FacesContextUtils#getWebApplicationContext
057 */
058public class WebApplicationContextFacesELResolver extends ELResolver {
059
060        /**
061         * Name of the exposed WebApplicationContext variable: "webApplicationContext".
062         */
063        public static final String WEB_APPLICATION_CONTEXT_VARIABLE_NAME = "webApplicationContext";
064
065
066        /** Logger available to subclasses. */
067        protected final Log logger = LogFactory.getLog(getClass());
068
069
070        @Override
071        @Nullable
072        public Object getValue(ELContext elContext, @Nullable Object base, Object property) throws ELException {
073                if (base != null) {
074                        if (base instanceof WebApplicationContext) {
075                                WebApplicationContext wac = (WebApplicationContext) base;
076                                String beanName = property.toString();
077                                if (logger.isTraceEnabled()) {
078                                        logger.trace("Attempting to resolve property '" + beanName + "' in root WebApplicationContext");
079                                }
080                                if (wac.containsBean(beanName)) {
081                                        if (logger.isDebugEnabled()) {
082                                                logger.debug("Successfully resolved property '" + beanName + "' in root WebApplicationContext");
083                                        }
084                                        elContext.setPropertyResolved(true);
085                                        try {
086                                                return wac.getBean(beanName);
087                                        }
088                                        catch (BeansException ex) {
089                                                throw new ELException(ex);
090                                        }
091                                }
092                                else {
093                                        // Mimic standard JSF/JSP behavior when base is a Map by returning null.
094                                        return null;
095                                }
096                        }
097                }
098                else {
099                        if (WEB_APPLICATION_CONTEXT_VARIABLE_NAME.equals(property)) {
100                                elContext.setPropertyResolved(true);
101                                return getWebApplicationContext(elContext);
102                        }
103                }
104
105                return null;
106        }
107
108        @Override
109        @Nullable
110        public Class<?> getType(ELContext elContext, @Nullable Object base, Object property) throws ELException {
111                if (base != null) {
112                        if (base instanceof WebApplicationContext) {
113                                WebApplicationContext wac = (WebApplicationContext) base;
114                                String beanName = property.toString();
115                                if (logger.isDebugEnabled()) {
116                                        logger.debug("Attempting to resolve property '" + beanName + "' in root WebApplicationContext");
117                                }
118                                if (wac.containsBean(beanName)) {
119                                        if (logger.isDebugEnabled()) {
120                                                logger.debug("Successfully resolved property '" + beanName + "' in root WebApplicationContext");
121                                        }
122                                        elContext.setPropertyResolved(true);
123                                        try {
124                                                return wac.getType(beanName);
125                                        }
126                                        catch (BeansException ex) {
127                                                throw new ELException(ex);
128                                        }
129                                }
130                                else {
131                                        // Mimic standard JSF/JSP behavior when base is a Map by returning null.
132                                        return null;
133                                }
134                        }
135                }
136                else {
137                        if (WEB_APPLICATION_CONTEXT_VARIABLE_NAME.equals(property)) {
138                                elContext.setPropertyResolved(true);
139                                return WebApplicationContext.class;
140                        }
141                }
142
143                return null;
144        }
145
146        @Override
147        public void setValue(ELContext elContext, Object base, Object property, Object value) throws ELException {
148        }
149
150        @Override
151        public boolean isReadOnly(ELContext elContext, Object base, Object property) throws ELException {
152                if (base instanceof WebApplicationContext) {
153                        elContext.setPropertyResolved(true);
154                        return true;
155                }
156                return false;
157        }
158
159        @Override
160        @Nullable
161        public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, Object base) {
162                return null;
163        }
164
165        @Override
166        public Class<?> getCommonPropertyType(ELContext elContext, Object base) {
167                return Object.class;
168        }
169
170
171        /**
172         * Retrieve the {@link WebApplicationContext} reference to expose.
173         * <p>The default implementation delegates to {@link FacesContextUtils},
174         * returning {@code null} if no {@code WebApplicationContext} found.
175         * @param elContext the current JSF ELContext
176         * @return the Spring web application context
177         * @see org.springframework.web.jsf.FacesContextUtils#getWebApplicationContext
178         */
179        @Nullable
180        protected WebApplicationContext getWebApplicationContext(ELContext elContext) {
181                FacesContext facesContext = FacesContext.getCurrentInstance();
182                return FacesContextUtils.getRequiredWebApplicationContext(facesContext);
183        }
184
185}