diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index 69368a0695..8dd25bc5aa 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -199,11 +199,6 @@ - - org.springframework.integration - spring-integration-jmx - true - org.springframework.integration spring-integration-core @@ -349,6 +344,11 @@ spring-data-rest-webmvc test + + org.springframework.integration + spring-integration-jmx + test + org.springframework.security spring-security-test 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/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java index e3cad77e69..14a02cc3bf 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 @@ -44,6 +44,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 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; @@ -51,6 +52,7 @@ 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.support.management.IntegrationManagementConfigurer; /** * {@link EnableAutoConfiguration Auto-configuration} for {@link PublicMetrics}. @@ -58,6 +60,7 @@ import org.springframework.integration.monitor.IntegrationMBeanExporter; * @author Stephane Nicoll * @author Phillip Webb * @author Johannes Edmeier + * @author Artem Bilan * @since 1.2.0 */ @Configuration @@ -137,15 +140,23 @@ public class PublicMetricsAutoConfiguration { @Configuration @ConditionalOnClass(IntegrationMBeanExporter.class) - @ConditionalOnBean(IntegrationMBeanExporter.class) 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 62217c1a76..dab8e7a778 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,22 +22,27 @@ 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; /** * 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 */ public class SpringIntegrationMetricReader implements MetricReader { - private final IntegrationMBeanExporter exporter; + private final IntegrationManagementConfigurer configurer; - public SpringIntegrationMetricReader(IntegrationMBeanExporter exporter) { - this.exporter = exporter; + public SpringIntegrationMetricReader(IntegrationManagementConfigurer configurer) { + this.configurer = configurer; } @Override @@ -47,51 +52,80 @@ public class SpringIntegrationMetricReader implements MetricReader { @Override public Iterable> findAll() { - IntegrationMBeanExporter exporter = this.exporter; - List> metrics = new ArrayList>(); - for (String name : exporter.getChannelNames()) { - 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))); - } - for (String name : exporter.getHandlerNames()) { - metrics.addAll(getStatistics("integration.handler." + name + ".duration", - exporter.getHandlerDuration(name))); - } - metrics.add(new Metric("integration.activeHandlerCount", - exporter.getActiveHandlerCount())); - metrics.add(new Metric("integration.handlerCount", - exporter.getHandlerCount())); - metrics.add(new Metric("integration.channelCount", - exporter.getChannelCount())); - metrics.add(new Metric("integration.queuedMessageCount", - exporter.getQueuedMessageCount())); - 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.exporter.getChannelCount() * 11; - int totalHandlerCount = this.exporter.getHandlerCount() * 5; - return totalChannelCount + totalHandlerCount + 4; + 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/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-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..18efb7f359 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/integration/SpringIntegrationMetricReaderNoJmxTests.java @@ -0,0 +1,60 @@ +/* + * 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..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 @@ -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,9 @@ 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..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 @@ -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; @@ -33,8 +31,11 @@ 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.integration.config.EnableIntegration; +import org.springframework.integration.config.EnableIntegrationManagement; +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; @@ -54,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) @@ -67,17 +74,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 +100,35 @@ 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; } } + /** + * Integration management configuration. + */ + @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 { + } + + } + + /** + * Integration component scan configuration. + */ + @ConditionalOnMissingBean(GatewayProxyFactoryBean.class) + @Import(IntegrationAutoConfigurationScanRegistrar.class) + protected static class IntegrationComponentScanAutoConfiguration { + + } + } 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/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/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-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..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,13 +24,18 @@ 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; 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; import org.springframework.test.context.support.TestPropertySourceUtils; @@ -60,24 +65,35 @@ 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(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(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 @@ -86,12 +102,17 @@ public class IntegrationAutoConfigurationTests { MBeanServer mBeanServer = this.context.getBean(MBeanServer.class); assertDomains(mBeanServer, true, "org.springframework.integration", "org.springframework.integration.monitor"); + 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(); } @Test @@ -145,4 +166,15 @@ public class IntegrationAutoConfigurationTests { } + @Configuration + @IntegrationComponentScan + static class IntegrationComponentScanConfiguration { + + } + + @MessagingGateway + public interface TestGateway extends RequestReplyExchanger { + + } + } 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); 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); } } 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() { diff --git a/spring-boot-starters/spring-boot-starter-integration/pom.xml b/spring-boot-starters/spring-boot-starter-integration/pom.xml index 8a6f1caad5..6ca4b5fd0b 100644 --- a/spring-boot-starters/spring-boot-starter-integration/pom.xml +++ b/spring-boot-starters/spring-boot-starter-integration/pom.xml @@ -30,9 +30,5 @@ org.springframework.integration spring-integration-core - - 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 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(); + } + + } + +} 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/bind/RelaxedPropertyResolver.java b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedPropertyResolver.java index bf8f7da890..788681e652 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; /** @@ -129,4 +131,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 7e815cc94d..4ad47f0816 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 54b3c81916..55e6c7b259 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) { @@ -131,13 +132,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"); @@ -146,7 +147,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/logging/LoggingApplicationListenerTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/LoggingApplicationListenerTests.java index 84932d7591..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"); @@ -469,6 +469,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, 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); }