Deprecate Properties as bean for int global props

Having a bean as a `java.util.Properties` is not confusing
and may lead to some conflicts in the real application.
Plus it is not so easy to configure: need to know all
the possible integration properties - bad end-user experience

* Make an `IntegrationProperties` as a public POJO
for easy configuration of global properties
* Deprecate a presence of the `java.util.Properties`
* Leave framework-created `integrationGlobalProperties` as
a `Properties` instance for backward compatibility
* Fix tests to expose an `IntegrationProperties` bean instead
of deprecated `Properties`
* Fix docs according the change and current recommendations
This commit is contained in:
Artem Bilan
2021-02-17 13:36:16 -05:00
committed by Gary Russell
parent aeb43f3069
commit 281d8d5bf6
10 changed files with 314 additions and 85 deletions

View File

@@ -19,10 +19,7 @@ package org.springframework.integration.config;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
@@ -328,17 +325,15 @@ class DefaultConfiguringBeanFactoryPostProcessor
if (!this.beanFactory.containsBean(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME)) {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(this.classLoader);
try {
Resource[] defaultResources =
resourceResolver.getResources("classpath*:META-INF/spring.integration.default.properties");
Resource[] userResources =
Resource[] resources =
resourceResolver.getResources("classpath*:META-INF/spring.integration.properties");
List<Resource> resources = new LinkedList<>(Arrays.asList(defaultResources));
resources.addAll(Arrays.asList(userResources));
// TODO Revise in favor of 'IntegrationProperties' instance in the next 6.0 version
BeanDefinitionBuilder integrationPropertiesBuilder = BeanDefinitionBuilder
.genericBeanDefinition(PropertiesFactoryBean.class)
.addPropertyValue("locations", resources);
.addPropertyValue("properties", IntegrationProperties.defaults())
.addPropertyValue("locations", resources)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
this.registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME,
integrationPropertiesBuilder.getBeanDefinition());

View File

@@ -18,7 +18,12 @@ package org.springframework.integration.context;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -110,6 +115,8 @@ public abstract class IntegrationContextUtils {
public static final String LIST_MESSAGE_HANDLER_FACTORY_BEAN_NAME = "integrationListMessageHandlerMethodFactory";
private static final Log LOGGER = LogFactory.getLog(IntegrationContextUtils.class);
/**
* @param beanFactory BeanFactory for lookup, must not be null.
* @return The {@link MetadataStore} bean whose name is "metadataStore".
@@ -173,15 +180,15 @@ public abstract class IntegrationContextUtils {
return beanFactory.getBean(beanName, type);
}
// TODO Revise in favor of 'IntegrationProperties' instance in the next 6.0 version
/**
* @param beanFactory The bean factory.
* @return the global {@link IntegrationContextUtils#INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME}
* bean from provided {@code #beanFactory}, which represents the merged
* properties values from all 'META-INF/spring.integration.default.properties'
* and 'META-INF/spring.integration.properties'.
* Or user-defined {@link Properties} bean.
* properties values from all 'META-INF/spring.integration.properties'.
* Or user-defined {@link IntegrationProperties} bean.
* May return only {@link IntegrationProperties#defaults()} if there is no
* {@link IntegrationContextUtils#INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME} bean within
* {@link IntegrationContextUtils#INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME} bean in the
* provided {@code #beanFactory} or provided {@code #beanFactory} is null.
*/
public static Properties getIntegrationProperties(BeanFactory beanFactory) {
@@ -191,8 +198,7 @@ public abstract class IntegrationContextUtils {
if (properties == null) {
Properties propertiesToRegister = new Properties();
propertiesToRegister.putAll(IntegrationProperties.defaults());
Properties userProperties =
getBeanOfType(beanFactory, INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, Properties.class);
Properties userProperties = obtainUserProperties(beanFactory);
if (userProperties != null) {
propertiesToRegister.putAll(userProperties);
}
@@ -215,6 +221,32 @@ public abstract class IntegrationContextUtils {
return properties;
}
@Nullable
private static Properties obtainUserProperties(BeanFactory beanFactory) {
Object userProperties = getBeanOfType(beanFactory, INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, Object.class);
if (userProperties instanceof Properties) {
if (BeanDefinition.ROLE_INFRASTRUCTURE !=
((BeanDefinitionRegistry) beanFactory)
.getBeanDefinition(INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME)
.getRole()) {
LOGGER.warn("The 'integrationGlobalProperties' bean must be declared as an instance of " +
"'org.springframework.integration.context.IntegrationProperties'. " +
"A 'java.util.Properties' support as a bean is deprecated for " +
"'integrationGlobalProperties' since version 5.5.");
}
return (Properties) userProperties;
}
else if (userProperties instanceof IntegrationProperties) {
return ((IntegrationProperties) userProperties).toProperties();
}
else if (userProperties != null) {
throw new BeanNotOfRequiredTypeException(INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME,
IntegrationProperties.class, userProperties.getClass());
}
return null;
}
/**
* Return a {@link BeanDefinition} with the given name,
* obtained from the given {@link BeanFactory} or one of its parents.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -294,12 +294,14 @@ public abstract class IntegrationObjectSupport implements BeanNameAware, NamedCo
/**
* @see IntegrationContextUtils#getIntegrationProperties(BeanFactory)
* @return The global integration properties.
* @deprecated since version 5.5 in favor of {@link #getIntegrationProperty(String, Class)};
* will be replaced with {@link IntegrationProperties} variant in the next major version.
*/
@Deprecated
protected Properties getIntegrationProperties() {
return this.integrationProperties;
}
protected MessageBuilderFactory getMessageBuilderFactory() {
if (this.messageBuilderFactory == null) {
this.messageBuilderFactory = new DefaultMessageBuilderFactory();

View File

@@ -16,17 +16,27 @@
package org.springframework.integration.context;
import java.io.IOException;
import java.util.Properties;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.integration.util.JavaUtils;
import org.springframework.util.StringUtils;
/**
* Utility class to encapsulate infrastructure Integration properties constants and
* their default values from resources 'META-INF/spring.integration.default.properties'.
* Utility class to encapsulate infrastructure Integration properties constants and their default values.
* The default values can be overridden by the {@code META-INF/spring.integration.properties} with this entries
* (includes their default values):
* <p>
* <ul>
* <li> {@code spring.integration.channels.autoCreate=true}
* <li> {@code spring.integration.channels.maxUnicastSubscribers=0x7fffffff}
* <li> {@code spring.integration.channels.maxBroadcastSubscribers=0x7fffffff}
* <li> {@code spring.integration.taskScheduler.poolSize=10}
* <li> {@code spring.integration.messagingTemplate.throwExceptionOnLateReply=false}
* <li> {@code spring.integration.readOnly.headers=}
* <li> {@code spring.integration.endpoints.noAutoStartup=}
* <li> {@code spring.integration.channels.error.requireSubscribers=true}
* <li> {@code spring.integration.channels.error.ignoreFailures=true}
* </ul>
*
* @author Artem Bilan
*
@@ -98,26 +108,228 @@ public final class IntegrationProperties {
private static final Properties DEFAULTS;
static {
String resourcePattern = "classpath*:META-INF/spring.integration.default.properties";
try {
ResourcePatternResolver resourceResolver =
new PathMatchingResourcePatternResolver(IntegrationProperties.class.getClassLoader());
Resource[] defaultResources = resourceResolver.getResources(resourcePattern);
private boolean channelsAutoCreate = true;
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocations(defaultResources);
propertiesFactoryBean.afterPropertiesSet();
DEFAULTS = propertiesFactoryBean.getObject();
}
catch (IOException e) {
throw new IllegalStateException("Can't load '" + resourcePattern + "' resources.", e);
}
private int channelsMaxUnicastSubscribers = Integer.MAX_VALUE;
private int channelsMaxBroadcastSubscribers = Integer.MAX_VALUE;
private boolean errorChannelRequireSubscribers = true;
private boolean errorChannelIgnoreFailures = true;
private int taskSchedulerPoolSize = 10;
private boolean messagingTemplateThrowExceptionOnLateReply = false;
private String[] readOnlyHeaders = { };
private String[] noAutoStartupEndpoints = { };
static {
DEFAULTS = new IntegrationProperties().toProperties();
}
/**
* @return {@link Properties} with default values for Integration properties
* from resources 'META-INF/spring.integration.default.properties'.
* Configure a value for {@link #CHANNELS_AUTOCREATE} option.
* @param channelsAutoCreate the value for {@link #CHANNELS_AUTOCREATE} option.
*/
public void setChannelsAutoCreate(boolean channelsAutoCreate) {
this.channelsAutoCreate = channelsAutoCreate;
}
/**
* Return the value of {@link #CHANNELS_AUTOCREATE} option.
* @return the value of {@link #CHANNELS_AUTOCREATE} option.
*/
public boolean isChannelsAutoCreate() {
return this.channelsAutoCreate;
}
/**
* Configure a value for {@link #CHANNELS_MAX_UNICAST_SUBSCRIBERS} option.
* @param channelsMaxUnicastSubscribers the value for {@link #CHANNELS_MAX_UNICAST_SUBSCRIBERS} option.
*/
public void setChannelsMaxUnicastSubscribers(int channelsMaxUnicastSubscribers) {
this.channelsMaxUnicastSubscribers = channelsMaxUnicastSubscribers;
}
/**
* Return the value of {@link #CHANNELS_MAX_UNICAST_SUBSCRIBERS} option.
* @return the value of {@link #CHANNELS_MAX_UNICAST_SUBSCRIBERS} option.
*/
public int getChannelsMaxUnicastSubscribers() {
return this.channelsMaxUnicastSubscribers;
}
/**
* Configure a value for {@link #CHANNELS_MAX_BROADCAST_SUBSCRIBERS} option.
* @param channelsMaxBroadcastSubscribers the value for {@link #CHANNELS_MAX_BROADCAST_SUBSCRIBERS} option.
*/
public void setChannelsMaxBroadcastSubscribers(int channelsMaxBroadcastSubscribers) {
this.channelsMaxBroadcastSubscribers = channelsMaxBroadcastSubscribers;
}
/**
* Return the value of {@link #CHANNELS_MAX_BROADCAST_SUBSCRIBERS} option.
* @return the value of {@link #CHANNELS_MAX_BROADCAST_SUBSCRIBERS} option.
*/
public int getChannelsMaxBroadcastSubscribers() {
return this.channelsMaxBroadcastSubscribers;
}
/**
* Configure a value for {@link #ERROR_CHANNEL_REQUIRE_SUBSCRIBERS} option.
* @param errorChannelRequireSubscribers the value for {@link #ERROR_CHANNEL_REQUIRE_SUBSCRIBERS} option.
*/
public void setErrorChannelRequireSubscribers(boolean errorChannelRequireSubscribers) {
this.errorChannelRequireSubscribers = errorChannelRequireSubscribers;
}
/**
* Return the value of {@link #ERROR_CHANNEL_REQUIRE_SUBSCRIBERS} option.
* @return the value of {@link #ERROR_CHANNEL_REQUIRE_SUBSCRIBERS} option.
*/
public boolean isErrorChannelRequireSubscribers() {
return this.errorChannelRequireSubscribers;
}
/**
* Configure a value for {@link #ERROR_CHANNEL_IGNORE_FAILURES} option.
* @param errorChannelIgnoreFailures the value for {@link #ERROR_CHANNEL_IGNORE_FAILURES} option.
*/
public void setErrorChannelIgnoreFailures(boolean errorChannelIgnoreFailures) {
this.errorChannelIgnoreFailures = errorChannelIgnoreFailures;
}
/**
* Return the value of {@link #ERROR_CHANNEL_IGNORE_FAILURES} option.
* @return the value of {@link #ERROR_CHANNEL_IGNORE_FAILURES} option.
*/
public boolean isErrorChannelIgnoreFailures() {
return this.errorChannelIgnoreFailures;
}
/**
* Configure a value for {@link #TASK_SCHEDULER_POOL_SIZE} option.
* @param taskSchedulerPoolSize the value for {@link #TASK_SCHEDULER_POOL_SIZE} option.
*/
public void setTaskSchedulerPoolSize(int taskSchedulerPoolSize) {
this.taskSchedulerPoolSize = taskSchedulerPoolSize;
}
/**
* Return the value of {@link #TASK_SCHEDULER_POOL_SIZE} option.
* @return the value of {@link #TASK_SCHEDULER_POOL_SIZE} option.
*/
public int getTaskSchedulerPoolSize() {
return this.taskSchedulerPoolSize;
}
/**
* Configure a value for {@link #THROW_EXCEPTION_ON_LATE_REPLY} option.
* @param messagingTemplateThrowExceptionOnLateReply the value for {@link #THROW_EXCEPTION_ON_LATE_REPLY} option.
*/
public void setMessagingTemplateThrowExceptionOnLateReply(boolean messagingTemplateThrowExceptionOnLateReply) {
this.messagingTemplateThrowExceptionOnLateReply = messagingTemplateThrowExceptionOnLateReply;
}
/**
* Return the value of {@link #THROW_EXCEPTION_ON_LATE_REPLY} option.
* @return the value of {@link #THROW_EXCEPTION_ON_LATE_REPLY} option.
*/
public boolean isMessagingTemplateThrowExceptionOnLateReply() {
return this.messagingTemplateThrowExceptionOnLateReply;
}
/**
* Configure a value for {@link #READ_ONLY_HEADERS} option.
* @param readOnlyHeaders the value for {@link #READ_ONLY_HEADERS} option.
*/
public void setReadOnlyHeaders(String... readOnlyHeaders) {
this.readOnlyHeaders = readOnlyHeaders;
}
/**
* Return the value of {@link #READ_ONLY_HEADERS} option.
* @return the value of {@link #READ_ONLY_HEADERS} option.
*/
public String[] getReadOnlyHeaders() {
return this.readOnlyHeaders;
}
/**
* Configure a value for {@link #ENDPOINTS_NO_AUTO_STARTUP} option.
* @param noAutoStartupEndpoints the value for {@link #ENDPOINTS_NO_AUTO_STARTUP} option.
*/
public void setNoAutoStartupEndpoints(String... noAutoStartupEndpoints) {
this.noAutoStartupEndpoints = noAutoStartupEndpoints;
}
/**
* Return the value of {@link #ENDPOINTS_NO_AUTO_STARTUP} option.
* @return the value of {@link #ENDPOINTS_NO_AUTO_STARTUP} option.
*/
public String[] getNoAutoStartupEndpoints() {
return this.noAutoStartupEndpoints;
}
/**
* Represent the current instance as a {@link Properties}.
* @return the {@link Properties} representation.
* @since 5.5
*/
public Properties toProperties() {
Properties properties = new Properties();
properties.setProperty(CHANNELS_AUTOCREATE, "" + this.channelsAutoCreate);
properties.setProperty(CHANNELS_MAX_UNICAST_SUBSCRIBERS, "" + this.channelsMaxUnicastSubscribers);
properties.setProperty(CHANNELS_MAX_BROADCAST_SUBSCRIBERS, "" + this.channelsMaxBroadcastSubscribers);
properties.setProperty(ERROR_CHANNEL_REQUIRE_SUBSCRIBERS, "" + this.errorChannelRequireSubscribers);
properties.setProperty(ERROR_CHANNEL_IGNORE_FAILURES, "" + this.errorChannelIgnoreFailures);
properties.setProperty(TASK_SCHEDULER_POOL_SIZE, "" + this.taskSchedulerPoolSize);
properties.setProperty(THROW_EXCEPTION_ON_LATE_REPLY, "" + this.messagingTemplateThrowExceptionOnLateReply);
properties.setProperty(READ_ONLY_HEADERS, StringUtils.arrayToCommaDelimitedString(this.readOnlyHeaders));
properties.setProperty(ENDPOINTS_NO_AUTO_STARTUP,
StringUtils.arrayToCommaDelimitedString(this.noAutoStartupEndpoints));
return properties;
}
/**
* Parse a provided {@link Properties} and build an {@link IntegrationProperties} instance.
* @return {@link IntegrationProperties} based on the provided {@link Properties}.
* @since 5.5
*/
public static IntegrationProperties parse(Properties properties) {
IntegrationProperties integrationProperties = new IntegrationProperties();
JavaUtils.INSTANCE
.acceptIfHasText(properties.getProperty(CHANNELS_AUTOCREATE),
(value) -> integrationProperties.setChannelsAutoCreate(Boolean.parseBoolean(value)))
.acceptIfHasText(properties.getProperty(CHANNELS_MAX_UNICAST_SUBSCRIBERS),
(value) -> integrationProperties.setChannelsMaxUnicastSubscribers(Integer.parseInt(value)))
.acceptIfHasText(properties.getProperty(CHANNELS_MAX_BROADCAST_SUBSCRIBERS),
(value) -> integrationProperties.setChannelsMaxBroadcastSubscribers(Integer.parseInt(value)))
.acceptIfHasText(properties.getProperty(ERROR_CHANNEL_REQUIRE_SUBSCRIBERS),
(value) -> integrationProperties.setErrorChannelRequireSubscribers(Boolean.parseBoolean(value)))
.acceptIfHasText(properties.getProperty(ERROR_CHANNEL_IGNORE_FAILURES),
(value) -> integrationProperties.setErrorChannelIgnoreFailures(Boolean.parseBoolean(value)))
.acceptIfHasText(properties.getProperty(TASK_SCHEDULER_POOL_SIZE),
(value) -> integrationProperties.setTaskSchedulerPoolSize(Integer.parseInt(value)))
.acceptIfHasText(properties.getProperty(THROW_EXCEPTION_ON_LATE_REPLY),
(value) -> integrationProperties.setMessagingTemplateThrowExceptionOnLateReply(
Boolean.parseBoolean(value)))
.acceptIfHasText(properties.getProperty(READ_ONLY_HEADERS),
(value) -> integrationProperties.setReadOnlyHeaders(
StringUtils.commaDelimitedListToStringArray(value)))
.acceptIfHasText(properties.getProperty(ENDPOINTS_NO_AUTO_STARTUP),
(value) -> integrationProperties.setNoAutoStartupEndpoints(
StringUtils.commaDelimitedListToStringArray(value)));
return integrationProperties;
}
/**
* @return {@link Properties} with default values for Integration properties.
*/
public static Properties defaults() {
return DEFAULTS;
@@ -126,7 +338,6 @@ public final class IntegrationProperties {
/**
* Build the bean property definition expression to resolve the value
* from Integration properties within the bean building phase.
*
* @param key the Integration property key.
* @return the bean property definition expression.
* @throws IllegalArgumentException if provided {@code key} isn't an Integration property.
@@ -142,7 +353,4 @@ public final class IntegrationProperties {
}
}
private IntegrationProperties() {
}
}

View File

@@ -2,20 +2,18 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<util:properties id="integrationGlobalProperties">
<prop key="spring.integration.channels.maxUnicastSubscribers">456</prop>
<prop key="spring.integration.channels.maxBroadcastSubscribers">789</prop>
</util:properties>
<bean id="integrationGlobalProperties" class="org.springframework.integration.context.IntegrationProperties">
<property name="channelsMaxUnicastSubscribers" value="456"/>
<property name="channelsMaxBroadcastSubscribers" value="789"/>
</bean>
<import resource="DispatcherMaxSubscribersDefaultConfigurationTests-context.xml" />
<import resource="DispatcherMaxSubscribersDefaultConfigurationTests-context.xml"/>
<int:channel id="oneSub">
<int:dispatcher max-subscribers="1" />
<int:dispatcher max-subscribers="1"/>
</int:channel>
</beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 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.
@@ -20,24 +20,21 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.util.Properties;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* @author Artem Bilan
*
* @since 3.0
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@SpringJUnitConfig
public class IntegrationContextTests {
@Autowired
@@ -56,6 +53,7 @@ public class IntegrationContextTests {
private ThreadPoolTaskScheduler taskScheduler;
@Test
@SuppressWarnings("deprecation")
public void testIntegrationContextComponents() {
assertThat(this.integrationProperties.get(IntegrationProperties.THROW_EXCEPTION_ON_LATE_REPLY))
.isEqualTo("true");

View File

@@ -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.
@@ -17,6 +17,7 @@
package org.springframework.integration.gateway;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -29,15 +30,13 @@ import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@@ -86,8 +85,7 @@ import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
@@ -97,8 +95,7 @@ import org.springframework.util.concurrent.ListenableFutureCallback;
* @author Gary Russell
* @author Artem Bilan
*/
@ContextConfiguration(classes = GatewayInterfaceTests.TestConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
@SpringJUnitConfig(classes = GatewayInterfaceTests.TestConfig.class)
@DirtiesContext
@ActiveProfiles("gatewayTest")
public class GatewayInterfaceTests {
@@ -236,7 +233,7 @@ public class GatewayInterfaceTests {
}
@Test
public void testWithServiceUnAnnotatedMethodGlobalHeaderDoesntOverride() throws Exception {
public void testWithServiceUnAnnotatedMethodGlobalHeaderDoesNotOverride() throws Exception {
ConfigurableApplicationContext ac =
new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", getClass());
DirectChannel channel = ac.getBean("requestChannelBaz", DirectChannel.class);
@@ -344,9 +341,9 @@ public class GatewayInterfaceTests {
ac.close();
}
@Test(expected = IllegalArgumentException.class)
@Test
public void testWithServiceAsNotAnInterface() {
new GatewayProxyFactoryBean(NotAnInterface.class);
assertThatIllegalArgumentException().isThrownBy(() -> new GatewayProxyFactoryBean(NotAnInterface.class));
}
@Test
@@ -420,7 +417,7 @@ public class GatewayInterfaceTests {
ListenableFuture<Thread> result2 = this.execGateway.test2(Thread.currentThread());
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<Thread> thread = new AtomicReference<>();
result2.addCallback(new ListenableFutureCallback<Thread>() {
result2.addCallback(new ListenableFutureCallback<>() {
@Override
public void onSuccess(Thread result) {
@@ -431,6 +428,7 @@ public class GatewayInterfaceTests {
@Override
public void onFailure(Throwable t) {
}
});
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(result2.get().getName()).startsWith("exec-");
@@ -594,9 +592,9 @@ public class GatewayInterfaceTests {
public static class TestConfig {
@Bean(name = IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME)
public static Properties integrationProperties() {
Properties properties = new Properties();
properties.setProperty(IntegrationProperties.READ_ONLY_HEADERS, IGNORE_HEADER);
public static IntegrationProperties integrationProperties() {
IntegrationProperties properties = new IntegrationProperties();
properties.setReadOnlyHeaders(IGNORE_HEADER);
return properties;
}

View File

@@ -2,14 +2,12 @@
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">
<util:properties id="integrationGlobalProperties">
<beans:prop key="spring.integration.readOnly.headers">contentType,foo</beans:prop>
</util:properties>
<beans:bean id="integrationGlobalProperties" class="org.springframework.integration.context.IntegrationProperties">
<beans:property name="readOnlyHeaders" value="contentType,foo"/>
</beans:bean>
<object-to-json-transformer id="defaultTransformer" input-channel="defaultObjectMapperInput"/>

View File

@@ -154,8 +154,7 @@ The next section describes what happens if exceptions occur within the asynchron
Certain global framework properties can be overridden by providing a properties file on the classpath.
The default properties can be found in `/META-INF/spring.integration.default.properties` in the `spring-integration-core` jar.
You can see them on GitHub https://github.com/spring-projects/spring-integration/blob/master/spring-integration-core/src/main/resources/META-INF/spring.integration.default.properties[here].
The default properties can be found in `org.springframework.integration.context.IntegrationProperties` class.
The following listing shows the default values:
====
@@ -205,7 +204,7 @@ See <<./error-handling.adoc#error-handling,Error Handling>> for more information
Since version 5.5.
====
These properties can be overridden by adding a `/META-INF/spring.integration.properties` file to the classpath.
These properties can be overridden by adding a `/META-INF/spring.integration.properties` file to the classpath or an `IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME` bean for the `org.springframework.integration.context.IntegrationProperties` instance.
You need not provide all the properties -- only those that you want to override.
Starting with version 5.1, all the merged global properties are printed in the logs after application context startup when a `DEBUG` logic level is turned on for the `org.springframework.integration` category.

View File

@@ -21,6 +21,7 @@ If you are interested in more details, see the Issue Tracker tickets that were r
All the persistent `MessageGroupStore` implementation provide a `streamMessagesForGroup(Object groupId)` contract based on the target database streaming API.
See <<./message-store.adoc#message-store,Message Store>> for more information.
The `integrationGlobalProperties` bean (if declared) must be now an instance of `org.springframework.integration.context.IntegrationProperties` instead of `java.util.Properties`, which support is deprecated for backward compatibility.
The `spring.integration.channels.error.requireSubscribers=true` global property is added to indicate that the global default `errorChannel` must be configured with the `requireSubscribers` option (or not).
The `spring.integration.channels.error.ignoreFailures=true` global property is added to indicate that the global default `errorChannel` must ignore (or not) dispatching errors and pass the message to the next handler.
See <<./configuration.adoc#global-properties,Global Properties>> for more information.