diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index dd0f773bf8..84eb9a2e64 100755 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -329,6 +329,11 @@ spring-integration-core true + + org.springframework.integration + spring-integration-jdbc + true + org.springframework.integration spring-integration-jmx 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 0241676b84..bdf301d8c2 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.integration; import javax.management.MBeanServer; +import javax.sql.DataSource; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; @@ -25,17 +26,21 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.boot.bind.RelaxedPropertyResolver; +import org.springframework.boot.context.properties.EnableConfigurationProperties; 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.io.ResourceLoader; import org.springframework.integration.config.EnableIntegration; import org.springframework.integration.config.EnableIntegrationManagement; import org.springframework.integration.gateway.GatewayProxyFactoryBean; +import org.springframework.integration.jdbc.store.JdbcMessageStore; import org.springframework.integration.jmx.config.EnableIntegrationMBeanExport; import org.springframework.integration.monitor.IntegrationMBeanExporter; import org.springframework.integration.support.management.IntegrationManagementConfigurer; @@ -48,10 +53,12 @@ import org.springframework.util.StringUtils; * @author Artem Bilan * @author Dave Syer * @author Stephane Nicoll + * @author Vedran Pavic * @since 1.1.0 */ @Configuration @ConditionalOnClass(EnableIntegration.class) +@EnableConfigurationProperties(IntegrationProperties.class) @AutoConfigureAfter(JmxAutoConfiguration.class) public class IntegrationAutoConfiguration { @@ -131,4 +138,24 @@ public class IntegrationAutoConfiguration { } + /** + * Integration JDBC configuration. + */ + @Configuration + @ConditionalOnClass(JdbcMessageStore.class) + @ConditionalOnSingleCandidate(DataSource.class) + protected static class IntegrationJdbcConfiguration { + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(prefix = "spring.integration.jdbc.initializer", name = "enabled") + public IntegrationDatabaseInitializer integrationDatabaseInitializer( + DataSource dataSource, ResourceLoader resourceLoader, + IntegrationProperties properties) { + return new IntegrationDatabaseInitializer(dataSource, resourceLoader, + properties); + } + + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationDatabaseInitializer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationDatabaseInitializer.java new file mode 100644 index 0000000000..29d4d8f306 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationDatabaseInitializer.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2017 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 javax.sql.DataSource; + +import org.springframework.boot.autoconfigure.AbstractDatabaseInitializer; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.Assert; + +/** + * Initializer for Spring Integration schema. + * + * @author Vedran Pavic + * @since 2.0.0 + */ +public class IntegrationDatabaseInitializer extends AbstractDatabaseInitializer { + + private final IntegrationProperties.Jdbc properties; + + public IntegrationDatabaseInitializer(DataSource dataSource, + ResourceLoader resourceLoader, IntegrationProperties properties) { + super(dataSource, resourceLoader); + Assert.notNull(properties, "IntegrationProperties must not be null"); + this.properties = properties.getJdbc(); + } + + @Override + protected boolean isEnabled() { + return this.properties.getInitializer().isEnabled(); + } + + @Override + protected String getSchemaLocation() { + return this.properties.getSchema(); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationProperties.java new file mode 100644 index 0000000000..3ebab5508d --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationProperties.java @@ -0,0 +1,80 @@ +/* + * Copyright 2012-2017 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 org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for Spring Integration. + * + * @author Vedran Pavic + * @author Stephane Nicoll + * @since 2.0.0 + */ +@ConfigurationProperties(prefix = "spring.integration") +public class IntegrationProperties { + + private final Jdbc jdbc = new Jdbc(); + + public Jdbc getJdbc() { + return this.jdbc; + } + + public static class Jdbc { + + private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/springframework/" + + "integration/jdbc/schema-@@platform@@.sql"; + + /** + * Path to the SQL file to use to initialize the database schema. + */ + private String schema = DEFAULT_SCHEMA_LOCATION; + + private final Initializer initializer = new Initializer(); + + public String getSchema() { + return this.schema; + } + + public void setSchema(String schema) { + this.schema = schema; + } + + public Initializer getInitializer() { + return this.initializer; + } + + public class Initializer { + + /** + * Create the required integration tables on startup. + */ + private boolean enabled = false; + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + } + + } + +} 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 381b945f8e..04840221a9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -22,10 +22,16 @@ import java.util.List; import javax.management.MBeanServer; import org.junit.After; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration.IntegrationComponentScanAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; +import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -36,6 +42,8 @@ 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.jdbc.BadSqlGrammarException; +import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jmx.export.MBeanExporter; import org.springframework.test.context.support.TestPropertySourceUtils; @@ -47,9 +55,13 @@ import static org.mockito.Mockito.mock; * * @author Artem Bilan * @author Stephane Nicoll + * @author Vedran Pavic */ public class IntegrationAutoConfigurationTests { + @Rule + public ExpectedException thrown = ExpectedException.none(); + private AnnotationConfigApplicationContext context; @After @@ -126,12 +138,62 @@ public class IntegrationAutoConfigurationTests { @Test public void primaryExporterIsAllowed() { - load(CustomMBeanExporter.class); + load(new Class[] { CustomMBeanExporter.class }); assertThat(this.context.getBeansOfType(MBeanExporter.class)).hasSize(2); assertThat(this.context.getBean(MBeanExporter.class)) .isSameAs(this.context.getBean("myMBeanExporter")); } + @Test + public void integrationJdbcDatabaseInitializerEnabled() { + load(new Class[] { EmbeddedDataSourceConfiguration.class, + DataSourceTransactionManagerAutoConfiguration.class, + JdbcTemplateAutoConfiguration.class, + IntegrationAutoConfiguration.class}, + "spring.datasource.generate-unique-name=true", + "spring.integration.jdbc.initializer.enabled=true"); + assertThat(this.context.getBean(IntegrationProperties.class).getJdbc() + .getInitializer().isEnabled()).isTrue(); + JdbcOperations jdbcOperations = this.context.getBean(JdbcOperations.class); + assertThat(jdbcOperations.queryForList("select * from INT_MESSAGE")).isEmpty(); + assertThat(jdbcOperations.queryForList("select * from INT_GROUP_TO_MESSAGE")) + .isEmpty(); + assertThat(jdbcOperations.queryForList("select * from INT_MESSAGE_GROUP")) + .isEmpty(); + assertThat(jdbcOperations.queryForList("select * from INT_LOCK")).isEmpty(); + assertThat(jdbcOperations.queryForList("select * from INT_CHANNEL_MESSAGE")) + .isEmpty(); + } + + @Test + public void integrationJdbcDatabaseInitializerDisabled() { + load(new Class[] { EmbeddedDataSourceConfiguration.class, + DataSourceTransactionManagerAutoConfiguration.class, + JdbcTemplateAutoConfiguration.class, + IntegrationAutoConfiguration.class }, + "spring.datasource.generate-unique-name=true", + "spring.integration.jdbc.initializer.enabled=false"); + assertThat(this.context.getBean(IntegrationProperties.class).getJdbc() + .getInitializer().isEnabled()).isFalse(); + JdbcOperations jdbcOperations = this.context.getBean(JdbcOperations.class); + this.thrown.expect(BadSqlGrammarException.class); + jdbcOperations.queryForList("select * from INT_MESSAGE"); + } + + @Test + public void integrationJdbcDatabaseInitializerDisabledByDefault() { + load(new Class[] { EmbeddedDataSourceConfiguration.class, + DataSourceTransactionManagerAutoConfiguration.class, + JdbcTemplateAutoConfiguration.class, + IntegrationAutoConfiguration.class }, + "spring.datasource.generate-unique-name=true"); + assertThat(this.context.getBean(IntegrationProperties.class).getJdbc() + .getInitializer().isEnabled()).isFalse(); + JdbcOperations jdbcOperations = this.context.getBean(JdbcOperations.class); + this.thrown.expect(BadSqlGrammarException.class); + jdbcOperations.queryForList("select * from INT_MESSAGE"); + } + private static void assertDomains(MBeanServer mBeanServer, boolean expected, String... domains) { List actual = Arrays.asList(mBeanServer.getDomains()); @@ -144,12 +206,12 @@ public class IntegrationAutoConfigurationTests { load(null, environment); } - private void load(Class config, String... environment) { + private void load(Class[] configs, String... environment) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - if (config != null) { - ctx.register(config); + EnvironmentTestUtils.addEnvironment(ctx, environment); + if (configs != null) { + ctx.register(configs); } - TestPropertySourceUtils.addInlinedPropertiesToEnvironment(ctx, environment); ctx.register(JmxAutoConfiguration.class, IntegrationAutoConfiguration.class); ctx.refresh(); this.context = ctx; diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 591be0d4e4..3e5bb1617c 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -890,6 +890,10 @@ content into your application; rather pick only the properties that you need. spring.batch.schema=classpath:org/springframework/batch/core/schema-@@platform@@.sql # Path to the SQL file to use to initialize the database schema. spring.batch.table-prefix= # Table prefix for all the batch meta-data tables. + # SPRING INTEGRATION ({sc-spring-boot-autoconfigure}/integration/IntegrationProperties.{sc-ext}[IntegrationProperties]) + spring.integration.jdbc.initializer.enabled=false # Create the required integration tables on startup. + spring.integration.jdbc.schema=classpath:org/springframework/integration/jdbc/schema-@@platform@@.sql # Path to the SQL file to use to initialize the database schema. + # JMS ({sc-spring-boot-autoconfigure}/jms/JmsProperties.{sc-ext}[JmsProperties]) spring.jms.jndi-name= # Connection factory JNDI name. When set, takes precedence to others connection factory auto-configurations. spring.jms.listener.acknowledge-mode= # Acknowledge mode of the container. By default, the listener is transacted with automatic acknowledgment. diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 9a159859b5..e162a9edd1 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -5113,10 +5113,23 @@ Spring Boot offers several conveniences for working with Spring Integration, inc the `spring-boot-starter-integration` '`Starter`'. Spring Integration provides abstractions over messaging and also other transports such as HTTP, TCP etc. If Spring Integration is available on your classpath it will be initialized through the -`@EnableIntegration` annotation. Message processing statistics will be published over JMX -if `'spring-integration-jmx'` is also on the classpath. See the +`@EnableIntegration` annotation. + +Spring Boot will also configure some features that are triggered by the presence of +additional Spring Integration modules. Message processing statistics will be published +over JMX if `'spring-integration-jmx'` is also on the classpath. If +`'spring-integration-jdbc'` is available, the default database schema can be created +on startup: + +[source,properties,indent=0] +---- + spring.integration.jdbc.initializer.enabled=true +---- + +See the {sc-spring-boot-autoconfigure}/integration/IntegrationAutoConfiguration.{sc-ext}[`IntegrationAutoConfiguration`] -class for more details. +and {sc-spring-boot-autoconfigure}/integration/IntegrationProperties.{sc-ext}[`IntegrationProperties`] +classes for more details.