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