001/*
002 * Copyright 2002-2014 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.beans.factory.access.el;
018
019import java.beans.FeatureDescriptor;
020import java.util.Iterator;
021import javax.el.ELContext;
022import javax.el.ELException;
023import javax.el.ELResolver;
024import javax.el.PropertyNotWritableException;
025
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028
029import org.springframework.beans.factory.BeanFactory;
030
031/**
032 * Unified EL {@code ELResolver} that delegates to a Spring BeanFactory,
033 * resolving name references to Spring-defined beans.
034 *
035 * @author Juergen Hoeller
036 * @since 2.5.2
037 * @see org.springframework.web.jsf.el.SpringBeanFacesELResolver
038 */
039public abstract class SpringBeanELResolver extends ELResolver {
040
041        /** Logger available to subclasses */
042        protected final Log logger = LogFactory.getLog(getClass());
043
044
045        @Override
046        public Object getValue(ELContext elContext, Object base, Object property) throws ELException {
047                if (base == null) {
048                        String beanName = property.toString();
049                        BeanFactory bf = getBeanFactory(elContext);
050                        if (bf.containsBean(beanName)) {
051                                if (logger.isTraceEnabled()) {
052                                        logger.trace("Successfully resolved variable '" + beanName + "' in Spring BeanFactory");
053                                }
054                                elContext.setPropertyResolved(true);
055                                return bf.getBean(beanName);
056                        }
057                }
058                return null;
059        }
060
061        @Override
062        public Class<?> getType(ELContext elContext, Object base, Object property) throws ELException {
063                if (base == null) {
064                        String beanName = property.toString();
065                        BeanFactory bf = getBeanFactory(elContext);
066                        if (bf.containsBean(beanName)) {
067                                elContext.setPropertyResolved(true);
068                                return bf.getType(beanName);
069                        }
070                }
071                return null;
072        }
073
074        @Override
075        public void setValue(ELContext elContext, Object base, Object property, Object value) throws ELException {
076                if (base == null) {
077                        String beanName = property.toString();
078                        BeanFactory bf = getBeanFactory(elContext);
079                        if (bf.containsBean(beanName)) {
080                                if (value == bf.getBean(beanName)) {
081                                        // Setting the bean reference to the same value is alright - can simply be ignored...
082                                        elContext.setPropertyResolved(true);
083                                }
084                                else {
085                                        throw new PropertyNotWritableException(
086                                                        "Variable '" + beanName + "' refers to a Spring bean which by definition is not writable");
087                                }
088                        }
089                }
090        }
091
092        @Override
093        public boolean isReadOnly(ELContext elContext, Object base, Object property) throws ELException {
094                if (base == null) {
095                        String beanName = property.toString();
096                        BeanFactory bf = getBeanFactory(elContext);
097                        if (bf.containsBean(beanName)) {
098                                return true;
099                        }
100                }
101                return false;
102        }
103
104        @Override
105        public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, Object base) {
106                return null;
107        }
108
109        @Override
110        public Class<?> getCommonPropertyType(ELContext elContext, Object base) {
111                return Object.class;
112        }
113
114
115        /**
116         * Retrieve the Spring BeanFactory to delegate bean name resolution to.
117         * @param elContext the current ELContext
118         * @return the Spring BeanFactory (never {@code null})
119         */
120        protected abstract BeanFactory getBeanFactory(ELContext elContext);
121
122}