Browse Source
* pr/25377: Polish "Expose Spring Integration global properties" Expose Spring Integration global properties Closes gh-25377pull/25815/head
8 changed files with 505 additions and 2 deletions
@ -0,0 +1,113 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.boot.autoconfigure.integration; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication; |
||||||
|
import org.springframework.boot.env.EnvironmentPostProcessor; |
||||||
|
import org.springframework.boot.env.OriginTrackedMapPropertySource; |
||||||
|
import org.springframework.boot.env.PropertiesPropertySourceLoader; |
||||||
|
import org.springframework.boot.origin.Origin; |
||||||
|
import org.springframework.boot.origin.OriginLookup; |
||||||
|
import org.springframework.core.Ordered; |
||||||
|
import org.springframework.core.env.ConfigurableEnvironment; |
||||||
|
import org.springframework.core.env.PropertySource; |
||||||
|
import org.springframework.core.io.ClassPathResource; |
||||||
|
import org.springframework.core.io.Resource; |
||||||
|
import org.springframework.integration.context.IntegrationProperties; |
||||||
|
|
||||||
|
/** |
||||||
|
* An {@link EnvironmentPostProcessor} that maps the configuration of |
||||||
|
* {@code META-INF/spring.integration.properties} in the environment. |
||||||
|
* |
||||||
|
* @author Artem Bilan |
||||||
|
* @author Stephane Nicoll |
||||||
|
*/ |
||||||
|
class IntegrationPropertiesEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getOrder() { |
||||||
|
return Ordered.LOWEST_PRECEDENCE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { |
||||||
|
Resource resource = new ClassPathResource("META-INF/spring.integration.properties"); |
||||||
|
if (resource.exists()) { |
||||||
|
registerIntegrationPropertiesPropertySource(environment, resource); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void registerIntegrationPropertiesPropertySource(ConfigurableEnvironment environment, Resource resource) { |
||||||
|
PropertiesPropertySourceLoader loader = new PropertiesPropertySourceLoader(); |
||||||
|
try { |
||||||
|
OriginTrackedMapPropertySource propertyFileSource = (OriginTrackedMapPropertySource) loader |
||||||
|
.load("META-INF/spring.integration.properties", resource).get(0); |
||||||
|
environment.getPropertySources().addLast(new IntegrationPropertiesPropertySource(propertyFileSource)); |
||||||
|
} |
||||||
|
catch (IOException ex) { |
||||||
|
throw new IllegalStateException("Failed to load integration properties from " + resource, ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static final class IntegrationPropertiesPropertySource extends PropertySource<Map<String, Object>> |
||||||
|
implements OriginLookup<String> { |
||||||
|
|
||||||
|
private static final String PREFIX = "spring.integration."; |
||||||
|
|
||||||
|
private static final Map<String, String> KEYS_MAPPING; |
||||||
|
|
||||||
|
static { |
||||||
|
Map<String, String> mappings = new HashMap<>(); |
||||||
|
mappings.put(PREFIX + "channel.auto-create", IntegrationProperties.CHANNELS_AUTOCREATE); |
||||||
|
mappings.put(PREFIX + "channel.max-unicast-subscribers", |
||||||
|
IntegrationProperties.CHANNELS_MAX_UNICAST_SUBSCRIBERS); |
||||||
|
mappings.put(PREFIX + "channel.max-broadcast-subscribers", |
||||||
|
IntegrationProperties.CHANNELS_MAX_BROADCAST_SUBSCRIBERS); |
||||||
|
mappings.put(PREFIX + "error.require-subscribers", IntegrationProperties.ERROR_CHANNEL_REQUIRE_SUBSCRIBERS); |
||||||
|
mappings.put(PREFIX + "error.ignore-failures", IntegrationProperties.ERROR_CHANNEL_IGNORE_FAILURES); |
||||||
|
mappings.put(PREFIX + "endpoint.throw-exception-on-late-reply", |
||||||
|
IntegrationProperties.THROW_EXCEPTION_ON_LATE_REPLY); |
||||||
|
mappings.put(PREFIX + "endpoint.read-only-headers", IntegrationProperties.READ_ONLY_HEADERS); |
||||||
|
mappings.put(PREFIX + "endpoint.no-auto-startup", IntegrationProperties.ENDPOINTS_NO_AUTO_STARTUP); |
||||||
|
KEYS_MAPPING = Collections.unmodifiableMap(mappings); |
||||||
|
} |
||||||
|
|
||||||
|
private final OriginTrackedMapPropertySource delegate; |
||||||
|
|
||||||
|
IntegrationPropertiesPropertySource(OriginTrackedMapPropertySource delegate) { |
||||||
|
super("META-INF/spring.integration.properties", delegate.getSource()); |
||||||
|
this.delegate = delegate; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object getProperty(String name) { |
||||||
|
return this.delegate.getProperty(KEYS_MAPPING.get(name)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Origin getOrigin(String key) { |
||||||
|
return this.delegate.getOrigin(KEYS_MAPPING.get(key)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,125 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012-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. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.springframework.boot.autoconfigure.integration; |
||||||
|
|
||||||
|
import java.io.FileNotFoundException; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.function.Consumer; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication; |
||||||
|
import org.springframework.boot.origin.Origin; |
||||||
|
import org.springframework.boot.origin.OriginLookup; |
||||||
|
import org.springframework.boot.origin.TextResourceOrigin; |
||||||
|
import org.springframework.core.env.ConfigurableEnvironment; |
||||||
|
import org.springframework.core.env.MapPropertySource; |
||||||
|
import org.springframework.core.env.PropertySource; |
||||||
|
import org.springframework.core.env.StandardEnvironment; |
||||||
|
import org.springframework.core.io.ClassPathResource; |
||||||
|
import org.springframework.core.io.Resource; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||||
|
import static org.mockito.Mockito.mock; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests for {@link IntegrationPropertiesEnvironmentPostProcessor}. |
||||||
|
* |
||||||
|
* @author Stephane Nicoll |
||||||
|
*/ |
||||||
|
class IntegrationPropertiesEnvironmentPostProcessorTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
void postProcessEnvironmentAddPropertySource() { |
||||||
|
ConfigurableEnvironment environment = new StandardEnvironment(); |
||||||
|
new IntegrationPropertiesEnvironmentPostProcessor().postProcessEnvironment(environment, |
||||||
|
mock(SpringApplication.class)); |
||||||
|
assertThat(environment.getPropertySources().contains("META-INF/spring.integration.properties")).isTrue(); |
||||||
|
assertThat(environment.getProperty("spring.integration.endpoint.no-auto-startup")).isEqualTo("testService*"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void postProcessEnvironmentAddPropertySourceLast() { |
||||||
|
ConfigurableEnvironment environment = new StandardEnvironment(); |
||||||
|
environment.getPropertySources().addLast(new MapPropertySource("test", |
||||||
|
Collections.singletonMap("spring.integration.endpoint.no-auto-startup", "another*"))); |
||||||
|
new IntegrationPropertiesEnvironmentPostProcessor().postProcessEnvironment(environment, |
||||||
|
mock(SpringApplication.class)); |
||||||
|
assertThat(environment.getPropertySources().contains("META-INF/spring.integration.properties")).isTrue(); |
||||||
|
assertThat(environment.getProperty("spring.integration.endpoint.no-auto-startup")).isEqualTo("another*"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void registerIntegrationPropertiesPropertySourceWithUnknownResourceThrowsException() { |
||||||
|
ConfigurableEnvironment environment = new StandardEnvironment(); |
||||||
|
ClassPathResource unknown = new ClassPathResource("does-not-exist.properties", getClass()); |
||||||
|
assertThatThrownBy(() -> new IntegrationPropertiesEnvironmentPostProcessor() |
||||||
|
.registerIntegrationPropertiesPropertySource(environment, unknown)) |
||||||
|
.isInstanceOf(IllegalStateException.class).hasCauseInstanceOf(FileNotFoundException.class) |
||||||
|
.hasMessageContaining(unknown.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
void registerIntegrationPropertiesPropertySourceWithResourceAddPropertySource() { |
||||||
|
ConfigurableEnvironment environment = new StandardEnvironment(); |
||||||
|
new IntegrationPropertiesEnvironmentPostProcessor().registerIntegrationPropertiesPropertySource(environment, |
||||||
|
new ClassPathResource("spring.integration.properties", getClass())); |
||||||
|
assertThat(environment.getProperty("spring.integration.channel.auto-create", Boolean.class)).isFalse(); |
||||||
|
assertThat(environment.getProperty("spring.integration.channel.max-unicast-subscribers", Integer.class)) |
||||||
|
.isEqualTo(4); |
||||||
|
assertThat(environment.getProperty("spring.integration.channel.max-broadcast-subscribers", Integer.class)) |
||||||
|
.isEqualTo(6); |
||||||
|
assertThat(environment.getProperty("spring.integration.error.require-subscribers", Boolean.class)).isFalse(); |
||||||
|
assertThat(environment.getProperty("spring.integration.error.ignore-failures", Boolean.class)).isFalse(); |
||||||
|
assertThat(environment.getProperty("spring.integration.endpoint.throw-exception-on-late-reply", Boolean.class)) |
||||||
|
.isTrue(); |
||||||
|
assertThat(environment.getProperty("spring.integration.endpoint.read-only-headers", String.class)) |
||||||
|
.isEqualTo("header1,header2"); |
||||||
|
assertThat(environment.getProperty("spring.integration.endpoint.no-auto-startup", String.class)) |
||||||
|
.isEqualTo("testService,anotherService"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
void registerIntegrationPropertiesPropertySourceWithResourceCanRetrieveOrigin() { |
||||||
|
ConfigurableEnvironment environment = new StandardEnvironment(); |
||||||
|
ClassPathResource resource = new ClassPathResource("spring.integration.properties", getClass()); |
||||||
|
new IntegrationPropertiesEnvironmentPostProcessor().registerIntegrationPropertiesPropertySource(environment, |
||||||
|
resource); |
||||||
|
PropertySource<?> ps = environment.getPropertySources().get("META-INF/spring.integration.properties"); |
||||||
|
assertThat(ps).isNotNull().isInstanceOf(OriginLookup.class); |
||||||
|
OriginLookup<String> originLookup = (OriginLookup<String>) ps; |
||||||
|
assertThat(originLookup.getOrigin("spring.integration.channel.auto-create")) |
||||||
|
.satisfies(textOrigin(resource, 0, 39)); |
||||||
|
assertThat(originLookup.getOrigin("spring.integration.channel.max-unicast-subscribers")) |
||||||
|
.satisfies(textOrigin(resource, 1, 50)); |
||||||
|
assertThat(originLookup.getOrigin("spring.integration.channel.max-broadcast-subscribers")) |
||||||
|
.satisfies(textOrigin(resource, 2, 52)); |
||||||
|
} |
||||||
|
|
||||||
|
private Consumer<Origin> textOrigin(Resource resource, int line, int column) { |
||||||
|
return (origin) -> { |
||||||
|
assertThat(origin).isInstanceOf(TextResourceOrigin.class); |
||||||
|
TextResourceOrigin textOrigin = (TextResourceOrigin) origin; |
||||||
|
assertThat(textOrigin.getResource()).isEqualTo(resource); |
||||||
|
assertThat(textOrigin.getLocation().getLine()).isEqualTo(line); |
||||||
|
assertThat(textOrigin.getLocation().getColumn()).isEqualTo(column); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
spring.integration.endpoints.noAutoStartup=testService* |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
spring.integration.channels.autoCreate=false |
||||||
|
spring.integration.channels.maxUnicastSubscribers=4 |
||||||
|
spring.integration.channels.maxBroadcastSubscribers=6 |
||||||
|
spring.integration.channels.error.requireSubscribers=false |
||||||
|
spring.integration.channels.error.ignoreFailures=false |
||||||
|
spring.integration.messagingTemplate.throwExceptionOnLateReply=true |
||||||
|
spring.integration.readOnly.headers=header1,header2 |
||||||
|
spring.integration.endpoints.noAutoStartup=testService,anotherService |
||||||
Loading…
Reference in new issue