Skip to content

INT-4322: Log merged global properties on startup #2612

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -29,6 +32,7 @@

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;
Expand Down Expand Up @@ -80,7 +84,8 @@
*
* @see IntegrationContextUtils
*/
class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProcessor, BeanClassLoaderAware {
class DefaultConfiguringBeanFactoryPostProcessor
implements BeanFactoryPostProcessor, BeanClassLoaderAware, SmartInitializingSingleton {

private static final Log logger = LogFactory.getLog(DefaultConfiguringBeanFactoryPostProcessor.class);

Expand Down Expand Up @@ -128,14 +133,26 @@ else if (logger.isWarnEnabled()) {
}
}

@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.
* The bean name is defined by the constant {@link IntegrationContextUtils#NULL_CHANNEL_BEAN_NAME}.
*/
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);
Expand Down Expand Up @@ -319,7 +336,7 @@ private void registerBuiltInBeans() {
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);

}
Expand Down Expand Up @@ -482,7 +499,8 @@ private void registerListCapableArgumentResolvers() {
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
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;
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -176,15 +180,33 @@ private static <T> T getBeanOfType(BeanFactory beanFactory, String beanName, Cla
* 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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -28,12 +28,7 @@
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;
Expand Down Expand Up @@ -134,30 +129,14 @@ public static ThreadPoolTaskScheduler createTaskScheduler(int poolSize) {
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);
}


Expand Down
38 changes: 23 additions & 15 deletions src/reference/asciidoc/configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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:

====
Expand All @@ -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.
Expand Down Expand Up @@ -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.

Expand All @@ -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:

Expand All @@ -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.
Expand All @@ -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>>).
Expand All @@ -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

Expand Down
10 changes: 9 additions & 1 deletion src/reference/asciidoc/whats-new.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.