From 3f0c57894b085398bcbc02278a764b8d2eb4158d Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Tue, 6 Apr 2021 10:06:07 -0400 Subject: [PATCH] GH-3502: More refactoring to avoid reflection (#3532) * GH-3502: More refactoring to avoid reflection Fixes https://github.com/spring-projects/spring-integration/issues/3502 * Move `ChannelInitializer` bean registration into an `AbstractIntegrationNamespaceHandler` - it was never used for annotations and Java DSL... * Rework `IntegrationFlows.fromSupplier()` to call a provided `Supplier` directly - not via reflection in the `MethodInvokingMessageSource` * Resolve new Sonar smells * Rework `EndpointSpec` to accept an expected factory bean instance via ctor arg instead of reflection * Rework `Jackson2JsonObjectMapper` to use well-known module instances directly - not via reflection from their class names * * Revert `DefaultMethodInvokingMethodInterceptor.methodHandleCache` property definition wrap --- build.gradle | 12 +- .../config/ChannelInitializer.java | 23 +-- ...ltConfiguringBeanFactoryPostProcessor.java | 147 ++++++++---------- .../GlobalChannelInterceptorInitializer.java | 62 ++++++-- .../config/IntegrationConfigUtils.java | 26 +++- .../config/IntegrationRegistrar.java | 68 ++------ .../config/PublisherRegistrar.java | 89 ++++++----- .../AbstractIntegrationNamespaceHandler.java | 46 +++++- .../config/xml/AnnotationConfigParser.java | 22 ++- .../dsl/BaseIntegrationFlowDefinition.java | 3 - .../integration/dsl/ConsumerEndpointSpec.java | 2 +- .../integration/dsl/EndpointSpec.java | 18 +-- .../integration/dsl/IntegrationFlows.java | 21 ++- .../dsl/SourcePollingChannelAdapterSpec.java | 4 +- ...slIntegrationConfigurationInitializer.java | 10 +- ...efaultMethodInvokingMethodInterceptor.java | 4 +- .../JsonNodeWrapperToJsonNodeConverter.java | 2 +- .../json/Jackson2JsonObjectMapper.java | 99 ++++++------ .../EnableIntegrationTests-context.xml | 4 - .../configuration/EnableIntegrationTests.java | 10 +- .../http/dsl/BaseHttpMessageHandlerSpec.java | 8 +- 21 files changed, 372 insertions(+), 308 deletions(-) diff --git a/build.gradle b/build.gradle index 760edee5f2..693f395447 100644 --- a/build.gradle +++ b/build.gradle @@ -440,7 +440,15 @@ project('spring-integration-core') { exclude group: 'org.springframework' } api 'io.projectreactor:reactor-core' + optionalApi 'com.fasterxml.jackson.core:jackson-databind' + optionalApi 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8' + optionalApi 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' + optionalApi 'com.fasterxml.jackson.datatype:jackson-datatype-joda' + optionalApi ('com.fasterxml.jackson.module:jackson-module-kotlin') { + exclude group: 'org.jetbrains.kotlin' + } + optionalApi "com.jayway.jsonpath:json-path:$jsonpathVersion" optionalApi "com.esotericsoftware:kryo-shaded:$kryoShadedVersion" optionalApi "io.micrometer:micrometer-core:$micrometerVersion" @@ -449,10 +457,6 @@ project('spring-integration-core') { optionalApi 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' testImplementation "org.aspectj:aspectjweaver:$aspectjVersion" - testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' - testRuntime ('com.fasterxml.jackson.module:jackson-module-kotlin') { - exclude group: 'org.jetbrains.kotlin' - } testImplementation "org.hamcrest:hamcrest-core:$hamcrestVersion" } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/ChannelInitializer.java b/spring-integration-core/src/main/java/org/springframework/integration/config/ChannelInitializer.java index 0276592412..92a5661b5a 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/ChannelInitializer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/ChannelInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 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. @@ -40,16 +40,20 @@ import org.springframework.util.Assert; * * @author Oleg Zhurakousky * @author Gary Russell + * @author Artem Bilan + * * @since 2.1.1 */ -final class ChannelInitializer implements BeanFactoryAware, InitializingBean { +public final class ChannelInitializer implements BeanFactoryAware, InitializingBean { - private final Log logger = LogFactory.getLog(this.getClass()); + private static final Log LOGGER = LogFactory.getLog(ChannelInitializer.class); private volatile BeanFactory beanFactory; private volatile boolean autoCreate = true; + ChannelInitializer() { + } public void setAutoCreate(boolean autoCreate) { this.autoCreate = autoCreate; @@ -68,17 +72,18 @@ final class ChannelInitializer implements BeanFactoryAware, InitializingBean { } else { AutoCreateCandidatesCollector channelCandidatesCollector = - this.beanFactory.getBean(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME, AutoCreateCandidatesCollector.class); - Assert.notNull(channelCandidatesCollector, "Failed to locate '" + IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); + this.beanFactory.getBean(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME, + AutoCreateCandidatesCollector.class); // at this point channelNames are all resolved with placeholders and SpEL Collection channelNames = channelCandidatesCollector.getChannelNames(); if (channelNames != null) { for (String channelName : channelNames) { if (!this.beanFactory.containsBean(channelName)) { - if (this.logger.isDebugEnabled()) { - this.logger.debug("Auto-creating channel '" + channelName + "' as DirectChannel"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Auto-creating channel '" + channelName + "' as DirectChannel"); } - IntegrationConfigUtils.autoCreateDirectChannel(channelName, (BeanDefinitionRegistry) this.beanFactory); + IntegrationConfigUtils.autoCreateDirectChannel(channelName, + (BeanDefinitionRegistry) this.beanFactory); } } } @@ -88,7 +93,7 @@ final class ChannelInitializer implements BeanFactoryAware, InitializingBean { /* * Collects candidate channel names to be auto-created by ChannelInitializer */ - static class AutoCreateCandidatesCollector { + public static class AutoCreateCandidatesCollector { private final Collection channelNames; diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java index d51b17dccf..78c4d571e9 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java @@ -32,7 +32,6 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.HierarchicalBeanFactory; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.PropertiesFactoryBean; @@ -58,6 +57,7 @@ import org.springframework.integration.handler.support.CollectionArgumentResolve import org.springframework.integration.handler.support.MapArgumentResolver; import org.springframework.integration.handler.support.PayloadExpressionArgumentResolver; import org.springframework.integration.handler.support.PayloadsArgumentResolver; +import org.springframework.integration.json.JsonNodeWrapperToJsonNodeConverter; import org.springframework.integration.support.DefaultMessageBuilderFactory; import org.springframework.integration.support.NullAwarePayloadArgumentResolver; import org.springframework.integration.support.SmartLifecycleRoleController; @@ -67,7 +67,6 @@ import org.springframework.integration.support.converter.ConfigurableCompositeMe import org.springframework.integration.support.converter.DefaultDatatypeChannelMessageConverter; import org.springframework.integration.support.json.JacksonPresent; import org.springframework.integration.support.utils.IntegrationUtils; -import org.springframework.lang.Nullable; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory; @@ -75,6 +74,7 @@ import org.springframework.messaging.handler.invocation.HandlerMethodArgumentRes import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.util.ClassUtils; import org.springframework.util.ErrorHandler; +import org.springframework.util.StringUtils; /** * A {@link BeanFactoryPostProcessor} implementation that registers bean definitions @@ -100,12 +100,44 @@ class DefaultConfiguringBeanFactoryPostProcessor private static final Set REGISTRIES_PROCESSED = new HashSet<>(); + + private static final Class XPATH_CLASS; + + private static final Class JSON_PATH_CLASS; + + static { + Class xpathClass = null; + try { + xpathClass = ClassUtils.forName(IntegrationConfigUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", + ClassUtils.getDefaultClassLoader()); + } + catch (@SuppressWarnings("unused") ClassNotFoundException e) { + LOGGER.debug("SpEL function '#xpath' isn't registered: " + + "there is no spring-integration-xml.jar on the classpath."); + } + finally { + XPATH_CLASS = xpathClass; + } + + Class jsonPathClass = null; + try { + jsonPathClass = ClassUtils.forName(IntegrationConfigUtils.BASE_PACKAGE + ".json.JsonPathUtils", + ClassUtils.getDefaultClassLoader()); + } + catch (@SuppressWarnings("unused") ClassNotFoundException e) { + LOGGER.debug("The '#jsonPath' SpEL function cannot be registered: " + + "there is no jayway json-path.jar on the classpath."); + } + finally { + JSON_PATH_CLASS = jsonPathClass; + } + } + + private ClassLoader classLoader; private ConfigurableListableBeanFactory beanFactory; - private BeanExpressionContext expressionContext; - private BeanDefinitionRegistry registry; @Override @@ -117,7 +149,6 @@ class DefaultConfiguringBeanFactoryPostProcessor public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (beanFactory instanceof BeanDefinitionRegistry) { this.beanFactory = beanFactory; - this.expressionContext = new BeanExpressionContext(beanFactory, null); this.registry = (BeanDefinitionRegistry) beanFactory; registerBeanFactoryChannelResolver(); @@ -242,16 +273,14 @@ class DefaultConfiguringBeanFactoryPostProcessor } private PublishSubscribeChannel createErrorChannel() { - String requireSubscribersExpression = - IntegrationProperties.getExpressionFor(IntegrationProperties.ERROR_CHANNEL_REQUIRE_SUBSCRIBERS); - Boolean requireSubscribers = resolveExpression(requireSubscribersExpression, Boolean.class); + Properties integrationProperties = IntegrationContextUtils.getIntegrationProperties(this.beanFactory); + String requireSubscribers = + integrationProperties.getProperty(IntegrationProperties.ERROR_CHANNEL_REQUIRE_SUBSCRIBERS); - PublishSubscribeChannel errorChannel = new PublishSubscribeChannel(Boolean.TRUE.equals(requireSubscribers)); + PublishSubscribeChannel errorChannel = new PublishSubscribeChannel(Boolean.parseBoolean(requireSubscribers)); - String ignoreFailuresExpression = - IntegrationProperties.getExpressionFor(IntegrationProperties.ERROR_CHANNEL_IGNORE_FAILURES); - Boolean ignoreFailures = resolveExpression(ignoreFailuresExpression, Boolean.class); - errorChannel.setIgnoreFailures(Boolean.TRUE.equals(ignoreFailures)); + String ignoreFailures = integrationProperties.getProperty(IntegrationProperties.ERROR_CHANNEL_IGNORE_FAILURES); + errorChannel.setIgnoreFailures(Boolean.parseBoolean(ignoreFailures)); return errorChannel; } @@ -323,12 +352,9 @@ class DefaultConfiguringBeanFactoryPostProcessor taskScheduler.setErrorHandler( this.beanFactory.getBean(ChannelUtils.MESSAGE_PUBLISHING_ERROR_HANDLER_BEAN_NAME, ErrorHandler.class)); - String poolSizeExpression = - IntegrationProperties.getExpressionFor(IntegrationProperties.TASK_SCHEDULER_POOL_SIZE); - Integer poolSize = resolveExpression(poolSizeExpression, Integer.class); - if (poolSize != null) { - taskScheduler.setPoolSize(poolSize); - } + Properties integrationProperties = IntegrationContextUtils.getIntegrationProperties(this.beanFactory); + String poolSize = integrationProperties.getProperty(IntegrationProperties.TASK_SCHEDULER_POOL_SIZE); + taskScheduler.setPoolSize(Integer.parseInt(poolSize)); return taskScheduler; } @@ -376,54 +402,21 @@ class DefaultConfiguringBeanFactoryPostProcessor private void jsonPath(int registryId) throws LinkageError { String jsonPathBeanName = "jsonPath"; - if (!this.beanFactory.containsBean(jsonPathBeanName) && !REGISTRIES_PROCESSED.contains(registryId)) { - Class jsonPathClass = null; - try { - jsonPathClass = ClassUtils.forName("com.jayway.jsonpath.JsonPath", this.classLoader); - } - catch (@SuppressWarnings("unused") ClassNotFoundException e) { - LOGGER.debug("The '#jsonPath' SpEL function cannot be registered: " + - "there is no jayway json-path.jar on the classpath."); - } + if (JSON_PATH_CLASS != null + && !this.beanFactory.containsBean(jsonPathBeanName) + && !REGISTRIES_PROCESSED.contains(registryId)) { - if (jsonPathClass != null) { - try { - ClassUtils.forName("com.jayway.jsonpath.Predicate", this.classLoader); - } - catch (ClassNotFoundException ex) { - jsonPathClass = null; - LOGGER.warn(ex, "The '#jsonPath' SpEL function cannot be registered. " + - "An old json-path.jar version is detected in the classpath." + - "At least 2.4.0 is required; see version information at: " + - "https://github.com/jayway/JsonPath/releases"); - - } - } - - if (jsonPathClass != null) { - IntegrationConfigUtils.registerSpelFunctionBean(this.registry, jsonPathBeanName, - IntegrationConfigUtils.BASE_PACKAGE + ".json.JsonPathUtils", "evaluate"); - } + IntegrationConfigUtils.registerSpelFunctionBean(this.registry, jsonPathBeanName, JSON_PATH_CLASS, "evaluate"); } } private void xpath(int registryId) throws LinkageError { String xpathBeanName = "xpath"; - if (!this.beanFactory.containsBean(xpathBeanName) && !REGISTRIES_PROCESSED.contains(registryId)) { - Class xpathClass = null; - try { - xpathClass = ClassUtils.forName(IntegrationConfigUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", - this.classLoader); - } - catch (@SuppressWarnings("unused") ClassNotFoundException e) { - LOGGER.debug("SpEL function '#xpath' isn't registered: " + - "there is no spring-integration-xml.jar on the classpath."); - } + if (XPATH_CLASS != null + && !this.beanFactory.containsBean(xpathBeanName) + && !REGISTRIES_PROCESSED.contains(registryId)) { - if (xpathClass != null) { - IntegrationConfigUtils.registerSpelFunctionBean(this.registry, xpathBeanName, - IntegrationConfigUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", "evaluate"); - } + IntegrationConfigUtils.registerSpelFunctionBean(this.registry, xpathBeanName, XPATH_CLASS, "evaluate"); } } @@ -434,9 +427,9 @@ class DefaultConfiguringBeanFactoryPostProcessor this.registry.registerBeanDefinition( IntegrationContextUtils.JSON_NODE_WRAPPER_TO_JSON_NODE_CONVERTER, - BeanDefinitionBuilder.genericBeanDefinition(IntegrationConfigUtils.BASE_PACKAGE + - ".json.JsonNodeWrapperToJsonNodeConverter") - .getBeanDefinition()); + new RootBeanDefinition(JsonNodeWrapperToJsonNodeConverter.class, + JsonNodeWrapperToJsonNodeConverter::new)); + INTEGRATION_CONVERTER_INITIALIZER.registerConverter(this.registry, new RuntimeBeanReference(IntegrationContextUtils.JSON_NODE_WRAPPER_TO_JSON_NODE_CONVERTER)); } @@ -457,21 +450,19 @@ class DefaultConfiguringBeanFactoryPostProcessor */ private void registerMessageBuilderFactory() { if (!this.beanFactory.containsBean(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME)) { - BeanDefinition mbfBean = - new RootBeanDefinition(DefaultMessageBuilderFactory.class, - () -> { - DefaultMessageBuilderFactory messageBuilderFactory = new DefaultMessageBuilderFactory(); - String readOnlyHeadersExpression = - IntegrationProperties.getExpressionFor(IntegrationProperties.READ_ONLY_HEADERS); - String[] readOnlyHeaders = resolveExpression(readOnlyHeadersExpression, String[].class); - messageBuilderFactory.setReadOnlyHeaders(readOnlyHeaders); - return messageBuilderFactory; - }); - - this.registry.registerBeanDefinition(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME, mbfBean); + this.registry.registerBeanDefinition(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME, + new RootBeanDefinition(DefaultMessageBuilderFactory.class, this::createDefaultMessageBuilderFactory)); } } + private DefaultMessageBuilderFactory createDefaultMessageBuilderFactory() { + DefaultMessageBuilderFactory messageBuilderFactory = new DefaultMessageBuilderFactory(); + Properties integrationProperties = IntegrationContextUtils.getIntegrationProperties(this.beanFactory); + String readOnlyHeaders = integrationProperties.getProperty(IntegrationProperties.READ_ONLY_HEADERS); + messageBuilderFactory.setReadOnlyHeaders(StringUtils.commaDelimitedListToStringArray(readOnlyHeaders)); + return messageBuilderFactory; + } + /** * Register a {@link DefaultHeaderChannelRegistry} if necessary. */ @@ -585,10 +576,4 @@ class DefaultConfiguringBeanFactoryPostProcessor return resolvers; } - @Nullable - private T resolveExpression(String expression, Class expectedType) { - Object value = this.beanFactory.getBeanExpressionResolver().evaluate(expression, this.expressionContext); - return this.beanFactory.getTypeConverter().convertIfNecessary(value, expectedType); - } - } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorInitializer.java b/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorInitializer.java index 20affe115b..c56e4125ac 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorInitializer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/GlobalChannelInterceptorInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2021 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. @@ -16,18 +16,22 @@ package org.springframework.integration.config; +import java.util.Arrays; import java.util.Map; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanExpressionContext; +import org.springframework.beans.factory.config.BeanExpressionResolver; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.integration.channel.interceptor.GlobalChannelInterceptorWrapper; +import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.util.CollectionUtils; /** @@ -39,38 +43,72 @@ import org.springframework.util.CollectionUtils; * * @author Artem Bilan * @author Gary Russell + * * @since 4.0 */ public class GlobalChannelInterceptorInitializer implements IntegrationConfigurationInitializer { + private ConfigurableListableBeanFactory beanFactory; + + private BeanExpressionContext beanExpressionContext; + @Override public void initialize(ConfigurableListableBeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; - + this.beanExpressionContext = new BeanExpressionContext(beanFactory, null); for (String beanName : registry.getBeanDefinitionNames()) { BeanDefinition beanDefinition = registry.getBeanDefinition(beanName); if (beanDefinition instanceof AnnotatedBeanDefinition) { AnnotationMetadata metadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata(); - Map annotationAttributes = metadata - .getAnnotationAttributes(GlobalChannelInterceptor.class.getName()); + Map annotationAttributes = + metadata.getAnnotationAttributes(GlobalChannelInterceptor.class.getName()); if (CollectionUtils.isEmpty(annotationAttributes) && beanDefinition.getSource() instanceof MethodMetadata) { MethodMetadata beanMethod = (MethodMetadata) beanDefinition.getSource(); annotationAttributes = - beanMethod.getAnnotationAttributes(GlobalChannelInterceptor.class.getName()); // NOSONAR not null + beanMethod.getAnnotationAttributes(GlobalChannelInterceptor.class.getName()); // NOSONAR not null } if (!CollectionUtils.isEmpty(annotationAttributes)) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder - .genericBeanDefinition(GlobalChannelInterceptorWrapper.class) - .addConstructorArgReference(beanName) - .addPropertyValue("patterns", annotationAttributes.get("patterns")) - .addPropertyValue("order", annotationAttributes.get("order")); + Map attributes = annotationAttributes; + RootBeanDefinition channelInterceptorWrapper = + new RootBeanDefinition(GlobalChannelInterceptorWrapper.class, + () -> createGlobalChannelInterceptorWrapper(beanName, attributes)); - BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), registry); + BeanDefinitionReaderUtils.registerWithGeneratedName(channelInterceptorWrapper, registry); } } } } + private GlobalChannelInterceptorWrapper createGlobalChannelInterceptorWrapper(String interceptorBeanName, + Map annotationAttributes) { + + ChannelInterceptor interceptor = this.beanFactory.getBean(interceptorBeanName, ChannelInterceptor.class); + GlobalChannelInterceptorWrapper interceptorWrapper = new GlobalChannelInterceptorWrapper(interceptor); + String[] patterns = + Arrays.stream((String[]) annotationAttributes.get("patterns")) + .map(this::resolveEmbeddedValue) + .toArray(String[]::new); + interceptorWrapper.setPatterns(patterns); + interceptorWrapper.setOrder((Integer) annotationAttributes.get("order")); + return interceptorWrapper; + } + + private String resolveEmbeddedValue(String value) { + String valueToReturn = this.beanFactory.resolveEmbeddedValue(value); + if (valueToReturn == null || !(valueToReturn.startsWith("#{") && value.endsWith("}"))) { + return valueToReturn; + } + + BeanExpressionResolver beanExpressionResolver = this.beanFactory.getBeanExpressionResolver(); + if (beanExpressionResolver != null) { + Object result = beanExpressionResolver.evaluate(valueToReturn, this.beanExpressionContext); + return result != null ? result.toString() : null; + } + + return null; + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java index f1e0366dbc..c3c7c4b446 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2019 the original author or authors. + * Copyright 2014-2021 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. @@ -16,10 +16,9 @@ package org.springframework.integration.config; -import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.integration.channel.DirectChannel; /** @@ -36,7 +35,7 @@ public final class IntegrationConfigUtils { public static final String HANDLER_ALIAS_SUFFIX = ".handler"; public static void registerSpelFunctionBean(BeanDefinitionRegistry registry, String functionId, String className, - String methodSignature) { + String methodSignature) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SpelFunctionFactoryBean.class) .addConstructorArgValue(className) @@ -44,10 +43,23 @@ public final class IntegrationConfigUtils { registry.registerBeanDefinition(functionId, builder.getBeanDefinition()); } + /** + * Register a {@link SpelFunctionFactoryBean} for the provided method signature + * @param registry the registry for bean to register + * @param functionId the bean name + * @param aClass the class for function + * @param methodSignature the function method to be called from SpEL + * @since 5.5 + */ + public static void registerSpelFunctionBean(BeanDefinitionRegistry registry, String functionId, Class aClass, + String methodSignature) { + + registry.registerBeanDefinition(functionId, new RootBeanDefinition(SpelFunctionFactoryBean.class, + () -> new SpelFunctionFactoryBean(aClass, methodSignature))); + } + public static void autoCreateDirectChannel(String channelName, BeanDefinitionRegistry registry) { - BeanDefinitionBuilder channelBuilder = BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class); - BeanDefinitionHolder holder = new BeanDefinitionHolder(channelBuilder.getBeanDefinition(), channelName); - BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); + registry.registerBeanDefinition(channelName, new RootBeanDefinition(DirectChannel.class, DirectChannel::new)); } private IntegrationConfigUtils() { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java index 834a15bdd6..06c780e173 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java @@ -17,18 +17,14 @@ package org.springframework.integration.config; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.ManagedSet; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContextException; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; -import org.springframework.core.NativeDetector; import org.springframework.core.type.AnnotationMetadata; import org.springframework.integration.config.annotation.MessagingAnnotationPostProcessor; import org.springframework.integration.context.IntegrationContextUtils; -import org.springframework.integration.context.IntegrationProperties; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; @@ -63,42 +59,10 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar { public void registerBeanDefinitions(@Nullable AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { - registerImplicitChannelCreator(registry); registerDefaultConfiguringBeanFactoryPostProcessor(registry); registerIntegrationConfigurationBeanFactoryPostProcessor(registry); if (importingClassMetadata != null) { - registerMessagingAnnotationPostProcessors(importingClassMetadata, registry); - } - } - - /** - * This method will auto-register a ChannelInitializer which could also be overridden by the user - * by simply registering a ChannelInitializer {@code } with its {@code autoCreate} property - * set to false to suppress channel creation. - * It will also register a ChannelInitializer$AutoCreateCandidatesCollector - * which simply collects candidate channel names. - * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s. - */ - private void registerImplicitChannelCreator(BeanDefinitionRegistry registry) { - if (!registry.containsBeanDefinition(IntegrationContextUtils.CHANNEL_INITIALIZER_BEAN_NAME)) { - String channelsAutoCreateExpression = - IntegrationProperties.getExpressionFor(IntegrationProperties.CHANNELS_AUTOCREATE); - BeanDefinitionBuilder channelDef = BeanDefinitionBuilder.genericBeanDefinition(ChannelInitializer.class) - .addPropertyValue("autoCreate", channelsAutoCreateExpression); - BeanDefinitionHolder channelCreatorHolder = new BeanDefinitionHolder(channelDef.getBeanDefinition(), - IntegrationContextUtils.CHANNEL_INITIALIZER_BEAN_NAME); - BeanDefinitionReaderUtils.registerBeanDefinition(channelCreatorHolder, registry); - } - - if (!registry.containsBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) { - BeanDefinitionBuilder channelRegistryBuilder = BeanDefinitionBuilder - .genericBeanDefinition(ChannelInitializer.AutoCreateCandidatesCollector.class); - channelRegistryBuilder.addConstructorArgValue(new ManagedSet()); - channelRegistryBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); //SPR-12761 - BeanDefinitionHolder channelRegistryHolder = - new BeanDefinitionHolder(channelRegistryBuilder.getBeanDefinition(), - IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); - BeanDefinitionReaderUtils.registerBeanDefinition(channelRegistryHolder, registry); + registerMessagingAnnotationPostProcessors(registry); } } @@ -108,13 +72,9 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar { */ private void registerDefaultConfiguringBeanFactoryPostProcessor(BeanDefinitionRegistry registry) { if (!registry.containsBeanDefinition(IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME)) { - BeanDefinitionBuilder postProcessorBuilder = - BeanDefinitionBuilder.genericBeanDefinition(DefaultConfiguringBeanFactoryPostProcessor.class, - DefaultConfiguringBeanFactoryPostProcessor::new); - BeanDefinitionHolder postProcessorHolder = new BeanDefinitionHolder( - postProcessorBuilder.getBeanDefinition(), - IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME); - BeanDefinitionReaderUtils.registerBeanDefinition(postProcessorHolder, registry); + registry.registerBeanDefinition(IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME, + new RootBeanDefinition(DefaultConfiguringBeanFactoryPostProcessor.class, + DefaultConfiguringBeanFactoryPostProcessor::new)); } } @@ -123,13 +83,13 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar { * to process the external Integration infrastructure. */ private void registerIntegrationConfigurationBeanFactoryPostProcessor(BeanDefinitionRegistry registry) { - if (!(NativeDetector.inNativeImage()) // Spring Native detects all the 'spring.factories' - && !registry.containsBeanDefinition( + if (!registry.containsBeanDefinition( IntegrationContextUtils.INTEGRATION_CONFIGURATION_POST_PROCESSOR_BEAN_NAME)) { - BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder - .genericBeanDefinition(IntegrationConfigurationBeanFactoryPostProcessor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + BeanDefinitionBuilder postProcessorBuilder = + BeanDefinitionBuilder.genericBeanDefinition(IntegrationConfigurationBeanFactoryPostProcessor.class, + IntegrationConfigurationBeanFactoryPostProcessor::new) + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_CONFIGURATION_POST_PROCESSOR_BEAN_NAME, postProcessorBuilder.getBeanDefinition()); } @@ -140,10 +100,9 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar { * {@link org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor}, * if necessary. * Inject {@code defaultPublishedChannel} from provided {@link AnnotationMetadata}, if any. - * @param meta The {@link AnnotationMetadata} to get additional properties for {@link BeanDefinition}s. * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s. */ - private void registerMessagingAnnotationPostProcessors(AnnotationMetadata meta, BeanDefinitionRegistry registry) { + private void registerMessagingAnnotationPostProcessors(BeanDefinitionRegistry registry) { if (!registry.containsBeanDefinition(IntegrationContextUtils.MESSAGING_ANNOTATION_POSTPROCESSOR_NAME)) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MessagingAnnotationPostProcessor.class, @@ -153,11 +112,6 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar { registry.registerBeanDefinition(IntegrationContextUtils.MESSAGING_ANNOTATION_POSTPROCESSOR_NAME, builder.getBeanDefinition()); } - - if (meta.getAnnotationAttributes(EnablePublisher.class.getName()) != null) { - new PublisherRegistrar(). - registerBeanDefinitions(meta, registry); - } } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/PublisherRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/PublisherRegistrar.java index a76299587b..3a57485ee4 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/PublisherRegistrar.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/PublisherRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2020 the original author or authors. + * Copyright 2014-2021 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. @@ -21,17 +21,18 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.MutablePropertyValues; -import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.type.AnnotationMetadata; import org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** @@ -49,50 +50,62 @@ public class PublisherRegistrar implements ImportBeanDefinitionRegistrar { Map annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnablePublisher.class.getName()); - String defaultChannel = - annotationAttributes == null - ? (String) AnnotationUtils.getDefaultValue(EnablePublisher.class) - : (String) annotationAttributes.get("defaultChannel"); if (!registry.containsBeanDefinition(IntegrationContextUtils.PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME)) { + ConfigurableBeanFactory beanFactory; + if (registry instanceof ConfigurableBeanFactory) { + beanFactory = (ConfigurableBeanFactory) registry; + } + else if (registry instanceof ConfigurableApplicationContext) { + beanFactory = ((ConfigurableApplicationContext) registry).getBeanFactory(); + } + else { + beanFactory = null; + } BeanDefinitionBuilder builder = - BeanDefinitionBuilder.genericBeanDefinition(PublisherAnnotationBeanPostProcessor.class) + BeanDefinitionBuilder.genericBeanDefinition(PublisherAnnotationBeanPostProcessor.class, + () -> createPublisherAnnotationBeanPostProcessor(annotationAttributes, beanFactory)) .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - if (StringUtils.hasText(defaultChannel)) { - builder.addPropertyValue("defaultChannelName", defaultChannel); - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Setting '@Publisher' default-output-channel to '" + defaultChannel + "'."); - } - } - - if (annotationAttributes != null) { - Object proxyTargetClass = annotationAttributes.get("proxyTargetClass"); - builder.addPropertyValue("proxyTargetClass", proxyTargetClass); - Object order = annotationAttributes.get("order"); - builder.addPropertyValue("order", order); - } registry.registerBeanDefinition(IntegrationContextUtils.PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME, builder.getBeanDefinition()); } else { - BeanDefinition beanDefinition = - registry.getBeanDefinition(IntegrationContextUtils.PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME); - MutablePropertyValues propertyValues = beanDefinition.getPropertyValues(); - PropertyValue defaultChannelPropertyValue = propertyValues.getPropertyValue("defaultChannelName"); - if (StringUtils.hasText(defaultChannel)) { - if (defaultChannelPropertyValue == null) { - propertyValues.addPropertyValue("defaultChannelName", defaultChannel); - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Setting '@Publisher' default-output-channel to '" + defaultChannel + "'."); - } - } - else if (!defaultChannel.equals(defaultChannelPropertyValue.getValue())) { - throw new BeanDefinitionStoreException("When more than one enable publisher definition " + - "(@EnablePublisher or ) is found in the context, " + - "they all must have the same 'default-publisher-channel' attribute value."); - } - } + throw new BeanDefinitionStoreException("Only one enable publisher definition " + + "(@EnablePublisher or ) can be declared in the application context."); } } + private PublisherAnnotationBeanPostProcessor createPublisherAnnotationBeanPostProcessor( + @Nullable Map annotationAttributes, @Nullable ConfigurableBeanFactory beanFactory) { + + PublisherAnnotationBeanPostProcessor postProcessor = new PublisherAnnotationBeanPostProcessor(); + String defaultChannel = + annotationAttributes == null + ? (String) AnnotationUtils.getDefaultValue(EnablePublisher.class) + : (String) annotationAttributes.get("defaultChannel"); + if (StringUtils.hasText(defaultChannel)) { + if (beanFactory != null) { + defaultChannel = beanFactory.resolveEmbeddedValue(defaultChannel); + } + postProcessor.setDefaultChannelName(defaultChannel); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Setting '@Publisher' default-output-channel to '" + defaultChannel + "'."); + } + } + if (annotationAttributes != null) { + String proxyTargetClass = annotationAttributes.get("proxyTargetClass").toString(); + if (beanFactory != null) { + proxyTargetClass = beanFactory.resolveEmbeddedValue(proxyTargetClass); + } + postProcessor.setProxyTargetClass(Boolean.parseBoolean(proxyTargetClass)); + + String order = annotationAttributes.get("order").toString(); + if (beanFactory != null) { + order = beanFactory.resolveEmbeddedValue(order); + } + postProcessor.setOrder(Integer.parseInt(order)); + } + return postProcessor; + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java index d34d1ffa40..5dd001222a 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 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. @@ -21,9 +21,17 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.w3c.dom.Element; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.ManagedSet; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.ChannelInitializer; import org.springframework.integration.config.IntegrationRegistrar; +import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.context.IntegrationProperties; /** * Base class for NamespaceHandlers that registers a BeanFactoryPostProcessor @@ -41,10 +49,42 @@ public abstract class AbstractIntegrationNamespaceHandler extends NamespaceHandl @Override public final BeanDefinition parse(Element element, ParserContext parserContext) { if (!this.initialized.getAndSet(true)) { - IntegrationRegistrar integrationRegistrar = new IntegrationRegistrar(); - integrationRegistrar.registerBeanDefinitions(null, parserContext.getRegistry()); + BeanDefinitionRegistry registry = parserContext.getRegistry(); + new IntegrationRegistrar().registerBeanDefinitions(null, registry); + registerImplicitChannelCreator(registry); } return super.parse(element, parserContext); } + /** + * This method will auto-register a ChannelInitializer which could also be overridden by the user + * by simply registering a ChannelInitializer {@code } with its {@code autoCreate} property + * set to false to suppress channel creation. + * It will also register a ChannelInitializer$AutoCreateCandidatesCollector + * which simply collects candidate channel names. + * @param registry The {@link BeanDefinitionRegistry} to register additional {@link BeanDefinition}s. + */ + private static void registerImplicitChannelCreator(BeanDefinitionRegistry registry) { + if (!registry.containsBeanDefinition(IntegrationContextUtils.CHANNEL_INITIALIZER_BEAN_NAME)) { + String channelsAutoCreateExpression = + IntegrationProperties.getExpressionFor(IntegrationProperties.CHANNELS_AUTOCREATE); + BeanDefinitionBuilder channelDef = BeanDefinitionBuilder.genericBeanDefinition(ChannelInitializer.class) + .addPropertyValue("autoCreate", channelsAutoCreateExpression); + BeanDefinitionHolder channelCreatorHolder = new BeanDefinitionHolder(channelDef.getBeanDefinition(), + IntegrationContextUtils.CHANNEL_INITIALIZER_BEAN_NAME); + BeanDefinitionReaderUtils.registerBeanDefinition(channelCreatorHolder, registry); + } + + if (!registry.containsBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) { + BeanDefinitionBuilder channelRegistryBuilder = BeanDefinitionBuilder + .genericBeanDefinition(ChannelInitializer.AutoCreateCandidatesCollector.class); + channelRegistryBuilder.addConstructorArgValue(new ManagedSet()); + channelRegistryBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + BeanDefinitionHolder channelRegistryHolder = + new BeanDefinitionHolder(channelRegistryBuilder.getBeanDefinition(), + IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); + BeanDefinitionReaderUtils.registerBeanDefinition(channelRegistryHolder, registry); + } + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java index d29c3c892c..e2bf47695a 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2021 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. @@ -22,16 +22,20 @@ import java.util.Map; import org.w3c.dom.Element; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.EnablePublisher; import org.springframework.integration.config.IntegrationRegistrar; +import org.springframework.integration.config.PublisherRegistrar; import org.springframework.integration.config.annotation.AnnotationMetadataAdapter; import org.springframework.util.xml.DomUtils; /** - * Parser for the <annotation-config> element of the integration namespace. - * Just delegate the real configuration to the {@link IntegrationRegistrar}. + * Parser for the {@code } element of the integration namespace. + * Delegates the real configuration to the {@link IntegrationRegistrar}. + * If {@code } sub-element is present, the {@link PublisherRegistrar} + * is called, too. * * @author Mark Fisher * @author Artem Bilan @@ -40,9 +44,15 @@ import org.springframework.util.xml.DomUtils; public class AnnotationConfigParser implements BeanDefinitionParser { @Override - public BeanDefinition parse(final Element element, ParserContext parserContext) { - new IntegrationRegistrar().registerBeanDefinitions(new ExtendedAnnotationMetadata(element), - parserContext.getRegistry()); + public BeanDefinition parse(Element element, ParserContext parserContext) { + ExtendedAnnotationMetadata importingClassMetadata = new ExtendedAnnotationMetadata(element); + BeanDefinitionRegistry registry = parserContext.getRegistry(); + new IntegrationRegistrar() + .registerBeanDefinitions(importingClassMetadata, registry); + if (DomUtils.getChildElementByTagName(element, "enable-publisher") != null) { + new PublisherRegistrar() + .registerBeanDefinitions(importingClassMetadata, registry); + } return null; } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dsl/BaseIntegrationFlowDefinition.java b/spring-integration-core/src/main/java/org/springframework/integration/dsl/BaseIntegrationFlowDefinition.java index 74390afbc8..1730badb93 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dsl/BaseIntegrationFlowDefinition.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dsl/BaseIntegrationFlowDefinition.java @@ -3106,9 +3106,6 @@ public abstract class BaseIntegrationFlowDefinition, protected final List adviceChain = new LinkedList<>(); // NOSONAR final protected ConsumerEndpointSpec(H messageHandler) { - super(messageHandler); + super(messageHandler, new ConsumerEndpointFactoryBean()); } @Override diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dsl/EndpointSpec.java b/spring-integration-core/src/main/java/org/springframework/integration/dsl/EndpointSpec.java index 176c53f5b0..0b4a53aa5b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dsl/EndpointSpec.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dsl/EndpointSpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2021 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. @@ -20,10 +20,10 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Function; -import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanNameAware; -import org.springframework.core.ResolvableType; +import org.springframework.beans.factory.FactoryBean; import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.scheduling.PollerMetadata; import org.springframework.messaging.MessageChannel; import org.springframework.util.Assert; @@ -44,20 +44,18 @@ import reactor.util.function.Tuples; * * @since 5.0 */ -public abstract class EndpointSpec, F extends BeanNameAware, H> +public abstract class EndpointSpec, F extends BeanNameAware & FactoryBean, H> extends IntegrationComponentSpec> implements ComponentsRegistration { protected final Map componentsToRegister = new LinkedHashMap<>(); // NOSONAR final - protected H handler; // NOSONAR final + protected final F endpointFactoryBean; // NOSONAR final - protected F endpointFactoryBean; // NOSONAR final + protected H handler; // NOSONAR - @SuppressWarnings("unchecked") - protected EndpointSpec(H handler) { - Class fClass = ResolvableType.forClass(this.getClass()).as(EndpointSpec.class).resolveGenerics()[1]; - this.endpointFactoryBean = (F) BeanUtils.instantiateClass(fClass); + protected EndpointSpec(H handler, F endpointFactoryBean) { + this.endpointFactoryBean = endpointFactoryBean; this.handler = handler; } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlows.java b/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlows.java index 9f328d71ff..1d87d6a4d2 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlows.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dsl/IntegrationFlows.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2021 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. @@ -26,8 +26,8 @@ import org.springframework.integration.channel.FluxMessageChannel; import org.springframework.integration.core.MessageSource; import org.springframework.integration.dsl.support.FixedSubscriberChannelPrototype; import org.springframework.integration.dsl.support.MessageChannelReference; +import org.springframework.integration.endpoint.AbstractMessageSource; import org.springframework.integration.endpoint.MessageProducerSupport; -import org.springframework.integration.endpoint.MethodInvokingMessageSource; import org.springframework.integration.gateway.MessagingGatewaySupport; import org.springframework.lang.Nullable; import org.springframework.messaging.Message; @@ -160,10 +160,19 @@ public final class IntegrationFlows { Consumer endpointConfigurer) { Assert.notNull(messageSource, "'messageSource' must not be null"); - MethodInvokingMessageSource methodInvokingMessageSource = new MethodInvokingMessageSource(); - methodInvokingMessageSource.setObject(messageSource); - methodInvokingMessageSource.setMethodName("get"); - return from(methodInvokingMessageSource, endpointConfigurer); + return from(new AbstractMessageSource() { + + @Override + protected Object doReceive() { + return messageSource.get(); + } + + @Override + public String getComponentType() { + return "inbound-channel-adapter"; + } + + }, endpointConfigurer); } /** diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dsl/SourcePollingChannelAdapterSpec.java b/spring-integration-core/src/main/java/org/springframework/integration/dsl/SourcePollingChannelAdapterSpec.java index 4cee04531f..47cb2b4ac8 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dsl/SourcePollingChannelAdapterSpec.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dsl/SourcePollingChannelAdapterSpec.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 the original author or authors. + * Copyright 2016-2021 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. @@ -29,7 +29,7 @@ public class SourcePollingChannelAdapterSpec extends EndpointSpec> { protected SourcePollingChannelAdapterSpec(MessageSource messageSource) { - super(messageSource); + super(messageSource, new SourcePollingChannelAdapterFactoryBean()); this.endpointFactoryBean.setSource(messageSource); } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/dsl/context/DslIntegrationConfigurationInitializer.java b/spring-integration-core/src/main/java/org/springframework/integration/dsl/context/DslIntegrationConfigurationInitializer.java index 91a8af5eb0..9ecd8b1366 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/dsl/context/DslIntegrationConfigurationInitializer.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/dsl/context/DslIntegrationConfigurationInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2021 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. @@ -60,11 +60,13 @@ public class DslIntegrationConfigurationInitializer implements IntegrationConfig BeanDefinitionRegistry registry = (BeanDefinitionRegistry) configurableListableBeanFactory; if (!registry.containsBeanDefinition(INTEGRATION_FLOW_BPP_BEAN_NAME)) { registry.registerBeanDefinition(INTEGRATION_FLOW_BPP_BEAN_NAME, - new RootBeanDefinition(IntegrationFlowBeanPostProcessor.class)); + new RootBeanDefinition(IntegrationFlowBeanPostProcessor.class, + IntegrationFlowBeanPostProcessor::new)); registry.registerBeanDefinition(INTEGRATION_FLOW_CONTEXT_BEAN_NAME, - new RootBeanDefinition(StandardIntegrationFlowContext.class)); + new RootBeanDefinition(StandardIntegrationFlowContext.class, StandardIntegrationFlowContext::new)); registry.registerBeanDefinition(INTEGRATION_FLOW_REPLY_PRODUCER_CLEANER_BEAN_NAME, - new RootBeanDefinition(IntegrationFlowDefinition.ReplyProducerCleaner.class)); + new RootBeanDefinition(IntegrationFlowDefinition.ReplyProducerCleaner.class, + IntegrationFlowDefinition.ReplyProducerCleaner::new)); } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/gateway/DefaultMethodInvokingMethodInterceptor.java b/spring-integration-core/src/main/java/org/springframework/integration/gateway/DefaultMethodInvokingMethodInterceptor.java index 874ede2889..77c4f87d0c 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/gateway/DefaultMethodInvokingMethodInterceptor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/gateway/DefaultMethodInvokingMethodInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2019 the original author or authors. + * Copyright 2015-2021 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. @@ -34,7 +34,7 @@ import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType; import org.springframework.util.ReflectionUtils; /** - * Method interceptor to invoke default methods on the repository proxy. + * Method interceptor to invoke default methods on the gateway proxy. * * The copy of {@code DefaultMethodInvokingMethodInterceptor} from Spring Data Commons. * diff --git a/spring-integration-core/src/main/java/org/springframework/integration/json/JsonNodeWrapperToJsonNodeConverter.java b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonNodeWrapperToJsonNodeConverter.java index 07ec545692..9682a12e67 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/json/JsonNodeWrapperToJsonNodeConverter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/json/JsonNodeWrapperToJsonNodeConverter.java @@ -37,7 +37,7 @@ import com.fasterxml.jackson.databind.JsonNode; * * @since 5.5 */ -class JsonNodeWrapperToJsonNodeConverter implements GenericConverter { +public class JsonNodeWrapperToJsonNodeConverter implements GenericConverter { @Override public Set getConvertibleTypes() { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java index 0519008f2b..9742175b56 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/json/Jackson2JsonObjectMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 the original author or authors. + * Copyright 2013-2021 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. @@ -26,7 +26,6 @@ import java.net.URL; import java.util.Collection; import java.util.Map; -import org.springframework.beans.BeanUtils; import org.springframework.integration.mapping.support.JsonHeaders; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -38,14 +37,13 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; /** * Jackson 2 JSON-processor (@link https://github.com/FasterXML) * {@linkplain JsonObjectMapper} implementation. - * Delegates toJson and fromJson + * Delegates {@link #toJson} and {@link #fromJson} * to the {@linkplain com.fasterxml.jackson.databind.ObjectMapper} *

* It customizes Jackson's default properties with the following ones: @@ -66,7 +64,20 @@ import com.fasterxml.jackson.databind.json.JsonMapper; */ public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper { - private static final String UNUSED = "unused"; + private static final ClassLoader CLASS_LOADER = ClassUtils.getDefaultClassLoader(); + + private static final boolean JDK8_MODULE_PRESENT = + ClassUtils.isPresent("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", CLASS_LOADER); + + private static final boolean JAVA_TIME_MODULE_PRESENT = + ClassUtils.isPresent("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", CLASS_LOADER); + + private static final boolean JODA_MODULE_PRESENT = + ClassUtils.isPresent("com.fasterxml.jackson.datatype.joda.JodaModule", CLASS_LOADER); + + private static final boolean KOTLIN_MODULE_PRESENT = + ClassUtils.isPresent("kotlin.Unit", CLASS_LOADER) && + ClassUtils.isPresent("com.fasterxml.jackson.module.kotlin.KotlinModule", CLASS_LOADER); private final ObjectMapper objectMapper; @@ -185,58 +196,48 @@ public class Jackson2JsonObjectMapper extends AbstractJacksonJsonObjectMapper jdk7Module = (Class) - ClassUtils.forName("com.fasterxml.jackson.datatype.jdk7.Jdk7Module", getClassLoader()); - this.objectMapper.registerModule(BeanUtils.instantiateClass(jdk7Module)); - } - catch (@SuppressWarnings(UNUSED) ClassNotFoundException ex) { - // jackson-datatype-jdk7 not available + if (JDK8_MODULE_PRESENT) { + this.objectMapper.registerModule(Jdk8ModuleProvider.module); } - try { - Class jdk8Module = (Class) - ClassUtils.forName("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", getClassLoader()); - this.objectMapper.registerModule(BeanUtils.instantiateClass(jdk8Module)); - } - catch (@SuppressWarnings(UNUSED) ClassNotFoundException ex) { - // jackson-datatype-jdk8 not available + if (JAVA_TIME_MODULE_PRESENT) { + this.objectMapper.registerModule(JavaTimeModuleProvider.module); } - try { - Class javaTimeModule = (Class) - ClassUtils.forName("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", getClassLoader()); - this.objectMapper.registerModule(BeanUtils.instantiateClass(javaTimeModule)); - } - catch (@SuppressWarnings(UNUSED) ClassNotFoundException ex) { - // jackson-datatype-jsr310 not available + if (JODA_MODULE_PRESENT) { + this.objectMapper.registerModule(JodaModuleProvider.module); } - // Joda-Time present? - if (ClassUtils.isPresent("org.joda.time.LocalDate", getClassLoader())) { - try { - Class jodaModule = (Class) - ClassUtils.forName("com.fasterxml.jackson.datatype.joda.JodaModule", getClassLoader()); - this.objectMapper.registerModule(BeanUtils.instantiateClass(jodaModule)); - } - catch (@SuppressWarnings(UNUSED) ClassNotFoundException ex) { - // jackson-datatype-joda not available - } - } - - // Kotlin present? - if (ClassUtils.isPresent("kotlin.Unit", getClassLoader())) { - try { - Class kotlinModule = (Class) - ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", getClassLoader()); - this.objectMapper.registerModule(BeanUtils.instantiateClass(kotlinModule)); - } - catch (@SuppressWarnings(UNUSED) ClassNotFoundException ex) { - //jackson-module-kotlin not available - } + if (KOTLIN_MODULE_PRESENT) { + this.objectMapper.registerModule(KotlinModuleProvider.module); } } + private static final class Jdk8ModuleProvider { + + static final com.fasterxml.jackson.databind.Module module = new com.fasterxml.jackson.datatype.jdk8.Jdk8Module(); + + } + + private static final class JavaTimeModuleProvider { + + static final com.fasterxml.jackson.databind.Module module = + new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule(); + + } + + private static final class JodaModuleProvider { + + static final com.fasterxml.jackson.databind.Module module = new com.fasterxml.jackson.datatype.joda.JodaModule(); + + } + + private static final class KotlinModuleProvider { + + static final com.fasterxml.jackson.databind.Module module = + new com.fasterxml.jackson.module.kotlin.KotlinModule(); + + } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml index 8cf2fc9b8a..86ca52ef77 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml @@ -14,10 +14,6 @@ - - - - diff --git a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java index 994cd27e1b..e03076a1b8 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java @@ -554,11 +554,11 @@ public class EnableIntegrationTests { @Test public void testIntegrationConverter() { - this.numberChannel.send(new GenericMessage(10)); - this.numberChannel.send(new GenericMessage(true)); + this.numberChannel.send(new GenericMessage<>(10)); + this.numberChannel.send(new GenericMessage<>(true)); assertThat(this.testConverter.getInvoked()).isGreaterThan(0); - assertThat(this.bytesChannel.send(new GenericMessage("foo".getBytes()))).isTrue(); + assertThat(this.bytesChannel.send(new GenericMessage<>("foo".getBytes()))).isTrue(); assertThat(this.bytesChannel.send(new GenericMessage<>(MutableMessageBuilder.withPayload("").build()))) .isTrue(); @@ -1046,7 +1046,7 @@ public class EnableIntegrationTests { @EnableIntegration @ImportResource("classpath:org/springframework/integration/configuration/EnableIntegrationTests-context.xml") @EnableMessageHistory("${message.history.tracked.components}") - @EnablePublisher(defaultChannel = "publishedChannel") + @EnablePublisher(defaultChannel = "publishedChannel", proxyTargetClass = true, order = 2147483646) @EnableAsync public static class ContextConfiguration2 { @@ -1069,7 +1069,7 @@ public class EnableIntegrationTests { @Bean public AtomicReference asyncAnnotationProcessThread() { - return new AtomicReference(); + return new AtomicReference<>(); } @Bean diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/BaseHttpMessageHandlerSpec.java b/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/BaseHttpMessageHandlerSpec.java index a04c63c9ee..95962cdfa6 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/BaseHttpMessageHandlerSpec.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/dsl/BaseHttpMessageHandlerSpec.java @@ -25,7 +25,6 @@ import org.springframework.core.ParameterizedTypeReference; import org.springframework.expression.Expression; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; import org.springframework.integration.dsl.ComponentsRegistration; import org.springframework.integration.dsl.MessageHandlerSpec; import org.springframework.integration.expression.FunctionExpression; @@ -312,9 +311,10 @@ public abstract class BaseHttpMessageHandlerSpec