INT-4326: Add endpoints.noAutoStartup property

JIRA: https://jira.spring.io/browse/INT-4326

To let to disable autoStartup for particular endpoint bean globally,
in one place, add `spring.integration.endpoints.noAutoStartup`
to the `spring.integration.properties`

So, now all the `AbstractEndpoint` checks that property for matching its
bean name to configure `autoStartup` property to `false`

**Cherry-pick to 4.3.x**

Conflicts:
	spring-integration-core/src/main/resources/META-INF/spring.integration.default.properties
	spring-integration-core/src/test/resources/META-INF/spring.integration.properties
	src/reference/asciidoc/configuration.adoc
Resolved.
This commit is contained in:
Artem Bilan
2017-08-11 16:26:46 -04:00
committed by Gary Russell
parent c877b041d4
commit d8ecdce83d
8 changed files with 68 additions and 8 deletions

View File

@@ -71,7 +71,7 @@ public class ConsumerEndpointFactoryBean
private volatile PollerMetadata pollerMetadata; private volatile PollerMetadata pollerMetadata;
private volatile boolean autoStartup = true; private volatile Boolean autoStartup;
private volatile int phase = 0; private volatile int phase = 0;
@@ -133,7 +133,7 @@ public class ConsumerEndpointFactoryBean
this.beanClassLoader = classLoader; this.beanClassLoader = classLoader;
} }
public void setAutoStartup(boolean autoStartup) { public void setAutoStartup(Boolean autoStartup) {
this.autoStartup = autoStartup; this.autoStartup = autoStartup;
} }
@@ -257,7 +257,9 @@ public class ConsumerEndpointFactoryBean
Assert.isNull(this.pollerMetadata, "A poller should not be specified for endpoint '" + this.beanName Assert.isNull(this.pollerMetadata, "A poller should not be specified for endpoint '" + this.beanName
+ "', since '" + channel + "' is a SubscribableChannel (not pollable)."); + "', since '" + channel + "' is a SubscribableChannel (not pollable).");
this.endpoint = new EventDrivenConsumer((SubscribableChannel) channel, this.handler); this.endpoint = new EventDrivenConsumer((SubscribableChannel) channel, this.handler);
if (this.logger.isWarnEnabled() && !this.autoStartup && channel instanceof FixedSubscriberChannel) { if (this.logger.isWarnEnabled()
&& Boolean.FALSE.equals(this.autoStartup)
&& channel instanceof FixedSubscriberChannel) {
this.logger.warn("'autoStartup=\"false\"' has no effect when using a FixedSubscriberChannel"); this.logger.warn("'autoStartup=\"false\"' has no effect when using a FixedSubscriberChannel");
} }
} }
@@ -287,7 +289,9 @@ public class ConsumerEndpointFactoryBean
} }
this.endpoint.setBeanName(this.beanName); this.endpoint.setBeanName(this.beanName);
this.endpoint.setBeanFactory(this.beanFactory); this.endpoint.setBeanFactory(this.beanFactory);
this.endpoint.setAutoStartup(this.autoStartup); if (this.autoStartup != null) {
this.endpoint.setAutoStartup(this.autoStartup);
}
int phase = this.phase; int phase = this.phase;
if (!this.isPhaseSet && this.endpoint instanceof PollingConsumer) { if (!this.isPhaseSet && this.endpoint instanceof PollingConsumer) {
phase = Integer.MAX_VALUE / 2; phase = Integer.MAX_VALUE / 2;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2014-2016 the original author or authors. * Copyright 2014-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import org.springframework.core.io.support.ResourcePatternResolver;
* their default values from resources 'META-INF/spring.integration.default.properties'. * their default values from resources 'META-INF/spring.integration.default.properties'.
* *
* @author Artem Bilan * @author Artem Bilan
*
* @since 3.0 * @since 3.0
*/ */
public final class IntegrationProperties { public final class IntegrationProperties {
@@ -81,6 +82,11 @@ public final class IntegrationProperties {
*/ */
public static final String READ_ONLY_HEADERS = INTEGRATION_PROPERTIES_PREFIX + "readOnly.headers"; public static final String READ_ONLY_HEADERS = INTEGRATION_PROPERTIES_PREFIX + "readOnly.headers";
/**
* Specifies the value of {@link org.springframework.integration.endpoint.AbstractEndpoint#autoStartup}.
*/
public static final String ENDPOINTS_NO_AUTO_STARTUP = INTEGRATION_PROPERTIES_PREFIX + "endpoints.noAutoStartup";
private static Properties defaults; private static Properties defaults;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -21,7 +21,9 @@ import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.SmartLifecycle; import org.springframework.context.SmartLifecycle;
import org.springframework.integration.context.IntegrationObjectSupport; import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.integration.context.IntegrationProperties;
import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.PatternMatchUtils;
/** /**
* The base class for Message Endpoint implementations. * The base class for Message Endpoint implementations.
@@ -36,9 +38,12 @@ import org.springframework.scheduling.TaskScheduler;
* @author Mark Fisher * @author Mark Fisher
* @author Kris Jacyna * @author Kris Jacyna
* @author Gary Russell * @author Gary Russell
* @author Artem Bilan
*/ */
public abstract class AbstractEndpoint extends IntegrationObjectSupport implements SmartLifecycle { public abstract class AbstractEndpoint extends IntegrationObjectSupport implements SmartLifecycle {
private boolean autoStartupSetExplicitly;
private volatile boolean autoStartup = true; private volatile boolean autoStartup = true;
private volatile int phase = 0; private volatile int phase = 0;
@@ -52,6 +57,7 @@ public abstract class AbstractEndpoint extends IntegrationObjectSupport implemen
public void setAutoStartup(boolean autoStartup) { public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup; this.autoStartup = autoStartup;
this.autoStartupSetExplicitly = true;
} }
public void setPhase(int phase) { public void setPhase(int phase) {
@@ -63,6 +69,23 @@ public abstract class AbstractEndpoint extends IntegrationObjectSupport implemen
super.setTaskScheduler(taskScheduler); super.setTaskScheduler(taskScheduler);
} }
@Override
protected void onInit() throws Exception {
super.onInit();
if (!this.autoStartupSetExplicitly) {
String[] endpointNamePatterns =
getIntegrationProperty(IntegrationProperties.ENDPOINTS_NO_AUTO_STARTUP, String[].class);
for (String pattern : endpointNamePatterns) {
if (PatternMatchUtils.simpleMatch(pattern, getComponentName())) {
this.autoStartup = false;
break;
}
}
}
}
// SmartLifecycle implementation // SmartLifecycle implementation
@Override @Override

View File

@@ -7,3 +7,4 @@ spring.integration.messagingAnnotations.require.componentAnnotation=false
spring.integration.messagingGateway.convertReceiveMessage=false spring.integration.messagingGateway.convertReceiveMessage=false
# Defaults to MessageHeaders.ID and MessageHeaders.TIMESTAMP # Defaults to MessageHeaders.ID and MessageHeaders.TIMESTAMP
spring.integration.readOnly.headers= spring.integration.readOnly.headers=
spring.integration.endpoints.noAutoStartup=

View File

@@ -11,4 +11,7 @@
<service-activator id="fooService" input-channel="foo" output-channel="nullChannel" expression="'foo'"/> <service-activator id="fooService" input-channel="foo" output-channel="nullChannel" expression="'foo'"/>
<service-activator id="fooServiceExplicit" input-channel="foo" output-channel="nullChannel" expression="'foo'"
auto-startup="true"/>
</beans:beans> </beans:beans>

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2014 the original author or authors. * Copyright 2013-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -17,6 +17,8 @@
package org.springframework.integration.context; package org.springframework.integration.context;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Properties; import java.util.Properties;
@@ -25,6 +27,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.test.util.TestUtils; import org.springframework.integration.test.util.TestUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
@@ -32,6 +35,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/** /**
* @author Artem Bilan * @author Artem Bilan
*
* @since 3.0 * @since 3.0
*/ */
@ContextConfiguration @ContextConfiguration
@@ -44,7 +48,11 @@ public class IntegrationContextTests {
@Autowired @Autowired
@Qualifier("fooService") @Qualifier("fooService")
private IntegrationObjectSupport serviceActivator; private AbstractEndpoint serviceActivator;
@Autowired
@Qualifier("fooServiceExplicit")
private AbstractEndpoint serviceActivatorExplicit;
@Autowired @Autowired
private ThreadPoolTaskScheduler taskScheduler; private ThreadPoolTaskScheduler taskScheduler;
@@ -55,6 +63,10 @@ public class IntegrationContextTests {
assertEquals("20", this.integrationProperties.get(IntegrationProperties.TASK_SCHEDULER_POOL_SIZE)); assertEquals("20", this.integrationProperties.get(IntegrationProperties.TASK_SCHEDULER_POOL_SIZE));
assertEquals(this.integrationProperties, this.serviceActivator.getIntegrationProperties()); assertEquals(this.integrationProperties, this.serviceActivator.getIntegrationProperties());
assertEquals(20, TestUtils.getPropertyValue(this.taskScheduler, "poolSize")); assertEquals(20, TestUtils.getPropertyValue(this.taskScheduler, "poolSize"));
assertFalse(this.serviceActivator.isAutoStartup());
assertFalse(this.serviceActivator.isRunning());
assertTrue(this.serviceActivatorExplicit.isAutoStartup());
assertTrue(this.serviceActivatorExplicit.isRunning());
} }
} }

View File

@@ -4,3 +4,4 @@
spring.integration.taskScheduler.poolSize=20 spring.integration.taskScheduler.poolSize=20
spring.integration.messagingTemplate.throwExceptionOnLateReply=true spring.integration.messagingTemplate.throwExceptionOnLateReply=true
spring.integration.readOnly.headers=contentType spring.integration.readOnly.headers=contentType
spring.integration.endpoints.noAutoStartup=fooService*

View File

@@ -206,6 +206,8 @@ spring.integration.channels.maxBroadcastSubscribers=0x7fffffff <3>
spring.integration.taskScheduler.poolSize=10 <4> spring.integration.taskScheduler.poolSize=10 <4>
spring.integration.messagingTemplate.throwExceptionOnLateReply=false <5> spring.integration.messagingTemplate.throwExceptionOnLateReply=false <5>
spring.integration.messagingAnnotations.require.componentAnnotation=false <6> spring.integration.messagingAnnotations.require.componentAnnotation=false <6>
spring.integration.readOnly.headers= <7>
spring.integration.endpoints.noAutoStartup= <8>
---- ----
<1> When true, `input-channel` s will be automatically declared as `DirectChannel` s when not explicitly found in the <1> When true, `input-channel` s will be automatically declared as `DirectChannel` s when not explicitly found in the
@@ -226,6 +228,14 @@ expecting a reply - because the sending thread has timed out, or already receive
<6> When `true`, Messaging Annotation Support (<<annotations>>) requires a declaration of the <6> When `true`, Messaging Annotation Support (<<annotations>>) requires a declaration of the
`@MessageEndpoint` (or any other `@Component`) annotation on the class level. `@MessageEndpoint` (or any other `@Component`) annotation on the class level.
<7> A comma-separated list of message header names which should not be populated into `Message` s 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>>).
By default only `MessageHeaders.ID` and `MessageHeaders.TIMESTAMP` are not copied during message building.
_Since version 4.3.2_
<8> A comma-separated list of `AbstractEndpoint` bean names patterns (`xxx*`, `*xxx`, `*xxx*` or `xxx*yyy`) which should not be started automatically during application startup.
These endpoints can be started later manually by their bean name via `Control Bus` (see <<control-bus>>), by their role using the `SmartLifecycleRoleController` (see <<endpoint-roles>>) or via simple `Lifecycle` bean injection.
The effect of this global property can be explicitly overridden by specifying `auto-startup` XML or `autoStartup` annotation attribute, or via call to the `AbstractEndpoint.setAutoStartup()` in bean definition.
_Since version 4.3.12_
These properties can be overridden by adding a file `/META-INF/spring.integration.properties` to the classpath. These properties can be overridden by adding a file `/META-INF/spring.integration.properties` to the classpath.
It is not necessary to provide all the properties, just those that you want to override. It is not necessary to provide all the properties, just those that you want to override.