/* * Copyright 2002-2016 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.context; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.Advised; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.integration.support.DefaultMessageBuilderFactory; import org.springframework.integration.support.MessageBuilderFactory; import org.springframework.integration.support.channel.BeanFactoryChannelResolver; import org.springframework.integration.support.context.NamedComponent; import org.springframework.integration.support.utils.IntegrationUtils; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.core.DestinationResolver; import org.springframework.scheduling.TaskScheduler; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * A base class that provides convenient access to the bean factory as * well as {@link TaskScheduler} and {@link ConversionService} instances. * * <p>This is intended to be used as a base class for internal framework * components whereas code built upon the integration framework should not * require tight coupling with the context but rather rely on standard * dependency injection. * * @author Mark Fisher * @author Oleg Zhurakousky * @author Josh Long * @author Stefan Ferstl * @author Gary Russell * @author Artem Bilan */ public abstract class IntegrationObjectSupport implements BeanNameAware, NamedComponent, ApplicationContextAware, BeanFactoryAware, InitializingBean, ExpressionCapable { protected static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); /** * Logger that is available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); private final ConversionService defaultConversionService = DefaultConversionService.getSharedInstance(); private volatile DestinationResolver<MessageChannel> channelResolver; private volatile String beanName; private volatile String componentName; private volatile BeanFactory beanFactory; private volatile TaskScheduler taskScheduler; private volatile Properties integrationProperties = IntegrationProperties.defaults(); private volatile ConversionService conversionService; private volatile ApplicationContext applicationContext; private volatile MessageBuilderFactory messageBuilderFactory; private Expression expression; @Override public final void setBeanName(String beanName) { this.beanName = beanName; } /** * Will return the name of this component identified by {@link #componentName} field. * If {@link #componentName} was not set this method will default to the 'beanName' of this component; */ @Override public String getComponentName() { return StringUtils.hasText(this.componentName) ? this.componentName : this.beanName; } /** * Sets the name of this component. * @param componentName The component name. */ public void setComponentName(String componentName) { this.componentName = componentName; } /** * Subclasses may implement this method to provide component type information. */ @Override public String getComponentType() { return null; } @Override public void setBeanFactory(BeanFactory beanFactory) { Assert.notNull(beanFactory, "'beanFactory' must not be null"); this.beanFactory = beanFactory; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Assert.notNull(applicationContext, "'applicationContext' must not be null"); this.applicationContext = applicationContext; } /** * Specify the {@link DestinationResolver} strategy to use. * The default is a BeanFactoryChannelResolver. * @param channelResolver The channel resolver. */ public void setChannelResolver(DestinationResolver<MessageChannel> channelResolver) { Assert.notNull(channelResolver, "'channelResolver' must not be null"); this.channelResolver = channelResolver; } @Override public Expression getExpression() { return this.expression; } /** * For expression-based components, set the primary expression. * @param expression the expression. * @since 4.3 */ public final void setPrimaryExpression(Expression expression) { this.expression = expression; } @Override public final void afterPropertiesSet() { this.integrationProperties = IntegrationContextUtils.getIntegrationProperties(this.beanFactory); try { if (this.messageBuilderFactory == null) { if (this.beanFactory != null) { this.messageBuilderFactory = IntegrationUtils.getMessageBuilderFactory(this.beanFactory); } else { this.messageBuilderFactory = new DefaultMessageBuilderFactory(); } } this.onInit(); } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new BeanInitializationException("failed to initialize", e); } } /** * Subclasses may implement this for initialization logic. * @throws Exception Any exception. */ protected void onInit() throws Exception { } protected BeanFactory getBeanFactory() { return this.beanFactory; } protected TaskScheduler getTaskScheduler() { if (this.taskScheduler == null && this.beanFactory != null) { this.taskScheduler = IntegrationContextUtils.getTaskScheduler(this.beanFactory); } return this.taskScheduler; } protected DestinationResolver<MessageChannel> getChannelResolver() { if (this.channelResolver == null) { this.channelResolver = new BeanFactoryChannelResolver(this.beanFactory); } return this.channelResolver; } protected void setTaskScheduler(TaskScheduler taskScheduler) { Assert.notNull(taskScheduler, "taskScheduler must not be null"); this.taskScheduler = taskScheduler; } public ConversionService getConversionService() { if (this.conversionService == null && this.beanFactory != null) { synchronized (this) { if (this.conversionService == null) { this.conversionService = IntegrationUtils.getConversionService(this.beanFactory); } } if (this.conversionService == null && this.logger.isDebugEnabled()) { this.logger.debug("Unable to attempt conversion of Message payload types. Component '" + this.getComponentName() + "' has no explicit ConversionService reference, " + "and there is no 'integrationConversionService' bean within the context."); } } return this.conversionService; } protected void setConversionService(ConversionService conversionService) { this.conversionService = conversionService; } /** * Returns the {@link ApplicationContext#getId()} if the * {@link ApplicationContext} is available. * @return The id, or null if there is no application context. */ public String getApplicationContextId() { return this.applicationContext == null ? null : this.applicationContext.getId(); } /** * @return the applicationContext */ protected ApplicationContext getApplicationContext() { return this.applicationContext; } /** * @see IntegrationContextUtils#getIntegrationProperties(BeanFactory) * @return The global integration properties. */ protected Properties getIntegrationProperties() { return this.integrationProperties; } protected MessageBuilderFactory getMessageBuilderFactory() { if (this.messageBuilderFactory == null) { this.messageBuilderFactory = new DefaultMessageBuilderFactory(); } return this.messageBuilderFactory; } public void setMessageBuilderFactory(MessageBuilderFactory messageBuilderFactory) { this.messageBuilderFactory = messageBuilderFactory; } /** * @param key Integration property. * @param tClass the class to convert a value of Integration property. * @param <T> The expected type of the property. * @return the value of the Integration property converted to the provide type. */ protected <T> T getIntegrationProperty(String key, Class<T> tClass) { return this.defaultConversionService.convert(this.integrationProperties.getProperty(key), tClass); } @SuppressWarnings("unchecked") protected <T> T extractTypeIfPossible(Object targetObject, Class<T> expectedType) { if (targetObject == null) { return null; } if (expectedType.isAssignableFrom(targetObject.getClass())) { return (T) targetObject; } if (targetObject instanceof Advised) { TargetSource targetSource = ((Advised) targetObject).getTargetSource(); if (targetSource == null) { return null; } try { return extractTypeIfPossible(targetSource.getTarget(), expectedType); } catch (Exception e) { throw new IllegalStateException(e); } } return null; } @Override public String toString() { return (this.beanName != null) ? this.beanName : super.toString(); } }