INT-4322: Log merged global properties on startup (#2612)
* INT-4322: Log merged global properties on startup JIRA: https://jira.spring.io/browse/INT-4322 Now we have DEBUG message in logs like this: ``` Spring Integration global properties: spring.integration.endpoints.noAutoStartup=fooService* spring.integration.taskScheduler.poolSize=20 spring.integration.channels.maxUnicastSubscribers=0x7fffffff spring.integration.channels.autoCreate=true spring.integration.channels.maxBroadcastSubscribers=0x7fffffff spring.integration.readOnly.headers= spring.integration.messagingTemplate.throwExceptionOnLateReply=true ``` * Also register a `mergedIntegrationGlobalProperties` bean on a first `IntegrationContextUtils.getIntegrationProperties()` call to avoid a new `Properties` object on each call * * Fix `TestUtils` to register a `BeanDefinition` instead of `registerSingleton()` and an explicit `afterPropertiesSet()` call * * Document mentioned changes * Some polishing for `whats-new.adoc`
This commit is contained in:
committed by
Gary Russell
parent
8b54277a34
commit
8305c6fec3
@@ -17,10 +17,13 @@
|
||||
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;
|
||||
|
||||
@@ -29,6 +32,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
@@ -80,7 +84,8 @@ import org.springframework.util.ClassUtils;
|
||||
*
|
||||
* @see IntegrationContextUtils
|
||||
*/
|
||||
class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProcessor, BeanClassLoaderAware {
|
||||
class DefaultConfiguringBeanFactoryPostProcessor
|
||||
implements BeanFactoryPostProcessor, BeanClassLoaderAware, SmartInitializingSingleton {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DefaultConfiguringBeanFactoryPostProcessor.class);
|
||||
|
||||
@@ -128,6 +133,18 @@ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProce
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
if (logger.isDebugEnabled()) {
|
||||
Properties integrationProperties = IntegrationContextUtils.getIntegrationProperties(this.beanFactory);
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
integrationProperties.list(new PrintWriter(writer));
|
||||
StringBuffer propertiesBuffer = writer.getBuffer()
|
||||
.delete(0, "-- listing properties --".length());
|
||||
logger.debug("\nSpring Integration global properties:\n" + propertiesBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a null channel in the application context.
|
||||
@@ -135,7 +152,7 @@ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProce
|
||||
*/
|
||||
private void registerNullChannel() {
|
||||
if (this.beanFactory.containsBean(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME)) {
|
||||
BeanDefinition nullChannelDefinition = null;
|
||||
BeanDefinition nullChannelDefinition;
|
||||
if (this.beanFactory.containsBeanDefinition(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME)) {
|
||||
nullChannelDefinition =
|
||||
this.beanFactory.getBeanDefinition(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME);
|
||||
@@ -319,7 +336,7 @@ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProce
|
||||
jsonPathClass = null;
|
||||
logger.warn("The '#jsonPath' SpEL function cannot be registered. " +
|
||||
"An old json-path.jar version is detected in the classpath." +
|
||||
"At least 1.2.0 is required; see version information at: " +
|
||||
"At least 2.4.0 is required; see version information at: " +
|
||||
"https://github.com/jayway/JsonPath/releases", e);
|
||||
|
||||
}
|
||||
@@ -482,7 +499,8 @@ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProce
|
||||
private BeanDefinition internalArgumentResolversBuilder(boolean listCapable) {
|
||||
ManagedList<BeanDefinition> resolvers = new ManagedList<>();
|
||||
resolvers.add(new RootBeanDefinition(PayloadExpressionArgumentResolver.class));
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(NullAwarePayloadArgumentResolver.class);
|
||||
BeanDefinitionBuilder builder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(NullAwarePayloadArgumentResolver.class);
|
||||
builder.addConstructorArgReference(IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME);
|
||||
// TODO Validator ?
|
||||
resolvers.add(builder.getBeanDefinition());
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.integration.context;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.expression.spel.support.SimpleEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.integration.config.IntegrationConfigUtils;
|
||||
@@ -57,6 +59,8 @@ public abstract class IntegrationContextUtils {
|
||||
|
||||
public static final String INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME = "integrationGlobalProperties";
|
||||
|
||||
public static final String MERGED_INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME = "mergedIntegrationGlobalProperties";
|
||||
|
||||
public static final String CHANNEL_INITIALIZER_BEAN_NAME = "channelInitializer";
|
||||
|
||||
public static final String AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME = "$autoCreateChannelCandidates";
|
||||
@@ -176,15 +180,33 @@ public abstract class IntegrationContextUtils {
|
||||
* provided {@code #beanFactory} or provided {@code #beanFactory} is null.
|
||||
*/
|
||||
public static Properties getIntegrationProperties(BeanFactory beanFactory) {
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(IntegrationProperties.defaults());
|
||||
Properties properties;
|
||||
if (beanFactory != null) {
|
||||
Properties userProperties =
|
||||
getBeanOfType(beanFactory, INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, Properties.class);
|
||||
if (userProperties != null) {
|
||||
properties.putAll(userProperties);
|
||||
properties = getBeanOfType(beanFactory, MERGED_INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, Properties.class);
|
||||
if (properties == null) {
|
||||
Properties propertiesToRegister = new Properties();
|
||||
propertiesToRegister.putAll(IntegrationProperties.defaults());
|
||||
Properties userProperties =
|
||||
getBeanOfType(beanFactory, INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, Properties.class);
|
||||
if (userProperties != null) {
|
||||
propertiesToRegister.putAll(userProperties);
|
||||
}
|
||||
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(Properties.class);
|
||||
beanDefinition.setInstanceSupplier(() -> propertiesToRegister);
|
||||
|
||||
registry.registerBeanDefinition(MERGED_INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, beanDefinition);
|
||||
}
|
||||
|
||||
properties = propertiesToRegister;
|
||||
}
|
||||
}
|
||||
else {
|
||||
properties = new Properties();
|
||||
properties.putAll(IntegrationProperties.defaults());
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -28,12 +28,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -134,30 +129,14 @@ public abstract class TestUtils {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void registerBean(String beanName, Object bean, BeanFactory beanFactory) {
|
||||
Assert.notNull(beanName, "bean name must not be null");
|
||||
ConfigurableListableBeanFactory configurableListableBeanFactory = null;
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
configurableListableBeanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
}
|
||||
else if (beanFactory instanceof GenericApplicationContext) {
|
||||
configurableListableBeanFactory = ((GenericApplicationContext) beanFactory).getBeanFactory();
|
||||
}
|
||||
if (bean instanceof BeanNameAware) {
|
||||
((BeanNameAware) bean).setBeanName(beanName);
|
||||
}
|
||||
if (bean instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) bean).setBeanFactory(beanFactory);
|
||||
}
|
||||
if (bean instanceof InitializingBean) {
|
||||
try {
|
||||
((InitializingBean) bean).afterPropertiesSet();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new FatalBeanException("failed to register bean with test context", e);
|
||||
}
|
||||
}
|
||||
configurableListableBeanFactory.registerSingleton(beanName, bean); //NOSONAR false positive
|
||||
Assert.isInstanceOf(GenericApplicationContext.class, beanFactory,
|
||||
"beanFactory must be an instance of GenericApplicationContext");
|
||||
GenericApplicationContext applicationContext = (GenericApplicationContext) beanFactory;
|
||||
|
||||
applicationContext.registerBean(beanName, (Class<Object>) bean.getClass(), () -> bean);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -133,9 +133,7 @@ Such endpoints have no poller configuration, since their handlers are invoked di
|
||||
|
||||
[IMPORTANT]
|
||||
=====
|
||||
When running in a JEE container, you may need to use Spring's `TimerManagerTaskScheduler`, as described
|
||||
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-task-scheduler-implementations[here],
|
||||
instead of the default `taskScheduler`.
|
||||
When running in a JEE container, you may need to use Spring's `TimerManagerTaskScheduler`, as described http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-task-scheduler-implementations[here], instead of the default `taskScheduler`.
|
||||
To do so, define a bean with the appropriate JNDI name for your environment, as the following example shows:
|
||||
|
||||
====
|
||||
@@ -154,8 +152,7 @@ The next section describes what happens if exceptions occur within the asynchron
|
||||
[[namespace-errorhandler]]
|
||||
=== Error Handling
|
||||
|
||||
As described in the <<overview,overview>> at the very beginning of this manual, one of the main motivations behind a message-oriented
|
||||
framework such as Spring Integration is to promote loose coupling between components.
|
||||
As described in the <<overview,overview>> at the very beginning of this manual, one of the main motivations behind a message-oriented framework such as Spring Integration is to promote loose coupling between components.
|
||||
The message channel plays an important role, in that producers and consumers do not have to know about each other.
|
||||
However, the advantages also have some drawbacks.
|
||||
Some things become more complicated in a loosely coupled environment, and one example is error handling.
|
||||
@@ -200,10 +197,8 @@ The following example shows how to define an error channel backed by a queue wit
|
||||
|
||||
NOTE: The default error channel is a `PublishSubscribeChannel`.
|
||||
|
||||
The most important thing to understand here is that the messaging-based error handling applies only to exceptions
|
||||
that are thrown by a Spring Integration task that is executing within a `TaskExecutor`.
|
||||
This does not apply to exceptions thrown by a handler that operates within the same thread as the sender (for example,
|
||||
through a `DirectChannel` as described earlier in this section).
|
||||
The most important thing to understand here is that the messaging-based error handling applies only to exceptions that are thrown by a Spring Integration task that is executing within a `TaskExecutor`.
|
||||
This does not apply to exceptions thrown by a handler that operates within the same thread as the sender (for example, through a `DirectChannel` as described earlier in this section).
|
||||
|
||||
NOTE: When exceptions occur in a scheduled poller task's execution, those exceptions are wrapped in `ErrorMessage` instances and sent to the 'errorChannel' as well.
|
||||
|
||||
@@ -227,8 +222,7 @@ The `DefaultErrorMessageStrategy` does exactly that.
|
||||
|
||||
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.
|
||||
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 following listing shows the default values:
|
||||
|
||||
@@ -245,8 +239,7 @@ spring.integration.endpoints.noAutoStartup= <7>
|
||||
spring.integration.postProcessDynamicBeans=false <8>
|
||||
----
|
||||
|
||||
<1> When true, `input-channel` instances are automatically declared as `DirectChannel` instances when not explicitly found in the
|
||||
application context.
|
||||
<1> When true, `input-channel` instances are automatically declared as `DirectChannel` instances when not explicitly found in the application context.
|
||||
|
||||
<2> Sets the default number of subscribers allowed on, for example, a `DirectChannel`.
|
||||
It can be used to avoid inadvertently subscribing multiple endpoints to the same channel.
|
||||
@@ -259,8 +252,7 @@ You can override it on individual channels by setting the `max-subscribers` attr
|
||||
<4> The number of threads available in the default `taskScheduler` bean.
|
||||
See <<namespace-taskscheduler>>.
|
||||
|
||||
<5> When `true`, messages that arrive at a gateway reply channel throw an exception when the gateway is not
|
||||
expecting a reply (because the sending thread has timed out or already received a reply).
|
||||
<5> When `true`, messages that arrive at a gateway reply channel throw an exception when the gateway is not expecting a reply (because the sending thread has timed out or already received a reply).
|
||||
|
||||
<6> A comma-separated list of message header names that should not be populated into `Message` instances during a header copying operation.
|
||||
The list is used by the `DefaultMessageBuilderFactory` bean and propagated to the `IntegrationMessageHeaderAccessor` instances (see <<message-header-accessor>>) used to build messages via `MessageBuilder` (see <<message-builder>>).
|
||||
@@ -279,6 +271,22 @@ Since version 4.3.15.
|
||||
These properties can be overridden by adding a `/META-INF/spring.integration.properties` file to the classpath.
|
||||
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.
|
||||
The output looks like this:
|
||||
====
|
||||
[source]
|
||||
----
|
||||
Spring Integration global properties:
|
||||
|
||||
spring.integration.endpoints.noAutoStartup=fooService*
|
||||
spring.integration.taskScheduler.poolSize=20
|
||||
spring.integration.channels.maxUnicastSubscribers=0x7fffffff
|
||||
spring.integration.channels.autoCreate=true
|
||||
spring.integration.channels.maxBroadcastSubscribers=0x7fffffff
|
||||
spring.integration.readOnly.headers=
|
||||
spring.integration.messagingTemplate.throwExceptionOnLateReply=true
|
||||
----
|
||||
|
||||
[[annotations]]
|
||||
=== Annotation Support
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ The following changes have been made in version 5.1:
|
||||
* <<x5.1-integration-flows-generated-bean-names>>
|
||||
* <<x5.1-aggregator>>
|
||||
* <<x5.1-publisher>>
|
||||
* <<x51.-integration-graph>>
|
||||
* <<x51.-global-properties>>
|
||||
|
||||
[[x5.1-java-dsl]]
|
||||
==== Java DSL
|
||||
@@ -218,4 +220,10 @@ See <<micrometer-integration>> for more information.
|
||||
=== Integration Graph Customization
|
||||
|
||||
It is now possible to add additional properties to the `IntegrationNode` s via `Function<NamedComponent, Map<String, Object>> additionalPropertiesCallback` on the `IntegrationGraphServer`.
|
||||
See <<<<integration-graph>>>> for more information.
|
||||
See <<integration-graph>> for more information.
|
||||
|
||||
[[x51.-x51.-global-properties]]
|
||||
=== Integration Global Properties
|
||||
|
||||
The Integration global properties (including defaults) can now be printed in the logs, when a `DEBUG` logic level is turned on for the `org.springframework.integration` category.
|
||||
See <<global-properties>> for more information.
|
||||
|
||||
Reference in New Issue
Block a user