/*
* Copyright 2016-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.dsl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationNotAllowedException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.FixedSubscriberChannel;
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.integration.config.IntegrationConfigUtils;
import org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.dsl.ComponentsRegistration;
import org.springframework.integration.dsl.ConsumerEndpointSpec;
import org.springframework.integration.dsl.IntegrationComponentSpec;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlowBuilder;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.SourcePollingChannelAdapterSpec;
import org.springframework.integration.dsl.StandardIntegrationFlow;
import org.springframework.integration.dsl.support.MessageChannelReference;
import org.springframework.integration.gateway.AnnotationGatewayProxyFactoryBean;
import org.springframework.integration.support.context.NamedComponent;
import org.springframework.messaging.MessageHandler;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* A {@link BeanPostProcessor} to parse {@link IntegrationFlow} beans and
* register their components as beans in the provided {@link BeanFactory},
* if necessary.
*
* @author Artem Bilan
* @since 5.0
*/
public class IntegrationFlowBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware,
SmartInitializingSingleton {
private final Set<ApplicationListener<?>> applicationListeners = new HashSet<ApplicationListener<?>>();
private ConfigurableListableBeanFactory beanFactory;
private AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
"To use Spring Integration Java DSL the 'beanFactory' has to be an instance of " +
"'ConfigurableListableBeanFactory'. Consider using 'GenericApplicationContext' implementation."
);
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
this.autowiredAnnotationBeanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
this.autowiredAnnotationBeanPostProcessor.setBeanFactory(this.beanFactory);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof StandardIntegrationFlow) {
return processStandardIntegrationFlow((StandardIntegrationFlow) bean, beanName);
}
else if (bean instanceof IntegrationFlow) {
return processIntegrationFlowImpl((IntegrationFlow) bean, beanName);
}
if (bean instanceof IntegrationComponentSpec) {
processIntegrationComponentSpec((IntegrationComponentSpec<?, ?>) bean);
}
return bean;
}
@Override
public void afterSingletonsInstantiated() {
if (this.beanFactory.containsBean(AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
ApplicationEventMulticaster multicaster =
(ApplicationEventMulticaster) this.beanFactory.getBean(
AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME);
this.applicationListeners.forEach(multicaster::addApplicationListener);
}
for (String beanName : this.beanFactory.getBeanNamesForType(IntegrationFlow.class)) {
if (this.beanFactory.containsBeanDefinition(beanName)) {
String scope = this.beanFactory.getBeanDefinition(beanName).getScope();
if (StringUtils.hasText(scope) && !BeanDefinition.SCOPE_SINGLETON.equals(scope)) {
throw new BeanCreationNotAllowedException(beanName, "IntegrationFlows can not be scoped beans. " +
"Any dependant beans are registered as singletons, meanwhile IntegrationFlow is just a " +
"logical container for them. \n" +
"Consider to use [IntegrationFlowContext] for manual registration of IntegrationFlows.");
}
}
}
}
private Object processStandardIntegrationFlow(StandardIntegrationFlow flow, String beanName) {
String flowNamePrefix = beanName + ".";
int subFlowNameIndex = 0;
int channelNameIndex = 0;
boolean registerSingleton = flow.isRegisterComponents();
List<Object> integrationComponents = new ArrayList<>(flow.getIntegrationComponents());
for (int i = 0; i < integrationComponents.size(); i++) {
Object component = integrationComponents.get(i);
if (component instanceof ConsumerEndpointSpec) {
ConsumerEndpointSpec<?, ?> endpointSpec = (ConsumerEndpointSpec<?, ?>) component;
MessageHandler messageHandler = endpointSpec.get().getT2();
ConsumerEndpointFactoryBean endpoint = endpointSpec.get().getT1();
String id = endpointSpec.getId();
Collection<?> messageHandlers = this.beanFactory.getBeansOfType(messageHandler.getClass(), false,
false).values();
if (!messageHandlers.contains(messageHandler)) {
String handlerBeanName = generateBeanName(messageHandler);
String[] handlerAlias = id != null
? new String[] { id + IntegrationConfigUtils.HANDLER_ALIAS_SUFFIX }
: null;
registerComponent(messageHandler, handlerBeanName, beanName, registerSingleton);
if (handlerAlias != null) {
for (String alias : handlerAlias) {
this.beanFactory.registerAlias(handlerBeanName, alias);
}
}
}
String endpointBeanName = id;
if (endpointBeanName == null) {
endpointBeanName = generateBeanName(endpoint);
}
registerComponent(endpoint, endpointBeanName, beanName, registerSingleton);
integrationComponents.set(i, endpoint);
}
else {
Collection<?> values = this.beanFactory.getBeansOfType(component.getClass(), false, false).values();
if (!values.contains(component)) {
if (component instanceof AbstractMessageChannel) {
String channelBeanName = ((AbstractMessageChannel) component).getComponentName();
if (channelBeanName == null) {
channelBeanName = flowNamePrefix + "channel" +
BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + channelNameIndex++;
}
registerComponent(component, channelBeanName, beanName, registerSingleton);
}
else if (component instanceof MessageChannelReference) {
String channelBeanName = ((MessageChannelReference) component).getName();
if (!this.beanFactory.containsBean(channelBeanName)) {
DirectChannel directChannel = new DirectChannel();
registerComponent(directChannel, channelBeanName, beanName, registerSingleton);
integrationComponents.set(i, directChannel);
}
}
else if (component instanceof FixedSubscriberChannel) {
FixedSubscriberChannel fixedSubscriberChannel = (FixedSubscriberChannel) component;
String channelBeanName = fixedSubscriberChannel.getComponentName();
if ("Unnamed fixed subscriber channel".equals(channelBeanName)) {
channelBeanName = flowNamePrefix + "channel" +
BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + channelNameIndex++;
}
registerComponent(component, channelBeanName, beanName, registerSingleton);
}
else if (component instanceof SourcePollingChannelAdapterSpec) {
SourcePollingChannelAdapterSpec spec = (SourcePollingChannelAdapterSpec) component;
Collection<Object> componentsToRegister = spec.getComponentsToRegister();
if (!CollectionUtils.isEmpty(componentsToRegister)) {
componentsToRegister.stream()
.filter(o -> !this.beanFactory.getBeansOfType(o.getClass(), false, false)
.values()
.contains(o))
.forEach(o -> registerComponent(o, generateBeanName(o))
);
}
SourcePollingChannelAdapterFactoryBean pollingChannelAdapterFactoryBean = spec.get().getT1();
String id = spec.getId();
if (!StringUtils.hasText(id)) {
id = generateBeanName(pollingChannelAdapterFactoryBean);
}
registerComponent(pollingChannelAdapterFactoryBean, id, beanName, registerSingleton);
integrationComponents.set(i, pollingChannelAdapterFactoryBean);
MessageSource<?> messageSource = spec.get().getT2();
if (!this.beanFactory.getBeansOfType(messageSource.getClass(), false, false)
.values()
.contains(messageSource)) {
String messageSourceId = id + ".source";
if (messageSource instanceof NamedComponent
&& ((NamedComponent) messageSource).getComponentName() != null) {
messageSourceId = ((NamedComponent) messageSource).getComponentName();
}
registerComponent(messageSource, messageSourceId, beanName, registerSingleton);
}
}
else if (component instanceof StandardIntegrationFlow) {
String subFlowBeanName = flowNamePrefix + "subFlow" +
BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + subFlowNameIndex++;
registerComponent(component, subFlowBeanName, beanName, registerSingleton);
}
else if (component instanceof AnnotationGatewayProxyFactoryBean) {
registerComponent(component, flowNamePrefix + "gateway", beanName, registerSingleton);
}
else {
String generateBeanName = generateBeanName(component);
registerComponent(component, generateBeanName, beanName, registerSingleton);
}
}
}
}
flow.setIntegrationComponents(integrationComponents);
return flow;
}
private Object processIntegrationFlowImpl(IntegrationFlow flow, String beanName) {
IntegrationFlowBuilder flowBuilder = IntegrationFlows.from(beanName + ".input");
flow.configure(flowBuilder);
Object standardIntegrationFlow = processStandardIntegrationFlow(flowBuilder.get(), beanName);
return isLambda(flow) ? standardIntegrationFlow : flow;
}
private void processIntegrationComponentSpec(IntegrationComponentSpec<?, ?> bean) {
registerComponent(bean.get(), generateBeanName(bean.get()), null, false);
if (bean instanceof ComponentsRegistration) {
Collection<Object> componentsToRegister = ((ComponentsRegistration) bean).getComponentsToRegister();
if (!CollectionUtils.isEmpty(componentsToRegister)) {
componentsToRegister.stream()
.filter(component -> !this.beanFactory.getBeansOfType(component.getClass(), false, false)
.values()
.contains(component))
.forEach(component -> registerComponent(component, generateBeanName(component)));
}
}
}
private void registerComponent(Object component, String beanName) {
registerComponent(component, beanName, null, true);
}
private void registerComponent(Object component, String beanName, String parentName, boolean registerSingleton) {
if (component instanceof ApplicationListener) {
this.applicationListeners.add((ApplicationListener<?>) component);
}
this.autowiredAnnotationBeanPostProcessor.processInjection(component);
this.beanFactory.initializeBean(component, beanName);
if (registerSingleton) {
this.beanFactory.registerSingleton(beanName, component);
if (parentName != null) {
this.beanFactory.registerDependentBean(parentName, beanName);
}
}
if (component instanceof DisposableBean) {
((DefaultSingletonBeanRegistry) this.beanFactory)
.registerDisposableBean(beanName, (DisposableBean) component);
}
}
private String generateBeanName(Object instance) {
if (instance instanceof NamedComponent && ((NamedComponent) instance).getComponentName() != null) {
return ((NamedComponent) instance).getComponentName();
}
String generatedBeanName = instance.getClass().getName();
String id = generatedBeanName;
int counter = -1;
while (counter == -1 || this.beanFactory.containsBean(id)) {
counter++;
id = generatedBeanName + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + counter;
}
return id;
}
private static boolean isLambda(Object o) {
Class<?> aClass = o.getClass();
return aClass.isSynthetic() && !aClass.isAnonymousClass() && !aClass.isLocalClass();
}
}