/* * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.integration.config; import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.context.expression.MapAccessor; import org.springframework.core.convert.ConversionService; import org.springframework.expression.BeanResolver; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypeConverter; import org.springframework.expression.TypeLocator; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardTypeConverter; import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.integration.expression.SpelPropertyAccessorRegistrar; import org.springframework.integration.support.utils.IntegrationUtils; import org.springframework.util.Assert; /** * <p> * {@link FactoryBean} to populate {@link StandardEvaluationContext} instances enhanced with: * <ul> * <li> * a {@link BeanFactoryResolver}. * </li> * <li> * a {@link TypeConverter} based on the {@link ConversionService} from the application context. * </li> * <li> * a set of provided {@link PropertyAccessor}s including a default {@link MapAccessor}. * </li> * <li> * a set of provided SpEL functions. * </li> * </ul> * <p> * After initialization this factory populates functions and property accessors from * {@link SpelFunctionFactoryBean}s and {@link SpelPropertyAccessorRegistrar}, respectively. * Functions and property accessors are also inherited from any parent context. * </p> * <p> * This factory returns a new instance for each reference - {@link #isSingleton()} returns false. * </p> * * @author Artem Bilan * @author Gary Russell * * @since 3.0 */ public class IntegrationEvaluationContextFactoryBean implements FactoryBean<StandardEvaluationContext>, ApplicationContextAware, InitializingBean { private volatile Map<String, PropertyAccessor> propertyAccessors = new LinkedHashMap<String, PropertyAccessor>(); private volatile Map<String, Method> functions = new LinkedHashMap<String, Method>(); private TypeConverter typeConverter = new StandardTypeConverter(); private volatile TypeLocator typeLocator; private BeanResolver beanResolver; private ApplicationContext applicationContext; private volatile boolean initialized; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public void setPropertyAccessors(Map<String, PropertyAccessor> accessors) { Assert.isTrue(!this.initialized, "'propertyAccessors' can't be changed after initialization."); Assert.notNull(accessors, "'accessors' must not be null."); Assert.noNullElements(accessors.values().toArray(), "'accessors' cannot have null values."); this.propertyAccessors = new LinkedHashMap<String, PropertyAccessor>(accessors); } public Map<String, PropertyAccessor> getPropertyAccessors() { return this.propertyAccessors; } public void setFunctions(Map<String, Method> functionsArg) { Assert.isTrue(!this.initialized, "'functions' can't be changed after initialization."); Assert.notNull(functionsArg, "'functions' must not be null."); Assert.noNullElements(functionsArg.values().toArray(), "'functions' cannot have null values."); this.functions = new LinkedHashMap<String, Method>(functionsArg); } public Map<String, Method> getFunctions() { return this.functions; } public void setTypeLocator(TypeLocator typeLocator) { this.typeLocator = typeLocator; } @Override public void afterPropertiesSet() throws Exception { if (this.applicationContext != null) { this.beanResolver = new BeanFactoryResolver(this.applicationContext); ConversionService conversionService = IntegrationUtils.getConversionService(this.applicationContext); if (conversionService != null) { this.typeConverter = new StandardTypeConverter(conversionService); } Map<String, SpelFunctionFactoryBean> functionFactoryBeanMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, SpelFunctionFactoryBean.class); for (SpelFunctionFactoryBean spelFunctionFactoryBean : functionFactoryBeanMap.values()) { if (!this.functions.containsKey(spelFunctionFactoryBean.getFunctionName())) { this.functions.put(spelFunctionFactoryBean.getFunctionName(), spelFunctionFactoryBean.getObject()); } } try { SpelPropertyAccessorRegistrar propertyAccessorRegistrar = this.applicationContext.getBean(SpelPropertyAccessorRegistrar.class); for (Entry<String, PropertyAccessor> entry : propertyAccessorRegistrar.getPropertyAccessors().entrySet()) { if (!this.propertyAccessors.containsKey(entry.getKey())) { this.propertyAccessors.put(entry.getKey(), entry.getValue()); } } } catch (NoSuchBeanDefinitionException e) { // There is no 'SpelPropertyAccessorRegistrar' bean in the application context. } ApplicationContext parent = this.applicationContext.getParent(); if (parent != null && parent.containsBean(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)) { IntegrationEvaluationContextFactoryBean parentFactoryBean = parent.getBean("&" + IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME, IntegrationEvaluationContextFactoryBean.class); for (Entry<String, PropertyAccessor> entry : parentFactoryBean.getPropertyAccessors().entrySet()) { if (!this.propertyAccessors.containsKey(entry.getKey())) { this.propertyAccessors.put(entry.getKey(), entry.getValue()); } } for (Entry<String, Method> entry : parentFactoryBean.getFunctions().entrySet()) { if (!this.functions.containsKey(entry.getKey())) { this.functions.put(entry.getKey(), entry.getValue()); } } } } this.initialized = true; } @Override public StandardEvaluationContext getObject() throws Exception { StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); if (this.typeLocator != null) { evaluationContext.setTypeLocator(this.typeLocator); } evaluationContext.setBeanResolver(this.beanResolver); evaluationContext.setTypeConverter(this.typeConverter); for (PropertyAccessor propertyAccessor : this.propertyAccessors.values()) { evaluationContext.addPropertyAccessor(propertyAccessor); } evaluationContext.addPropertyAccessor(new MapAccessor()); for (Entry<String, Method> functionEntry : this.functions.entrySet()) { evaluationContext.registerFunction(functionEntry.getKey(), functionEntry.getValue()); } return evaluationContext; } @Override public Class<?> getObjectType() { return StandardEvaluationContext.class; } @Override public boolean isSingleton() { return false; } }