From c23d8fb3757883b8cbccc728a16293692ce32cbc Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 21 Dec 2016 10:50:55 -0800 Subject: [PATCH 01/12] Ignore unresolvable placeholder in log properties Update the RelaxedPropertyResolver used to load log properties so that `${...}` patterns are ignored when possible. Fixes gh-7719 --- .../boot/bind/RelaxedPropertyResolver.java | 23 +++++++++++++++++++ .../boot/logging/LoggingSystemProperties.java | 4 ++-- .../logback/DefaultLogbackConfiguration.java | 3 ++- .../LoggingApplicationListenerTests.java | 10 ++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedPropertyResolver.java b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedPropertyResolver.java index 80ff09f91e..c3db1fea70 100644 --- a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedPropertyResolver.java +++ b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedPropertyResolver.java @@ -19,7 +19,9 @@ package org.springframework.boot.bind; import java.util.Map; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; import org.springframework.core.env.PropertyResolver; +import org.springframework.core.env.PropertySourcesPropertyResolver; import org.springframework.util.Assert; /** @@ -145,4 +147,25 @@ public class RelaxedPropertyResolver implements PropertyResolver { keyPrefix); } + /** + * Return a property resolver for the environment, preferring one that ignores + * unresolvable nested placeholders. + * @param environment the source environment + * @param prefix the prefix + * @return a property resolver for the environment + * @since 1.4.3 + */ + public static RelaxedPropertyResolver ignoringUnresolvableNestedPlaceholders( + Environment environment, String prefix) { + Assert.notNull(environment, "Environment must not be null"); + PropertyResolver resolver = environment; + if (environment instanceof ConfigurableEnvironment) { + resolver = new PropertySourcesPropertyResolver( + ((ConfigurableEnvironment) environment).getPropertySources()); + ((PropertySourcesPropertyResolver) resolver) + .setIgnoreUnresolvableNestedPlaceholders(true); + } + return new RelaxedPropertyResolver(resolver, prefix); + } + } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java index 14337f2037..91855cf44d 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java @@ -49,8 +49,8 @@ class LoggingSystemProperties { } public void apply(LogFile logFile) { - RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver( - this.environment, "logging."); + RelaxedPropertyResolver propertyResolver = RelaxedPropertyResolver + .ignoringUnresolvableNestedPlaceholders(this.environment, "logging."); setSystemProperty(propertyResolver, EXCEPTION_CONVERSION_WORD, "exception-conversion-word"); setSystemProperty(propertyResolver, CONSOLE_LOG_PATTERN, "pattern.console"); diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java index d6c4921154..d10bd1f0dc 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java @@ -72,7 +72,8 @@ class DefaultLogbackConfiguration { if (environment == null) { return new PropertySourcesPropertyResolver(null); } - return new RelaxedPropertyResolver(environment, "logging.pattern."); + return RelaxedPropertyResolver.ignoringUnresolvableNestedPlaceholders(environment, + "logging.pattern."); } public void apply(LogbackConfigurator config) { diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java index 92536c1f27..a6ac5e7d51 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java @@ -467,6 +467,16 @@ public class LoggingApplicationListenerTests { assertThat(System.getProperty("PID")).isNotNull(); } + @Test + public void environmentPropertiesIgnoreUnresolvablePlaceholders() { + // gh-7719 + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, + "logging.pattern.console=console ${pid}"); + this.initializer.initialize(this.context.getEnvironment(), + this.context.getClassLoader()); + assertThat(System.getProperty("CONSOLE_LOG_PATTERN")).isEqualTo("console ${pid}"); + } + @Test public void logFilePropertiesCanReferenceSystemProperties() { TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, From 138b96cf5f9536aaecdc8ebcc86cbe00a557b6ae Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 21 Dec 2016 11:18:24 -0800 Subject: [PATCH 02/12] Use unique `testRestTemplate` bean name Update `SpringBootTestContextCustomizer` to use the full qualified TestRestTemplate as the registered bean name. Prior to this commit it was possible that the customizer would replace the relatively common bean name `testRestTemplate`. Fixes gh-7711 --- .../SpringBootTestContextCustomizer.java | 2 +- ...ngBootTestEmbeddedWebEnvironmentTests.java | 4 ++ ...BootTestTestRestTemplateDefinedByUser.java | 65 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestTestRestTemplateDefinedByUser.java diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextCustomizer.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextCustomizer.java index fe3c0b2607..c7517d0c0e 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextCustomizer.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextCustomizer.java @@ -62,7 +62,7 @@ class SpringBootTestContextCustomizer implements ContextCustomizer { private void registerTestRestTemplate(ConfigurableApplicationContext context, BeanDefinitionRegistry registry) { - registry.registerBeanDefinition("testRestTemplate", + registry.registerBeanDefinition(TestRestTemplate.class.getName(), new RootBeanDefinition(TestRestTemplateFactory.class)); } diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedWebEnvironmentTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedWebEnvironmentTests.java index 7617948e64..9fe2c8d60b 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedWebEnvironmentTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedWebEnvironmentTests.java @@ -60,6 +60,10 @@ public abstract class AbstractSpringBootTestEmbeddedWebEnvironmentTests { @Autowired private TestRestTemplate restTemplate; + public WebApplicationContext getContext() { + return this.context; + } + public TestRestTemplate getRestTemplate() { return this.restTemplate; } diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestTestRestTemplateDefinedByUser.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestTestRestTemplateDefinedByUser.java new file mode 100644 index 0000000000..2887b6061e --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestTestRestTemplateDefinedByUser.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.test.context; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link SpringBootTest} configured with {@link WebEnvironment#RANDOM_PORT}. + * + * @author Phillip Webb + * @author Andy Wilkinson + */ +@RunWith(SpringRunner.class) +@DirtiesContext +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "value=123" }) +public class SpringBootTestTestRestTemplateDefinedByUser + extends AbstractSpringBootTestEmbeddedWebEnvironmentTests { + + @Test + public void restTemplateIsUserDefined() throws Exception { + assertThat(getContext().getBean("testRestTemplate")) + .isInstanceOf(RestTemplate.class); + } + + // gh-7711 + + @Configuration + @EnableWebMvc + @RestController + protected static class Config extends AbstractConfig { + + @Bean + public RestTemplate testRestTemplate() { + return new RestTemplate(); + } + + } + +} From 2c00b4f76e2f227ce88becc948956479aa2bdf94 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 21 Dec 2016 11:20:14 -0800 Subject: [PATCH 03/12] Upgrade to Spring Data Hopper SR6 Closes gh-7602 --- spring-boot-dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 4bedde66fc..867b2fad8a 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -149,7 +149,7 @@ 1.6.6.RELEASE 1.2.3.RELEASE 3.0.7.RELEASE - Hopper-BUILD-SNAPSHOT + Hopper-SR6 0.20.0.RELEASE 4.3.6.RELEASE 1.1.4.RELEASE From 8ed769ae344535ca7f773b13bbc2cca4a75cbbd4 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Thu, 22 Dec 2016 02:39:57 +0900 Subject: [PATCH 04/12] Update to Spring Secuirty 4.1.4 Closes gh-7721 --- spring-boot-dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 867b2fad8a..74d59de114 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -158,7 +158,7 @@ 1.2.0.RELEASE 1.1.2.RELEASE 1.1.5.RELEASE - 4.1.3.RELEASE + 4.1.4.RELEASE 1.0.5.RELEASE 2.0.12.RELEASE 1.2.2.RELEASE From 4aac1e3f90578b08b9f9d3ee67bdb7a05b0f191e Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 21 Dec 2016 12:11:49 -0800 Subject: [PATCH 05/12] Refine LoggingApplicationListenerTests Update the Tomcat logging test to be more like the real scenario. See gh-7639 --- .../boot/logging/LoggingApplicationListenerTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java index 88ecce59ff..14b28c84a0 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java @@ -167,7 +167,7 @@ public class LoggingApplicationListenerTests { @Test public void tomcatNopLoggingConfigDoesNotCauseAFailure() throws Exception { TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, - "logging.config: -Dnop"); + "LOGGING_CONFIG: -Dnop"); this.initializer.initialize(this.context.getEnvironment(), this.context.getClassLoader()); this.logger.info("Hello world"); From e12b4a944f0de01be00ca70fc369739b8923d84d Mon Sep 17 00:00:00 2001 From: Johnny Lim Date: Wed, 21 Dec 2016 14:51:42 -0800 Subject: [PATCH 06/12] Polish --- .../autoconfigure/ManagementServerProperties.java | 2 +- .../mvc/JolokiaMvcEndpointIntegrationTests.java | 2 +- .../autoconfigure/security/SecurityProperties.java | 2 +- .../boot/loader/tools/MainClassFinder.java | 2 +- .../loader/AbstractExecutableArchiveLauncherTests.java | 10 +++++----- .../src/site/apt/examples/custom-layout.apt | 2 +- .../logging/logback/DefaultLogbackConfiguration.java | 8 ++++---- .../jpa/hibernate/SpringPhysicalNamingStrategy.java | 4 ++-- .../boot/web/support/SpringBootServletInitializer.java | 2 +- .../boot/yaml/SpringProfileDocumentMatcherTests.java | 3 +-- 10 files changed, 18 insertions(+), 19 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerProperties.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerProperties.java index f32b5a4b7e..6b87ecce63 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerProperties.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerProperties.java @@ -45,7 +45,7 @@ public class ManagementServerProperties implements SecurityPrerequisite { /** * Order applied to the WebSecurityConfigurerAdapter that is used to configure basic * authentication for management endpoints. If you want to add your own authentication - * for all or some of those endpoints the best thing to do is add your own + * for all or some of those endpoints the best thing to do is to add your own * WebSecurityConfigurerAdapter with lower order, for instance by using * {@code ACCESS_OVERRIDE_ORDER}. */ diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointIntegrationTests.java index 7b7c1726ae..ecd9c20f31 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpointIntegrationTests.java @@ -47,7 +47,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** - * Integration tests for {@link JolokiaMvcEndpoint} + * Integration tests for {@link JolokiaMvcEndpoint}. * * @author Christian Dupuis * @author Dave Syer diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java index aae7bc7590..36d99a3fe6 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java @@ -48,7 +48,7 @@ public class SecurityProperties implements SecurityPrerequisite { /** * Order applied to the WebSecurityConfigurerAdapter that is used to configure basic * authentication for application endpoints. If you want to add your own - * authentication for all or some of those endpoints the best thing to do is add your + * authentication for all or some of those endpoints the best thing to do is to add your * own WebSecurityConfigurerAdapter with lower order. */ public static final int BASIC_AUTH_ORDER = Ordered.LOWEST_PRECEDENCE - 5; diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/MainClassFinder.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/MainClassFinder.java index a2f71f8c59..cc2fee07d1 100644 --- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/MainClassFinder.java +++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/MainClassFinder.java @@ -372,7 +372,7 @@ public abstract class MainClassFinder { /** * Handle the specified main class. - * @param mainClass the mainClass + * @param mainClass the main class * @return a non-null value if processing should end or {@code null} to continue */ T doWith(MainClass mainClass); diff --git a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java index 1c34d93220..48a2f53a2b 100644 --- a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java +++ b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/AbstractExecutableArchiveLauncherTests.java @@ -55,15 +55,15 @@ public class AbstractExecutableArchiveLauncherTests { jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/")); jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/classes/")); jarOutputStream.putNextEntry(new JarEntry(entryPrefix + "/lib/")); - JarEntry webInfLibFoo = new JarEntry(entryPrefix + "/lib/foo.jar"); - webInfLibFoo.setMethod(ZipEntry.STORED); + JarEntry libFoo = new JarEntry(entryPrefix + "/lib/foo.jar"); + libFoo.setMethod(ZipEntry.STORED); ByteArrayOutputStream fooJarStream = new ByteArrayOutputStream(); new JarOutputStream(fooJarStream).close(); - webInfLibFoo.setSize(fooJarStream.size()); + libFoo.setSize(fooJarStream.size()); CRC32 crc32 = new CRC32(); crc32.update(fooJarStream.toByteArray()); - webInfLibFoo.setCrc(crc32.getValue()); - jarOutputStream.putNextEntry(webInfLibFoo); + libFoo.setCrc(crc32.getValue()); + jarOutputStream.putNextEntry(libFoo); jarOutputStream.write(fooJarStream.toByteArray()); jarOutputStream.close(); return archive; diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt index f4b5809286..8ed1d734cc 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt +++ b/spring-boot-tools/spring-boot-maven-plugin/src/site/apt/examples/custom-layout.apt @@ -47,7 +47,7 @@ ... -+--- +--- The layout factory is provided as an implementation of <<>> (from spring-boot-loader-tools) explicitly specified in the pom. If there is only one custom diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java index 514d77326d..85e5052edc 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java @@ -136,13 +136,13 @@ class DefaultLogbackConfiguration { appender.setEncoder(encoder); config.start(encoder); appender.setFile(logFile); - getRollingPolicy(appender, config, logFile); - getMaxFileSize(appender, config); + setRollingPolicy(appender, config, logFile); + setMaxFileSize(appender, config); config.appender("FILE", appender); return appender; } - private void getRollingPolicy(RollingFileAppender appender, + private void setRollingPolicy(RollingFileAppender appender, LogbackConfigurator config, String logFile) { FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy(); rollingPolicy.setFileNamePattern(logFile + ".%i"); @@ -151,7 +151,7 @@ class DefaultLogbackConfiguration { config.start(rollingPolicy); } - private void getMaxFileSize(RollingFileAppender appender, + private void setMaxFileSize(RollingFileAppender appender, LogbackConfigurator config) { SizeBasedTriggeringPolicy triggeringPolicy = new SizeBasedTriggeringPolicy(); try { diff --git a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/hibernate/SpringPhysicalNamingStrategy.java b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/hibernate/SpringPhysicalNamingStrategy.java index 070ccf3579..bbbcadc21a 100644 --- a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/hibernate/SpringPhysicalNamingStrategy.java +++ b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/hibernate/SpringPhysicalNamingStrategy.java @@ -77,7 +77,7 @@ public class SpringPhysicalNamingStrategy implements PhysicalNamingStrategy { } /** - * Get an the identifier for the specified details. By default his method will return + * Get an the identifier for the specified details. By default this method will return * an identifier with the name adapted based on the result of * {@link #isCaseInsensitive(JdbcEnvironment)} * @param name the name of the identifier @@ -95,7 +95,7 @@ public class SpringPhysicalNamingStrategy implements PhysicalNamingStrategy { /** * Specify whether the database is case sensitive. - * @param jdbcEnvironment The JDBC environment which can be used to determine case + * @param jdbcEnvironment the JDBC environment which can be used to determine case * @return true if the database is case insensitive sensitivity */ protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) { diff --git a/spring-boot/src/main/java/org/springframework/boot/web/support/SpringBootServletInitializer.java b/spring-boot/src/main/java/org/springframework/boot/web/support/SpringBootServletInitializer.java index 8b2f9d57a6..f57ebc3244 100644 --- a/spring-boot/src/main/java/org/springframework/boot/web/support/SpringBootServletInitializer.java +++ b/spring-boot/src/main/java/org/springframework/boot/web/support/SpringBootServletInitializer.java @@ -163,7 +163,7 @@ public abstract class SpringBootServletInitializer implements WebApplicationInit } /** - * Configure the application. Normally all you would need to do is add sources (e.g. + * Configure the application. Normally all you would need to do is to add sources (e.g. * config classes) because other settings have sensible defaults. You might choose * (for instance) to add default command line arguments, or set an active Spring * profile. diff --git a/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java b/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java index f5d5ebd295..4f867ee370 100644 --- a/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/yaml/SpringProfileDocumentMatcherTests.java @@ -67,8 +67,7 @@ public class SpringProfileDocumentMatcherTests { @Test public void matchesCommaSeparatedArray() throws IOException { DocumentMatcher matcher = new SpringProfileDocumentMatcher("foo", "bar"); - Properties properties = getProperties( - String.format("spring.profiles: [bar, spam]")); + Properties properties = getProperties("spring.profiles: [bar, spam]"); assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.FOUND); } From 28474aa30adebc397ecdb993d62317e5659fce4a Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Wed, 21 Dec 2016 14:58:07 -0800 Subject: [PATCH 07/12] Fix compatibility with Apache Kafka 0.10.1 Update KafkaProperties since Apache Kafka `0.10.1` changed the type for the `ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG` from the `Long` to `Integer`. Kafka includes the following conversion logic: case LONG: if (value instanceof Integer) return ((Integer) value).longValue(); if (value instanceof Long) return (Long) value; else if (value instanceof String) return Long.parseLong(trimmed); So we remain compatible with both `0.10.0` and `0.10.1` Closes gh-7723 --- .../boot/autoconfigure/kafka/KafkaProperties.java | 7 ++++--- .../autoconfigure/kafka/KafkaAutoConfigurationTests.java | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaProperties.java index d4e0774400..356c9fbcd5 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaProperties.java @@ -43,6 +43,7 @@ import org.springframework.util.CollectionUtils; * * @author Gary Russell * @author Stephane Nicoll + * @author Artem Bilan * @since 1.5.0 */ @ConfigurationProperties(prefix = "spring.kafka") @@ -199,7 +200,7 @@ public class KafkaProperties { * Frequency in milliseconds that the consumer offsets are auto-committed to Kafka * if 'enable.auto.commit' true. */ - private Long autoCommitInterval; + private Integer autoCommitInterval; /** * What to do when there is no initial offset in Kafka or if the current offset @@ -264,11 +265,11 @@ public class KafkaProperties { return this.ssl; } - public Long getAutoCommitInterval() { + public Integer getAutoCommitInterval() { return this.autoCommitInterval; } - public void setAutoCommitInterval(Long autoCommitInterval) { + public void setAutoCommitInterval(Integer autoCommitInterval) { this.autoCommitInterval = autoCommitInterval; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfigurationTests.java index dc27e38da6..5dbb753df6 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kafka/KafkaAutoConfigurationTests.java @@ -100,7 +100,7 @@ public class KafkaAutoConfigurationTests { assertThat(configs.get(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG)) .isEqualTo(Boolean.FALSE); assertThat(configs.get(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG)) - .isEqualTo(123L); + .isEqualTo(123); assertThat(configs.get(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG)) .isEqualTo("earliest"); assertThat(configs.get(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG)).isEqualTo(456); From c700cf28cce44c33ed38de5012f271d349f788bc Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Wed, 21 Dec 2016 15:00:39 -0800 Subject: [PATCH 08/12] Fix typo in Kafka sample --- .../kafka/KafkaSpecialProducerConsumerConfigExample.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spring-boot-docs/src/main/java/org/springframework/boot/kafka/KafkaSpecialProducerConsumerConfigExample.java b/spring-boot-docs/src/main/java/org/springframework/boot/kafka/KafkaSpecialProducerConsumerConfigExample.java index 23a7aa139d..93c1f84b89 100644 --- a/spring-boot-docs/src/main/java/org/springframework/boot/kafka/KafkaSpecialProducerConsumerConfigExample.java +++ b/spring-boot-docs/src/main/java/org/springframework/boot/kafka/KafkaSpecialProducerConsumerConfigExample.java @@ -64,11 +64,10 @@ public class KafkaSpecialProducerConsumerConfigExample { */ @Bean public ConsumerFactory kafkaConsumerFactory(KafkaProperties properties) { - Map consumererProperties = properties - .buildConsumerProperties(); - consumererProperties.put(CommonClientConfigs.METRIC_REPORTER_CLASSES_CONFIG, + Map consumerProperties = properties.buildConsumerProperties(); + consumerProperties.put(CommonClientConfigs.METRIC_REPORTER_CLASSES_CONFIG, MyConsumerMetricsReporter.class); - return new DefaultKafkaConsumerFactory(consumererProperties); + return new DefaultKafkaConsumerFactory(consumerProperties); } } From d69e43b43303c67f0f7c1a3a482e182adbe0218a Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Wed, 21 Dec 2016 15:13:59 -0500 Subject: [PATCH 09/12] Refactor Spring Integration metrics support Update Spring Integration metrics support since Spring Integration `4.3.6`+ no longer needs `spring-integration-jmx` enable `MessageChannel`, `MessageHandler` and `MessageSource` metrics. - Add `IntegrationManagementConfiguration` conditional auto-configuration to provide `@EnableIntegrationManagement` when JMX is `enabled` or there is no `IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME` bean. By default this bean doesn't exist and you explicitly should declare it (e.g. via `@EnableIntegrationManagement`) if you would like to collect metrics. At the same time Spring Integration enables it when JMX management is present (that is a purpose of that new `IntegrationManagementConfiguration`) - Change `SpringIntegrationMetricReader` to read metrics from the `IntegrationManagementConfigurer`, not `IntegrationMBeanExporter` - Change `PublicMetricsAutoConfiguration` to register `IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME` bean if not present. Since we are here in `actuator`, therefore we are interested in the metrics for SI as well. - Since we don't need JMX for the metrics any more, remove SI-JMX dependency from the `spring-boot-starter-integration`. - Remove `IntegrationManagementConfiguration` modification from the `integrationMbeanExporter()`, since that looks like mutation of an external object, when end-user would prefer their own options. Therefore we don't need `ObjectProvider`, too - Add missed `MessageSourceMetrics` gathering for the `SpringIntegrationMetricReader` Closes gh-7722 --- spring-boot-actuator/pom.xml | 10 +-- .../PublicMetricsAutoConfiguration.java | 23 +++++-- .../SpringIntegrationMetricReader.java | 69 +++++++++++-------- ...ringIntegrationMetricReaderNoJmxTests.java | 61 ++++++++++++++++ .../SpringIntegrationMetricReaderTests.java | 7 +- .../IntegrationAutoConfiguration.java | 34 ++++----- .../IntegrationAutoConfigurationTests.java | 3 + .../spring-boot-starter-integration/pom.xml | 4 -- .../main/resources/META-INF/spring.provides | 2 +- 9 files changed, 151 insertions(+), 62 deletions(-) create mode 100644 spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index 75fa518e09..fd1b429244 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -193,11 +193,6 @@ - - org.springframework.integration - spring-integration-jmx - true - org.springframework.integration spring-integration-core @@ -373,5 +368,10 @@ snakeyaml test + + org.springframework.integration + spring-integration-jmx + test + diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java index 2aa3aa126c..387103fa14 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java @@ -46,13 +46,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnJava; import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider; import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.integration.monitor.IntegrationMBeanExporter; +import org.springframework.integration.config.EnableIntegrationManagement; +import org.springframework.integration.support.management.IntegrationManagementConfigurer; import org.springframework.lang.UsesJava7; /** @@ -61,6 +63,7 @@ import org.springframework.lang.UsesJava7; * @author Stephane Nicoll * @author Phillip Webb * @author Johannes Edmeier + * @author Artem Bilan * @since 1.2.0 */ @Configuration @@ -139,18 +142,28 @@ public class PublicMetricsAutoConfiguration { } @Configuration - @ConditionalOnClass(IntegrationMBeanExporter.class) - @ConditionalOnBean(IntegrationMBeanExporter.class) + @ConditionalOnClass(EnableIntegrationManagement.class) @ConditionalOnJava(JavaVersion.SEVEN) @UsesJava7 static class IntegrationMetricsConfiguration { + @Bean(name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME) + @ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, + name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, + search = SearchStrategy.CURRENT) + public IntegrationManagementConfigurer managementConfigurer() { + IntegrationManagementConfigurer configurer = new IntegrationManagementConfigurer(); + configurer.setDefaultCountsEnabled(true); + configurer.setDefaultStatsEnabled(true); + return configurer; + } + @Bean @ConditionalOnMissingBean(name = "springIntegrationPublicMetrics") public MetricReaderPublicMetrics springIntegrationPublicMetrics( - IntegrationMBeanExporter exporter) { + IntegrationManagementConfigurer managementConfigurer) { return new MetricReaderPublicMetrics( - new SpringIntegrationMetricReader(exporter)); + new SpringIntegrationMetricReader(managementConfigurer)); } } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java index 459a67bafa..aa1ca45f07 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java @@ -22,24 +22,29 @@ import java.util.List; import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.integration.monitor.IntegrationMBeanExporter; +import org.springframework.integration.support.management.IntegrationManagementConfigurer; +import org.springframework.integration.support.management.MessageChannelMetrics; +import org.springframework.integration.support.management.MessageHandlerMetrics; +import org.springframework.integration.support.management.MessageSourceMetrics; +import org.springframework.integration.support.management.PollableChannelManagement; import org.springframework.integration.support.management.Statistics; import org.springframework.lang.UsesJava7; /** * A {@link MetricReader} for Spring Integration metrics (as provided by - * spring-integration-jmx). + * {@link IntegrationManagementConfigurer}). * * @author Dave Syer + * @author Artem Bilan * @since 1.3.0 */ @UsesJava7 public class SpringIntegrationMetricReader implements MetricReader { - private final IntegrationMBeanExporter exporter; + private final IntegrationManagementConfigurer managementConfigurer; - public SpringIntegrationMetricReader(IntegrationMBeanExporter exporter) { - this.exporter = exporter; + public SpringIntegrationMetricReader(IntegrationManagementConfigurer managementConfigurer) { + this.managementConfigurer = managementConfigurer; } @Override @@ -49,31 +54,40 @@ public class SpringIntegrationMetricReader implements MetricReader { @Override public Iterable> findAll() { - IntegrationMBeanExporter exporter = this.exporter; List> metrics = new ArrayList>(); - for (String name : exporter.getChannelNames()) { + + for (String name : this.managementConfigurer.getChannelNames()) { + MessageChannelMetrics channelMetrics = this.managementConfigurer.getChannelMetrics(name); String prefix = "integration.channel." + name; - metrics.addAll(getStatistics(prefix + ".errorRate", - exporter.getChannelErrorRate(name))); - metrics.add(new Metric(prefix + ".sendCount", - exporter.getChannelSendCountLong(name))); - metrics.addAll(getStatistics(prefix + ".sendRate", - exporter.getChannelSendRate(name))); - metrics.add(new Metric(prefix + ".receiveCount", - exporter.getChannelReceiveCountLong(name))); + metrics.addAll(getStatistics(prefix + ".errorRate", channelMetrics.getErrorRate())); + metrics.add(new Metric(prefix + ".sendCount", channelMetrics.getSendCountLong())); + metrics.addAll(getStatistics(prefix + ".sendRate", channelMetrics.getSendRate())); + if (channelMetrics instanceof PollableChannelManagement) { + metrics.add(new Metric(prefix + ".receiveCount", + ((PollableChannelManagement) channelMetrics).getReceiveCountLong())); + } } - for (String name : exporter.getHandlerNames()) { - metrics.addAll(getStatistics("integration.handler." + name + ".duration", - exporter.getHandlerDuration(name))); + + for (String name : this.managementConfigurer.getHandlerNames()) { + MessageHandlerMetrics handlerMetrics = this.managementConfigurer.getHandlerMetrics(name); + String prefix = "integration.handler." + name; + metrics.addAll(getStatistics(prefix + ".duration", handlerMetrics.getDuration())); + metrics.add(new Metric(prefix + ".activeCount", handlerMetrics.getActiveCountLong())); } - metrics.add(new Metric("integration.activeHandlerCount", - exporter.getActiveHandlerCount())); + + for (String name : this.managementConfigurer.getSourceNames()) { + MessageSourceMetrics sourceMetrics = this.managementConfigurer.getSourceMetrics(name); + String prefix = "integration.source." + name; + metrics.add(new Metric(prefix + ".messageCount", sourceMetrics.getMessageCountLong())); + } + metrics.add(new Metric("integration.handlerCount", - exporter.getHandlerCount())); + this.managementConfigurer.getHandlerNames().length)); metrics.add(new Metric("integration.channelCount", - exporter.getChannelCount())); - metrics.add(new Metric("integration.queuedMessageCount", - exporter.getQueuedMessageCount())); + this.managementConfigurer.getChannelNames().length)); + metrics.add(new Metric("integration.sourceCount", + this.managementConfigurer.getSourceNames().length)); + return metrics; } @@ -91,9 +105,10 @@ public class SpringIntegrationMetricReader implements MetricReader { @Override public long count() { - int totalChannelCount = this.exporter.getChannelCount() * 11; - int totalHandlerCount = this.exporter.getHandlerCount() * 5; - return totalChannelCount + totalHandlerCount + 4; + int totalChannelCount = this.managementConfigurer.getChannelNames().length; + int totalHandlerCount = this.managementConfigurer.getHandlerNames().length; + int totalSourceCount = this.managementConfigurer.getSourceNames().length; + return totalChannelCount + totalHandlerCount + totalSourceCount; } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java new file mode 100644 index 0000000000..69aa6ad3fa --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.actuate.metrics.integration; + + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration; +import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics; +import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link SpringIntegrationMetricReader}. + * + * @author Artem Bilan + */ +@RunWith(SpringRunner.class) +@SpringBootTest("spring.jmx.enabled=false") +@DirtiesContext +public class SpringIntegrationMetricReaderNoJmxTests { + + @Autowired + @Qualifier("springIntegrationPublicMetrics") + private MetricReaderPublicMetrics integrationMetricReader; + + @Test + public void test() { + assertThat(this.integrationMetricReader.metrics().size() > 0).isTrue(); + } + + @Configuration + @Import({IntegrationAutoConfiguration.class, PublicMetricsAutoConfiguration.class}) + protected static class TestConfiguration { + + } + +} diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java index 9f4ba580ac..8353e77061 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java @@ -26,7 +26,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.integration.monitor.IntegrationMBeanExporter; +import org.springframework.integration.support.management.IntegrationManagementConfigurer; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; @@ -36,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link SpringIntegrationMetricReader}. * * @author Dave Syer + * @author Artem Bilan */ @RunWith(SpringRunner.class) @SpringBootTest("spring.jmx.enabled=true") @@ -55,8 +56,8 @@ public class SpringIntegrationMetricReaderTests { protected static class TestConfiguration { @Bean - public SpringIntegrationMetricReader reader(IntegrationMBeanExporter exporter) { - return new SpringIntegrationMetricReader(exporter); + public SpringIntegrationMetricReader reader(IntegrationManagementConfigurer managementConfigurer) { + return new SpringIntegrationMetricReader(managementConfigurer); } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java index 1f280dc9cf..7900d7144f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java @@ -21,8 +21,6 @@ import javax.management.MBeanServer; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -35,6 +33,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.integration.config.EnableIntegration; +import org.springframework.integration.config.EnableIntegrationManagement; import org.springframework.integration.jmx.config.EnableIntegrationMBeanExport; import org.springframework.integration.monitor.IntegrationMBeanExporter; import org.springframework.integration.support.management.IntegrationManagementConfigurer; @@ -67,17 +66,10 @@ public class IntegrationAutoConfiguration { protected static class IntegrationJmxConfiguration implements EnvironmentAware, BeanFactoryAware { - private final IntegrationManagementConfigurer configurer; - private BeanFactory beanFactory; private RelaxedPropertyResolver propertyResolver; - protected IntegrationJmxConfiguration( - @Qualifier(IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME) ObjectProvider configurerProvider) { - this.configurer = configurerProvider.getIfAvailable(); - } - @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; @@ -100,17 +92,25 @@ public class IntegrationAutoConfiguration { if (StringUtils.hasLength(server)) { exporter.setServer(this.beanFactory.getBean(server, MBeanServer.class)); } - if (this.configurer != null) { - if (this.configurer.getDefaultCountsEnabled() == null) { - this.configurer.setDefaultCountsEnabled(true); - } - if (this.configurer.getDefaultStatsEnabled() == null) { - this.configurer.setDefaultStatsEnabled(true); - } - } return exporter; } } + @Configuration + @ConditionalOnClass({EnableIntegrationManagement.class, EnableIntegrationMBeanExport.class}) + @ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, + name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, + search = SearchStrategy.CURRENT) + @ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true", matchIfMissing = true) + protected static class IntegrationManagementConfiguration { + + @Configuration + @EnableIntegrationManagement(defaultCountsEnabled = "true", defaultStatsEnabled = "true") + protected static class EnableIntegrationManagementConfiguration { + + } + + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java index c5932505bb..027fff7c40 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java @@ -31,6 +31,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.integration.support.channel.HeaderChannelRegistry; +import org.springframework.integration.support.management.IntegrationManagementConfigurer; import org.springframework.jmx.export.MBeanExporter; import org.springframework.test.context.support.TestPropertySourceUtils; @@ -86,12 +87,14 @@ public class IntegrationAutoConfigurationTests { MBeanServer mBeanServer = this.context.getBean(MBeanServer.class); assertDomains(mBeanServer, true, "org.springframework.integration", "org.springframework.integration.monitor"); + assertThat(this.context.getBean(IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME)).isNotNull(); } @Test public void disableJmxIntegration() { load("spring.jmx.enabled=false"); assertThat(this.context.getBeansOfType(MBeanServer.class)).hasSize(0); + assertThat(this.context.getBeansOfType(IntegrationManagementConfigurer.class)).isEmpty(); } @Test diff --git a/spring-boot-starters/spring-boot-starter-integration/pom.xml b/spring-boot-starters/spring-boot-starter-integration/pom.xml index 5a2a1219b3..18871ee4fa 100644 --- a/spring-boot-starters/spring-boot-starter-integration/pom.xml +++ b/spring-boot-starters/spring-boot-starter-integration/pom.xml @@ -34,9 +34,5 @@ org.springframework.integration spring-integration-java-dsl - - org.springframework.integration - spring-integration-jmx - diff --git a/spring-boot-starters/spring-boot-starter-integration/src/main/resources/META-INF/spring.provides b/spring-boot-starters/spring-boot-starter-integration/src/main/resources/META-INF/spring.provides index 66fb5df913..63514baf3b 100644 --- a/spring-boot-starters/spring-boot-starter-integration/src/main/resources/META-INF/spring.provides +++ b/spring-boot-starters/spring-boot-starter-integration/src/main/resources/META-INF/spring.provides @@ -1 +1 @@ -provides: spring-integration-core,spring-integration-java-dsl,spring-integration-jmx \ No newline at end of file +provides: spring-integration-core,spring-integration-java-dsl From b87e02dde0573e1b5d6c98a3aa3b002486af0113 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 21 Dec 2016 15:34:46 -0800 Subject: [PATCH 10/12] Polish Spring Integration metrics support See gh-7722 --- spring-boot-actuator/pom.xml | 10 +- .../PublicMetricsAutoConfiguration.java | 4 +- .../SpringIntegrationMetricReader.java | 117 ++++++++++-------- .../PublicMetricsAutoConfigurationTests.java | 2 +- ...ringIntegrationMetricReaderNoJmxTests.java | 3 +- .../SpringIntegrationMetricReaderTests.java | 3 +- .../IntegrationAutoConfiguration.java | 7 +- .../IntegrationAutoConfigurationTests.java | 7 +- 8 files changed, 86 insertions(+), 67 deletions(-) diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index fd1b429244..bcb23ce313 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -358,6 +358,11 @@ spring-data-rest-webmvc test + + org.springframework.integration + spring-integration-jmx + test + org.springframework.security spring-security-test @@ -368,10 +373,5 @@ snakeyaml test - - org.springframework.integration - spring-integration-jmx - test - diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java index 387103fa14..27526cc4ad 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java @@ -148,9 +148,7 @@ public class PublicMetricsAutoConfiguration { static class IntegrationMetricsConfiguration { @Bean(name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME) - @ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, - name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, - search = SearchStrategy.CURRENT) + @ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, search = SearchStrategy.CURRENT) public IntegrationManagementConfigurer managementConfigurer() { IntegrationManagementConfigurer configurer = new IntegrationManagementConfigurer(); configurer.setDefaultCountsEnabled(true); diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java index aa1ca45f07..9ce8776730 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReader.java @@ -41,10 +41,10 @@ import org.springframework.lang.UsesJava7; @UsesJava7 public class SpringIntegrationMetricReader implements MetricReader { - private final IntegrationManagementConfigurer managementConfigurer; + private final IntegrationManagementConfigurer configurer; - public SpringIntegrationMetricReader(IntegrationManagementConfigurer managementConfigurer) { - this.managementConfigurer = managementConfigurer; + public SpringIntegrationMetricReader(IntegrationManagementConfigurer configurer) { + this.configurer = configurer; } @Override @@ -54,60 +54,79 @@ public class SpringIntegrationMetricReader implements MetricReader { @Override public Iterable> findAll() { - List> metrics = new ArrayList>(); - - for (String name : this.managementConfigurer.getChannelNames()) { - MessageChannelMetrics channelMetrics = this.managementConfigurer.getChannelMetrics(name); - String prefix = "integration.channel." + name; - metrics.addAll(getStatistics(prefix + ".errorRate", channelMetrics.getErrorRate())); - metrics.add(new Metric(prefix + ".sendCount", channelMetrics.getSendCountLong())); - metrics.addAll(getStatistics(prefix + ".sendRate", channelMetrics.getSendRate())); - if (channelMetrics instanceof PollableChannelManagement) { - metrics.add(new Metric(prefix + ".receiveCount", - ((PollableChannelManagement) channelMetrics).getReceiveCountLong())); - } - } - - for (String name : this.managementConfigurer.getHandlerNames()) { - MessageHandlerMetrics handlerMetrics = this.managementConfigurer.getHandlerMetrics(name); - String prefix = "integration.handler." + name; - metrics.addAll(getStatistics(prefix + ".duration", handlerMetrics.getDuration())); - metrics.add(new Metric(prefix + ".activeCount", handlerMetrics.getActiveCountLong())); - } - - for (String name : this.managementConfigurer.getSourceNames()) { - MessageSourceMetrics sourceMetrics = this.managementConfigurer.getSourceMetrics(name); - String prefix = "integration.source." + name; - metrics.add(new Metric(prefix + ".messageCount", sourceMetrics.getMessageCountLong())); - } - - metrics.add(new Metric("integration.handlerCount", - this.managementConfigurer.getHandlerNames().length)); - metrics.add(new Metric("integration.channelCount", - this.managementConfigurer.getChannelNames().length)); - metrics.add(new Metric("integration.sourceCount", - this.managementConfigurer.getSourceNames().length)); - - return metrics; + List> result = new ArrayList>(); + String[] channelNames = this.configurer.getChannelNames(); + String[] handlerNames = this.configurer.getHandlerNames(); + String[] sourceNames = this.configurer.getSourceNames(); + addChannelMetrics(result, channelNames); + addHandlerMetrics(result, handlerNames); + addSourceMetrics(result, sourceNames); + result.add(new Metric("integration.handlerCount", handlerNames.length)); + result.add(new Metric("integration.channelCount", channelNames.length)); + result.add(new Metric("integration.sourceCount", sourceNames.length)); + return result; } - private Collection> getStatistics(String name, - Statistics statistic) { + private void addChannelMetrics(List> result, String[] names) { + for (String name : names) { + addChannelMetrics(result, name, this.configurer.getChannelMetrics(name)); + } + } + + private void addChannelMetrics(List> result, String name, + MessageChannelMetrics metrics) { + String prefix = "integration.channel." + name; + result.addAll(getStatistics(prefix + ".errorRate", metrics.getErrorRate())); + result.add(new Metric(prefix + ".sendCount", metrics.getSendCountLong())); + result.addAll(getStatistics(prefix + ".sendRate", metrics.getSendRate())); + if (metrics instanceof PollableChannelManagement) { + result.add(new Metric(prefix + ".receiveCount", + ((PollableChannelManagement) metrics).getReceiveCountLong())); + } + } + + private void addHandlerMetrics(List> result, String[] names) { + for (String name : names) { + addHandlerMetrics(result, name, this.configurer.getHandlerMetrics(name)); + } + } + + private void addHandlerMetrics(List> result, String name, + MessageHandlerMetrics metrics) { + String prefix = "integration.handler." + name; + result.addAll(getStatistics(prefix + ".duration", metrics.getDuration())); + long activeCount = metrics.getActiveCountLong(); + result.add(new Metric(prefix + ".activeCount", activeCount)); + } + + private void addSourceMetrics(List> result, String[] names) { + for (String name : names) { + addSourceMetrics(result, name, this.configurer.getSourceMetrics(name)); + } + } + + private void addSourceMetrics(List> result, String name, + MessageSourceMetrics sourceMetrics) { + String prefix = "integration.source." + name; + result.add(new Metric(prefix + ".messageCount", + sourceMetrics.getMessageCountLong())); + } + + private Collection> getStatistics(String name, Statistics stats) { List> metrics = new ArrayList>(); - metrics.add(new Metric(name + ".mean", statistic.getMean())); - metrics.add(new Metric(name + ".max", statistic.getMax())); - metrics.add(new Metric(name + ".min", statistic.getMin())); - metrics.add( - new Metric(name + ".stdev", statistic.getStandardDeviation())); - metrics.add(new Metric(name + ".count", statistic.getCountLong())); + metrics.add(new Metric(name + ".mean", stats.getMean())); + metrics.add(new Metric(name + ".max", stats.getMax())); + metrics.add(new Metric(name + ".min", stats.getMin())); + metrics.add(new Metric(name + ".stdev", stats.getStandardDeviation())); + metrics.add(new Metric(name + ".count", stats.getCountLong())); return metrics; } @Override public long count() { - int totalChannelCount = this.managementConfigurer.getChannelNames().length; - int totalHandlerCount = this.managementConfigurer.getHandlerNames().length; - int totalSourceCount = this.managementConfigurer.getSourceNames().length; + int totalChannelCount = this.configurer.getChannelNames().length; + int totalHandlerCount = this.configurer.getHandlerNames().length; + int totalSourceCount = this.configurer.getSourceNames().length; return totalChannelCount + totalHandlerCount + totalSourceCount; } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java index 3027b7759f..27c2f4dd14 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java @@ -92,7 +92,7 @@ public class PublicMetricsAutoConfigurationTests { public void metricReaderPublicMetrics() throws Exception { load(); assertThat(this.context.getBeansOfType(MetricReaderPublicMetrics.class)) - .hasSize(1); + .hasSize(2); } @Test diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java index 69aa6ad3fa..18efb7f359 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java @@ -16,7 +16,6 @@ package org.springframework.boot.actuate.metrics.integration; - import org.junit.Test; import org.junit.runner.RunWith; @@ -53,7 +52,7 @@ public class SpringIntegrationMetricReaderNoJmxTests { } @Configuration - @Import({IntegrationAutoConfiguration.class, PublicMetricsAutoConfiguration.class}) + @Import({ IntegrationAutoConfiguration.class, PublicMetricsAutoConfiguration.class }) protected static class TestConfiguration { } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java index 8353e77061..f8a01b0903 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderTests.java @@ -56,7 +56,8 @@ public class SpringIntegrationMetricReaderTests { protected static class TestConfiguration { @Bean - public SpringIntegrationMetricReader reader(IntegrationManagementConfigurer managementConfigurer) { + public SpringIntegrationMetricReader reader( + IntegrationManagementConfigurer managementConfigurer) { return new SpringIntegrationMetricReader(managementConfigurer); } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java index 7900d7144f..e5e61e58d7 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java @@ -98,10 +98,9 @@ public class IntegrationAutoConfiguration { } @Configuration - @ConditionalOnClass({EnableIntegrationManagement.class, EnableIntegrationMBeanExport.class}) - @ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, - name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, - search = SearchStrategy.CURRENT) + @ConditionalOnClass({ EnableIntegrationManagement.class, + EnableIntegrationMBeanExport.class }) + @ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, search = SearchStrategy.CURRENT) @ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true", matchIfMissing = true) protected static class IntegrationManagementConfiguration { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java index 027fff7c40..c42df154ae 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java @@ -87,14 +87,17 @@ public class IntegrationAutoConfigurationTests { MBeanServer mBeanServer = this.context.getBean(MBeanServer.class); assertDomains(mBeanServer, true, "org.springframework.integration", "org.springframework.integration.monitor"); - assertThat(this.context.getBean(IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME)).isNotNull(); + Object bean = this.context + .getBean(IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME); + assertThat(bean).isNotNull(); } @Test public void disableJmxIntegration() { load("spring.jmx.enabled=false"); assertThat(this.context.getBeansOfType(MBeanServer.class)).hasSize(0); - assertThat(this.context.getBeansOfType(IntegrationManagementConfigurer.class)).isEmpty(); + assertThat(this.context.getBeansOfType(IntegrationManagementConfigurer.class)) + .isEmpty(); } @Test From a79f71cbe802a8d87bc22e6eba67553ccedd54a7 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Wed, 21 Dec 2016 09:28:08 -0500 Subject: [PATCH 11/12] Add @IntegrationComponentScan auto-configuration Update Spring Integration auto-configuration so that `@IntegrationComponentScan` from `AutoConfigurationPackages` is implicitly applied. Prior to this commit `@MessagingGateway` interfaces would only get picked up if `@IntegrationComponentScan` was added alongside with the `@SpringBootApplication`. Fixes gh-2037 Closes gh-7718 --- .../IntegrationAutoConfiguration.java | 50 +++++++++++++++++++ .../IntegrationAutoConfigurationTests.java | 38 +++++++++++--- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java index e5e61e58d7..5334bc3395 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java @@ -16,11 +16,15 @@ package org.springframework.boot.autoconfigure.integration; +import java.util.Map; + import javax.management.MBeanServer; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -31,9 +35,15 @@ import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.integration.annotation.IntegrationComponentScan; import org.springframework.integration.config.EnableIntegration; import org.springframework.integration.config.EnableIntegrationManagement; +import org.springframework.integration.config.IntegrationComponentScanRegistrar; +import org.springframework.integration.gateway.GatewayProxyFactoryBean; import org.springframework.integration.jmx.config.EnableIntegrationMBeanExport; import org.springframework.integration.monitor.IntegrationMBeanExporter; import org.springframework.integration.support.management.IntegrationManagementConfigurer; @@ -107,6 +117,46 @@ public class IntegrationAutoConfiguration { @Configuration @EnableIntegrationManagement(defaultCountsEnabled = "true", defaultStatsEnabled = "true") protected static class EnableIntegrationManagementConfiguration { + } + + } + + @ConditionalOnMissingBean(GatewayProxyFactoryBean.class) + @Import(AutoIntegrationComponentScanRegistrar.class) + protected static class IntegrationComponentScanAutoConfiguration { + + } + + private static class AutoIntegrationComponentScanRegistrar + extends IntegrationComponentScanRegistrar { + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + final BeanDefinitionRegistry registry) { + StandardAnnotationMetadata metadata = new StandardAnnotationMetadata( + IntegrationComponentScanConfiguration.class, true) { + + @Override + public Map getAnnotationAttributes( + String annotationName) { + Map annotationAttributes = super.getAnnotationAttributes( + annotationName); + if (IntegrationComponentScan.class.getName().equals(annotationName)) { + BeanFactory beanFactory = (BeanFactory) registry; + if (AutoConfigurationPackages.has(beanFactory)) { + annotationAttributes.put("value", + AutoConfigurationPackages.get(beanFactory)); + } + } + return annotationAttributes; + } + + }; + super.registerBeanDefinitions(metadata, registry); + } + + @IntegrationComponentScan + private class IntegrationComponentScanConfiguration { } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java index c42df154ae..a445add2e1 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java @@ -30,6 +30,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import org.springframework.integration.annotation.IntegrationComponentScan; +import org.springframework.integration.annotation.MessagingGateway; +import org.springframework.integration.gateway.RequestReplyExchanger; import org.springframework.integration.support.channel.HeaderChannelRegistry; import org.springframework.integration.support.management.IntegrationManagementConfigurer; import org.springframework.jmx.export.MBeanExporter; @@ -61,24 +64,34 @@ public class IntegrationAutoConfigurationTests { @Test public void integrationIsAvailable() { load(); - assertThat(this.context.getBean(HeaderChannelRegistry.class)).isNotNull(); + assertThat(this.context.getBean(TestGateway.class)).isNotNull(); + assertThat(this.context.getBean(IntegrationAutoConfiguration.IntegrationComponentScanAutoConfiguration.class)) + .isNotNull(); + } + + @Test + public void explicitIntegrationComponentScan() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(IntegrationComponentScanConfiguration.class, + JmxAutoConfiguration.class, + IntegrationAutoConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBean(TestGateway.class)).isNotNull(); + assertThat(this.context.getBeansOfType(IntegrationAutoConfiguration.IntegrationComponentScanAutoConfiguration.class)) + .isEmpty(); } @Test public void parentContext() { - this.context = new AnnotationConfigApplicationContext(); - this.context.register(JmxAutoConfiguration.class, - IntegrationAutoConfiguration.class); - this.context.refresh(); + load(); AnnotationConfigApplicationContext parent = this.context; this.context = new AnnotationConfigApplicationContext(); this.context.setParent(parent); this.context.register(JmxAutoConfiguration.class, IntegrationAutoConfiguration.class); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, "SPRING_JMX_DEFAULT_DOMAIN=org.foo"); this.context.refresh(); assertThat(this.context.getBean(HeaderChannelRegistry.class)).isNotNull(); - ((ConfigurableApplicationContext) this.context.getParent()).close(); - this.context.close(); } @Test @@ -151,4 +164,15 @@ public class IntegrationAutoConfigurationTests { } + @Configuration + @IntegrationComponentScan + static class IntegrationComponentScanConfiguration { + + } + + @MessagingGateway + public interface TestGateway extends RequestReplyExchanger { + + } + } From 982f41b70c88784ada042362f5463cd6abf289d1 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 21 Dec 2016 20:06:36 -0800 Subject: [PATCH 12/12] Polish @IntegrationComponentScan auto-configuration See gh-2037 See gh-7718 --- .../IntegrationAutoConfiguration.java | 57 +++--------- ...grationAutoConfigurationScanRegistrar.java | 87 +++++++++++++++++++ .../IntegrationAutoConfigurationTests.java | 14 +-- .../integration/SampleCommandLineRunner.java | 38 ++++++++ .../integration/SampleMessageGateway.java | 26 ++++++ .../SampleIntegrationApplicationTests.java | 34 ++++---- 6 files changed, 191 insertions(+), 65 deletions(-) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationScanRegistrar.java create mode 100644 spring-boot-samples/spring-boot-sample-integration/src/main/java/sample/integration/SampleCommandLineRunner.java create mode 100644 spring-boot-samples/spring-boot-sample-integration/src/main/java/sample/integration/SampleMessageGateway.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java index 5334bc3395..0241676b84 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java @@ -16,15 +16,11 @@ package org.springframework.boot.autoconfigure.integration; -import java.util.Map; - import javax.management.MBeanServer; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -37,12 +33,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.env.Environment; -import org.springframework.core.type.AnnotationMetadata; -import org.springframework.core.type.StandardAnnotationMetadata; -import org.springframework.integration.annotation.IntegrationComponentScan; import org.springframework.integration.config.EnableIntegration; import org.springframework.integration.config.EnableIntegrationManagement; -import org.springframework.integration.config.IntegrationComponentScanRegistrar; import org.springframework.integration.gateway.GatewayProxyFactoryBean; import org.springframework.integration.jmx.config.EnableIntegrationMBeanExport; import org.springframework.integration.monitor.IntegrationMBeanExporter; @@ -63,12 +55,18 @@ import org.springframework.util.StringUtils; @AutoConfigureAfter(JmxAutoConfiguration.class) public class IntegrationAutoConfiguration { + /** + * Basic Spring Integration configuration. + */ @Configuration @EnableIntegration protected static class IntegrationConfiguration { } + /** + * Spring Integration JMX configuration. + */ @Configuration @ConditionalOnClass(EnableIntegrationMBeanExport.class) @ConditionalOnMissingBean(value = IntegrationMBeanExporter.class, search = SearchStrategy.CURRENT) @@ -107,6 +105,9 @@ public class IntegrationAutoConfiguration { } + /** + * Integration management configuration. + */ @Configuration @ConditionalOnClass({ EnableIntegrationManagement.class, EnableIntegrationMBeanExport.class }) @@ -121,45 +122,13 @@ public class IntegrationAutoConfiguration { } + /** + * Integration component scan configuration. + */ @ConditionalOnMissingBean(GatewayProxyFactoryBean.class) - @Import(AutoIntegrationComponentScanRegistrar.class) + @Import(IntegrationAutoConfigurationScanRegistrar.class) protected static class IntegrationComponentScanAutoConfiguration { } - private static class AutoIntegrationComponentScanRegistrar - extends IntegrationComponentScanRegistrar { - - @Override - public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, - final BeanDefinitionRegistry registry) { - StandardAnnotationMetadata metadata = new StandardAnnotationMetadata( - IntegrationComponentScanConfiguration.class, true) { - - @Override - public Map getAnnotationAttributes( - String annotationName) { - Map annotationAttributes = super.getAnnotationAttributes( - annotationName); - if (IntegrationComponentScan.class.getName().equals(annotationName)) { - BeanFactory beanFactory = (BeanFactory) registry; - if (AutoConfigurationPackages.has(beanFactory)) { - annotationAttributes.put("value", - AutoConfigurationPackages.get(beanFactory)); - } - } - return annotationAttributes; - } - - }; - super.registerBeanDefinitions(metadata, registry); - } - - @IntegrationComponentScan - private class IntegrationComponentScanConfiguration { - - } - - } - } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationScanRegistrar.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationScanRegistrar.java new file mode 100644 index 0000000000..87f2653b60 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationScanRegistrar.java @@ -0,0 +1,87 @@ +/* + * Copyright 2012-2016 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 + * + * http://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.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.boot.autoconfigure.AutoConfigurationPackages; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.integration.annotation.IntegrationComponentScan; +import org.springframework.integration.config.IntegrationComponentScanRegistrar; + +/** + * Variation of {@link IntegrationComponentScanRegistrar} the links + * {@link AutoConfigurationPackages}. + * + * @author Artem Bilan + * @author Phillip Webb + */ +class IntegrationAutoConfigurationScanRegistrar extends IntegrationComponentScanRegistrar + implements BeanFactoryAware { + + private BeanFactory beanFactory; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + final BeanDefinitionRegistry registry) { + super.registerBeanDefinitions( + new IntegrationComponentScanConfigurationMetaData(this.beanFactory), + registry); + } + + private static class IntegrationComponentScanConfigurationMetaData + extends StandardAnnotationMetadata { + + private final BeanFactory beanFactory; + + IntegrationComponentScanConfigurationMetaData(BeanFactory beanFactory) { + super(IntegrationComponentScanConfiguration.class, true); + this.beanFactory = beanFactory; + } + + @Override + public Map getAnnotationAttributes(String annotationName) { + Map attributes = super.getAnnotationAttributes( + annotationName); + if (IntegrationComponentScan.class.getName().equals(annotationName) + && AutoConfigurationPackages.has(this.beanFactory)) { + List packages = AutoConfigurationPackages.get(this.beanFactory); + attributes = new LinkedHashMap(attributes); + attributes.put("value", packages.toArray(new String[packages.size()])); + } + return attributes; + } + } + + @IntegrationComponentScan + private static class IntegrationComponentScanConfiguration { + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java index a445add2e1..381b945f8e 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java @@ -24,6 +24,7 @@ import javax.management.MBeanServer; import org.junit.After; import org.junit.Test; +import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration.IntegrationComponentScanAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -65,7 +66,7 @@ public class IntegrationAutoConfigurationTests { public void integrationIsAvailable() { load(); assertThat(this.context.getBean(TestGateway.class)).isNotNull(); - assertThat(this.context.getBean(IntegrationAutoConfiguration.IntegrationComponentScanAutoConfiguration.class)) + assertThat(this.context.getBean(IntegrationComponentScanAutoConfiguration.class)) .isNotNull(); } @@ -73,12 +74,12 @@ public class IntegrationAutoConfigurationTests { public void explicitIntegrationComponentScan() { this.context = new AnnotationConfigApplicationContext(); this.context.register(IntegrationComponentScanConfiguration.class, - JmxAutoConfiguration.class, - IntegrationAutoConfiguration.class); + JmxAutoConfiguration.class, IntegrationAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(TestGateway.class)).isNotNull(); - assertThat(this.context.getBeansOfType(IntegrationAutoConfiguration.IntegrationComponentScanAutoConfiguration.class)) - .isEmpty(); + assertThat(this.context + .getBeansOfType(IntegrationComponentScanAutoConfiguration.class)) + .isEmpty(); } @Test @@ -89,7 +90,8 @@ public class IntegrationAutoConfigurationTests { this.context.setParent(parent); this.context.register(JmxAutoConfiguration.class, IntegrationAutoConfiguration.class); - TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, "SPRING_JMX_DEFAULT_DOMAIN=org.foo"); + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context, + "SPRING_JMX_DEFAULT_DOMAIN=org.foo"); this.context.refresh(); assertThat(this.context.getBean(HeaderChannelRegistry.class)).isNotNull(); } diff --git a/spring-boot-samples/spring-boot-sample-integration/src/main/java/sample/integration/SampleCommandLineRunner.java b/spring-boot-samples/spring-boot-sample-integration/src/main/java/sample/integration/SampleCommandLineRunner.java new file mode 100644 index 0000000000..1ec5db2d13 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-integration/src/main/java/sample/integration/SampleCommandLineRunner.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2016 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 + * + * http://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 sample.integration; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +@Component +public class SampleCommandLineRunner implements CommandLineRunner { + + private final SampleMessageGateway gateway; + + public SampleCommandLineRunner(SampleMessageGateway gateway) { + this.gateway = gateway; + } + + @Override + public void run(String... args) throws Exception { + for (String arg : args) { + this.gateway.echo(arg); + } + } + +} diff --git a/spring-boot-samples/spring-boot-sample-integration/src/main/java/sample/integration/SampleMessageGateway.java b/spring-boot-samples/spring-boot-sample-integration/src/main/java/sample/integration/SampleMessageGateway.java new file mode 100644 index 0000000000..6f824c87f9 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-integration/src/main/java/sample/integration/SampleMessageGateway.java @@ -0,0 +1,26 @@ +/* + * Copyright 2012-2016 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 + * + * http://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 sample.integration; + +import org.springframework.integration.annotation.MessagingGateway; + +@MessagingGateway(defaultRequestChannel = "outputChannel") +public interface SampleMessageGateway { + + void echo(String message); + +} diff --git a/spring-boot-samples/spring-boot-sample-integration/src/test/java/sample/integration/consumer/SampleIntegrationApplicationTests.java b/spring-boot-samples/spring-boot-sample-integration/src/test/java/sample/integration/consumer/SampleIntegrationApplicationTests.java index bd5bd9ba43..ac013b438a 100644 --- a/spring-boot-samples/spring-boot-sample-integration/src/test/java/sample/integration/consumer/SampleIntegrationApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-integration/src/test/java/sample/integration/consumer/SampleIntegrationApplicationTests.java @@ -23,9 +23,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import org.junit.AfterClass; +import org.junit.After; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import sample.integration.SampleIntegrationApplication; import sample.integration.producer.ProducerApplication; @@ -48,32 +47,37 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class SampleIntegrationApplicationTests { - private static ConfigurableApplicationContext context; - - @BeforeClass - public static void start() throws Exception { - context = SpringApplication.run(SampleIntegrationApplication.class); - } - - @AfterClass - public static void stop() { - if (context != null) { - context.close(); - } - } + private ConfigurableApplicationContext context; @Before public void deleteOutput() { + FileSystemUtils.deleteRecursively(new File("target/input")); FileSystemUtils.deleteRecursively(new File("target/output")); } + @After + public void stop() { + if (this.context != null) { + this.context.close(); + } + } + @Test public void testVanillaExchange() throws Exception { + this.context = SpringApplication.run(SampleIntegrationApplication.class); SpringApplication.run(ProducerApplication.class, "World"); String output = getOutput(); assertThat(output).contains("Hello World"); } + @Test + public void testMessageGateway() throws Exception { + this.context = SpringApplication.run(SampleIntegrationApplication.class, + "testviamg"); + String output = getOutput(); + assertThat(output).contains("testviamg"); + } + private String getOutput() throws Exception { Future future = Executors.newSingleThreadExecutor() .submit(new Callable() {