From 68e33b25c1cba2ef993f0115727b9eb5bf38779e Mon Sep 17 00:00:00 2001 From: Marcel Overdijk Date: Thu, 17 Apr 2014 23:59:36 +0200 Subject: [PATCH] Added liquibase autoconfiguration for database migrations If Liquibase is on the classpath it will fire up on startup. Various config options are available (as well as the option to disable it). Liquibase uses a YAML format for changes (in classpath:db/changelog). --- spring-boot-autoconfigure/pom.xml | 5 + .../liquibase/LiquibaseAutoConfiguration.java | 83 +++++++++++ .../liquibase/LiquibaseProperties.java | 93 ++++++++++++ .../main/resources/META-INF/spring.factories | 1 + .../LiquibaseAutoConfigurationTests.java | 135 ++++++++++++++++++ .../db/changelog/db.changelog-master.yaml | 20 +++ .../db/changelog/db.changelog-override.xml | 20 +++ .../appendix-application-properties.adoc | 8 +- .../appendix-auto-configuration-classes.adoc | 3 + spring-boot-docs/src/main/asciidoc/howto.adoc | 15 ++ spring-boot-docs/src/main/asciidoc/index.adoc | 2 +- .../src/main/asciidoc/using-spring-boot.adoc | 3 + spring-boot-samples/pom.xml | 1 + .../spring-boot-sample-liquibase/pom.xml | 49 +++++++ .../liquibase/SampleLiquibaseApplication.java | 32 +++++ .../src/main/resources/application.properties | 1 + .../db/changelog/db.changelog-master.yaml | 38 +++++ .../SampleLiquibaseApplicationTests.java | 66 +++++++++ spring-boot-starters/pom.xml | 1 + .../spring-boot-starter-liquibase/pom.xml | 31 ++++ .../main/resources/META-INF/spring.provides | 1 + .../spring-boot-starter-parent/pom.xml | 5 + 22 files changed, 611 insertions(+), 2 deletions(-) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseProperties.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java create mode 100644 spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-master.yaml create mode 100644 spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-override.xml create mode 100644 spring-boot-samples/spring-boot-sample-liquibase/pom.xml create mode 100644 spring-boot-samples/spring-boot-sample-liquibase/src/main/java/sample/liquibase/SampleLiquibaseApplication.java create mode 100644 spring-boot-samples/spring-boot-sample-liquibase/src/main/resources/application.properties create mode 100644 spring-boot-samples/spring-boot-sample-liquibase/src/main/resources/db/changelog/db.changelog-master.yaml create mode 100644 spring-boot-samples/spring-boot-sample-liquibase/src/test/java/sample/liquibase/SampleLiquibaseApplicationTests.java create mode 100644 spring-boot-starters/spring-boot-starter-liquibase/pom.xml create mode 100644 spring-boot-starters/spring-boot-starter-liquibase/src/main/resources/META-INF/spring.provides diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 39a264756b..d2b1de8279 100644 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -167,6 +167,11 @@ jedis true + + org.liquibase + liquibase-core + true + org.springframework.security spring-security-acl diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java new file mode 100644 index 0000000000..06d1b6fe8d --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration.java @@ -0,0 +1,83 @@ +/* + * Copyright 2012-2014 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.liquibase; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; + +import liquibase.integration.spring.SpringLiquibase; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.Assert; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Liquibase. + * + * @author Marcel Overdijk + */ +@Configuration +@ConditionalOnClass(SpringLiquibase.class) +@AutoConfigureAfter(DataSourceAutoConfiguration.class) +public class LiquibaseAutoConfiguration { + + @Configuration + @ConditionalOnMissingBean(SpringLiquibase.class) + @EnableConfigurationProperties(LiquibaseProperties.class) + public static class LiquibaseConfiguration { + + @Autowired + private LiquibaseProperties properties = new LiquibaseProperties(); + + @Autowired + private ResourceLoader resourceLoader = new DefaultResourceLoader(); + + @Autowired + private DataSource dataSource; + + @PostConstruct + public void checkChangelogExists() { + if (this.properties.isCheckChangeLogLocation()) { + Resource resource = this.resourceLoader.getResource(this.properties.getChangeLog()); + Assert.state(resource.exists(), "Cannot find changelog location: " + + resource + " (please add changelog or check your Liquibase configuration)"); + } + } + + @Bean + public SpringLiquibase liquibase() { + SpringLiquibase liquibase = new SpringLiquibase(); + liquibase.setChangeLog(this.properties.getChangeLog()); + liquibase.setContexts(this.properties.getContexts()); + liquibase.setDataSource(dataSource); + liquibase.setDefaultSchema(this.properties.getDefaultSchema()); + liquibase.setDropFirst(this.properties.isDropFirst()); + liquibase.setShouldRun(this.properties.isShouldRun()); + return liquibase; + } + } +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseProperties.java new file mode 100644 index 0000000000..611e7488b5 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseProperties.java @@ -0,0 +1,93 @@ +/* + * Copyright 2012-2014 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.liquibase; + +import javax.validation.constraints.NotNull; + +import liquibase.integration.spring.SpringLiquibase; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties to configure {@link SpringLiquibase}. + * + * @author Marcel Overdijk + */ +@ConfigurationProperties(prefix = "spring.liquibase", ignoreUnknownFields = false) +public class LiquibaseProperties { + + @NotNull + private String changeLog = "classpath:/db/changelog/db.changelog-master.yaml"; + + private boolean checkChangeLogLocation = true; + + private String contexts; + + private String defaultSchema; + + private boolean dropFirst = false; + + private boolean shouldRun = true; + + public String getChangeLog() { + return changeLog; + } + + public void setChangeLog(String changeLog) { + this.changeLog = changeLog; + } + + public boolean isCheckChangeLogLocation() { + return checkChangeLogLocation; + } + + public void setCheckChangeLogLocation(boolean checkChangeLogLocation) { + this.checkChangeLogLocation = checkChangeLogLocation; + } + + public String getContexts() { + return contexts; + } + + public void setContexts(String contexts) { + this.contexts = contexts; + } + + public String getDefaultSchema() { + return defaultSchema; + } + + public void setDefaultSchema(String defaultSchema) { + this.defaultSchema = defaultSchema; + } + + public boolean isDropFirst() { + return dropFirst; + } + + public void setDropFirst(boolean dropFirst) { + this.dropFirst = dropFirst; + } + + public boolean isShouldRun() { + return shouldRun; + } + + public void setShouldRun(boolean shouldRun) { + this.shouldRun = shouldRun; + } +} diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 14dda16166..d6ae3f7b98 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -19,6 +19,7 @@ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ +org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java new file mode 100644 index 0000000000..bd854539a3 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfigurationTests.java @@ -0,0 +1,135 @@ +/* + * Copyright 2012-2014 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.liquibase; + +import liquibase.integration.spring.SpringLiquibase; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link LiquibaseAutoConfiguration}. + * + * @author Marcel Overdijk + */ +public class LiquibaseAutoConfigurationTests { + + @Rule + public ExpectedException expected = ExpectedException.none(); + + private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void testNoDataSource() throws Exception { + this.context.register(LiquibaseAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.expected.expect(BeanCreationException.class); + this.expected.expectMessage("No qualifying bean"); + this.expected.expectMessage("DataSource"); + this.context.refresh(); + } + + @Test + public void testDefaultSpringLiquibase() throws Exception { + this.context.register(EmbeddedDataSourceConfiguration.class, + LiquibaseAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + SpringLiquibase liquibase = this.context.getBean(SpringLiquibase.class); + assertEquals("classpath:/db/changelog/db.changelog-master.yaml", liquibase.getChangeLog()); + assertNull(liquibase.getContexts()); + assertNull(liquibase.getDefaultSchema()); + assertFalse(liquibase.isDropFirst()); + } + + @Test + public void testOverrideChangeLog() throws Exception { + EnvironmentTestUtils.addEnvironment(this.context, + "spring.liquibase.change-log:classpath:/db/changelog/db.changelog-override.xml"); + this.context.register(EmbeddedDataSourceConfiguration.class, + LiquibaseAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + SpringLiquibase liquibase = this.context.getBean(SpringLiquibase.class); + assertEquals("classpath:/db/changelog/db.changelog-override.xml", liquibase.getChangeLog()); + } + + @Test + public void testOverrideContexts() throws Exception { + EnvironmentTestUtils.addEnvironment(this.context, + "spring.liquibase.contexts:test, production"); + this.context.register(EmbeddedDataSourceConfiguration.class, + LiquibaseAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + SpringLiquibase liquibase = this.context.getBean(SpringLiquibase.class); + assertEquals("test, production", liquibase.getContexts()); + } + + @Test + public void testOverrideDefaultSchema() throws Exception { + EnvironmentTestUtils.addEnvironment(this.context, + "spring.liquibase.default-schema:public"); + this.context.register(EmbeddedDataSourceConfiguration.class, + LiquibaseAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + SpringLiquibase liquibase = this.context.getBean(SpringLiquibase.class); + assertEquals("public", liquibase.getDefaultSchema()); + } + + @Test + public void testOverrideDropFirst() throws Exception { + EnvironmentTestUtils.addEnvironment(this.context, + "spring.liquibase.drop-first:true"); + this.context.register(EmbeddedDataSourceConfiguration.class, + LiquibaseAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + SpringLiquibase liquibase = this.context.getBean(SpringLiquibase.class); + assertTrue(liquibase.isDropFirst()); + } + + @Test(expected = BeanCreationException.class) + public void testChangeLogDoesNotExist() throws Exception { + EnvironmentTestUtils.addEnvironment(this.context, + "spring.liquibase.change-log:classpath:/no-such-changelog.yaml"); + this.context.register(EmbeddedDataSourceConfiguration.class, + LiquibaseAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + } +} diff --git a/spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-master.yaml b/spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000000..134b17b543 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,20 @@ +databaseChangeLog: + - changeSet: + id: 1 + author: marceloverdijk + changes: + - createTable: + tableName: customer + columns: + - column: + name: id + type: int + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: name + type: varchar(50) + constraints: + nullable: false diff --git a/spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-override.xml b/spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-override.xml new file mode 100644 index 0000000000..2e418cb347 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/resources/db/changelog/db.changelog-override.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + 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 a9232a8f95..98be7cdedb 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -158,6 +158,13 @@ content into your application; rather pick only the properties that you need. spring.jpa.hibernate.naming-strategy= # naming classname spring.jpa.hibernate.ddl-auto= # defaults to create-drop for embedded dbs + # LIQUIBASE ({sc-spring-boot-autoconfigure}/liquibase/LiquibaseProperties.{sc-ext}[LiquibaseProperties]) + spring.liquibase.change-log=classpath:/db/changelog/db.changelog-master.yaml + spring.liquibase.contexts= # runtime contexts to use + spring.liquibase.default-schema= # default database schema to use + spring.liquibase.drop-first=false + spring.liquibase.should-run=true + # JMX spring.jmx.enabled=true # Expose MBeans from Spring @@ -170,7 +177,6 @@ content into your application; rather pick only the properties that you need. spring.rabbitmq.virtualhost= spring.rabbitmq.dynamic= - # REDIS ({sc-spring-boot-autoconfigure}/redis/RedisProperties.{sc-ext}[RedisProperties]) spring.redis.host=localhost # server host spring.redis.password= # server password diff --git a/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc b/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc index 1a81322ee4..3aef5ae839 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-auto-configuration-classes.adoc @@ -56,6 +56,9 @@ The following auto-configuration classes are from the `spring-boot-autoconfigure |{sc-spring-boot-autoconfigure}/data/JpaRepositoriesAutoConfiguration.{sc-ext}[JpaRepositoriesAutoConfiguration] |{dc-spring-boot-autoconfigure}/data/JpaRepositoriesAutoConfiguration.{dc-ext}[javadoc] +|{sc-spring-boot-autoconfigure}/liquibase/LiquibaseAutoConfiguration.{sc-ext}[LiquibaseAutoConfiguration] +|{dc-spring-boot-autoconfigure}/liquibase/LiquibaseAutoConfiguration.{dc-ext}[javadoc] + |{sc-spring-boot-autoconfigure}/MessageSourceAutoConfiguration.{sc-ext}[MessageSourceAutoConfiguration] |{dc-spring-boot-autoconfigure}/MessageSourceAutoConfiguration.{dc-ext}[javadoc] diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc index de13c412f1..e7ca6d215c 100644 --- a/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -1097,6 +1097,21 @@ independence: usually only one or at most couple of platforms is needed. +[[howto-execute-liquibase-database-migrations-on-startup]] +==== Execute Liquibase database migrations on startup +To automatically run Liquibase database migrations on startup, add the +`spring-boot-starter-liquibase` to your classpath. + +The master change log is by default read from `db/changelog/db.changelog-master.yaml` but +can be set using `spring.liquibase.change-log`. See +{sc-spring-boot-autoconfigure}/liquibase/LiquibaseProperties.{sc-ext}[`LiquibaseProperties`] +for details of available settings like contexts, default schema etc. + +There is a {github-code}/spring-boot-samples/spring-boot-sample-liquibase[Liquibase sample] so +you can see how to set things up. + + + [[howto-batch-applications]] == Batch applications diff --git a/spring-boot-docs/src/main/asciidoc/index.adoc b/spring-boot-docs/src/main/asciidoc/index.adoc index e7d603074f..e6f5d977c2 100644 --- a/spring-boot-docs/src/main/asciidoc/index.adoc +++ b/spring-boot-docs/src/main/asciidoc/index.adoc @@ -1,5 +1,5 @@ = Spring Boot Reference Guide -Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; +Phillip Webb; Dave Syer; Josh Long; Stéphane Nicoll; Rob Winch; Andy Wilkinson; Marcel Overdijk; :doctype: book :toc: :toclevels: 4 diff --git a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc index 9feef384d5..8809674784 100644 --- a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc +++ b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc @@ -238,6 +238,9 @@ and Hibernate. |`spring-boot-starter-jdbc` |JDBC Database support. +|`spring-boot-starter-liquibase` +|Support for Liquibase database migrations. + |`spring-boot-starter-mobile` |Support for `spring-mobile` diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index 83e0f32636..9def9bce5e 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -34,6 +34,7 @@ spring-boot-sample-data-rest spring-boot-sample-integration spring-boot-sample-jetty + spring-boot-sample-liquibase spring-boot-sample-profile spring-boot-sample-secure spring-boot-sample-servlet diff --git a/spring-boot-samples/spring-boot-sample-liquibase/pom.xml b/spring-boot-samples/spring-boot-sample-liquibase/pom.xml new file mode 100644 index 0000000000..f0522496be --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-liquibase/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 1.0.2.BUILD-SNAPSHOT + + spring-boot-sample-liquibase + Spring Boot Liquibase Sample + Spring Boot Liquibase Sample + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-liquibase + + + org.hsqldb + hsqldb + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-liquibase/src/main/java/sample/liquibase/SampleLiquibaseApplication.java b/spring-boot-samples/spring-boot-sample-liquibase/src/main/java/sample/liquibase/SampleLiquibaseApplication.java new file mode 100644 index 0000000000..7fc1fd0264 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-liquibase/src/main/java/sample/liquibase/SampleLiquibaseApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012-2013 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.liquibase; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan +@EnableAutoConfiguration +public class SampleLiquibaseApplication { + + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleLiquibaseApplication.class, args); + } +} diff --git a/spring-boot-samples/spring-boot-sample-liquibase/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-liquibase/src/main/resources/application.properties new file mode 100644 index 0000000000..bb3f2d169b --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-liquibase/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.jpa.hibernate.ddl-auto: none diff --git a/spring-boot-samples/spring-boot-sample-liquibase/src/main/resources/db/changelog/db.changelog-master.yaml b/spring-boot-samples/spring-boot-sample-liquibase/src/main/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 0000000000..b0ecfe9a25 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-liquibase/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,38 @@ +databaseChangeLog: + - changeSet: + id: 1 + author: marceloverdijk + changes: + - createTable: + tableName: person + columns: + - column: + name: id + type: int + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: first_name + type: varchar(255) + constraints: + nullable: false + - column: + name: last_name + type: varchar(255) + constraints: + nullable: false + - changeSet: + id: 2 + author: marceloverdijk + changes: + - insert: + tableName: person + columns: + - column: + name: first_name + value: Marcel + - column: + name: last_name + value: Overdijk diff --git a/spring-boot-samples/spring-boot-sample-liquibase/src/test/java/sample/liquibase/SampleLiquibaseApplicationTests.java b/spring-boot-samples/spring-boot-sample-liquibase/src/test/java/sample/liquibase/SampleLiquibaseApplicationTests.java new file mode 100644 index 0000000000..f6337dc0a5 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-liquibase/src/test/java/sample/liquibase/SampleLiquibaseApplicationTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2014 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.liquibase; + +import java.net.ConnectException; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.boot.test.OutputCapture; +import org.springframework.core.NestedCheckedException; + +import static org.junit.Assert.assertTrue; + +public class SampleLiquibaseApplicationTests { + + @Rule + public OutputCapture outputCapture = new OutputCapture(); + + @Test + public void testDefaultSettings() throws Exception { + try { + SampleLiquibaseApplication.main(new String[0]); + } + catch (IllegalStateException ex) { + if (serverNotRunning(ex)) { + return; + } + } + String output = this.outputCapture.toString(); + assertTrue("Wrong output: " + output, + output.contains("Successfully acquired change log lock") && + output.contains("Creating database history table with name: PUBLIC.DATABASECHANGELOG") && + output.contains("Table person created") && + output.contains("ChangeSet classpath:/db/changelog/db.changelog-master.yaml::1::marceloverdijk ran successfully") && + output.contains("New row inserted into person") && + output.contains("ChangeSet classpath:/db/changelog/db.changelog-master.yaml::2::marceloverdijk ran successfully") && + output.contains("Successfully released change log lock")); + } + + private boolean serverNotRunning(IllegalStateException ex) { + @SuppressWarnings("serial") + NestedCheckedException nested = new NestedCheckedException("failed", ex) { + }; + if (nested.contains(ConnectException.class)) { + Throwable root = nested.getRootCause(); + if (root.getMessage().contains("Connection refused")) { + return true; + } + } + return false; + } +} diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index 5db4ec71f4..f8067c5def 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -33,6 +33,7 @@ spring-boot-starter-integration spring-boot-starter-jdbc spring-boot-starter-jetty + spring-boot-starter-liquibase spring-boot-starter-logging spring-boot-starter-log4j spring-boot-starter-mobile diff --git a/spring-boot-starters/spring-boot-starter-liquibase/pom.xml b/spring-boot-starters/spring-boot-starter-liquibase/pom.xml new file mode 100644 index 0000000000..c7d4822175 --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-liquibase/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starters + 1.0.2.BUILD-SNAPSHOT + + spring-boot-starter-liquibase + Spring Boot Liquibase Starter + Spring Boot Liquibase Starter + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + ${project.groupId} + spring-boot-starter-jdbc + ${project.version} + + + org.liquibase + liquibase-core + + + diff --git a/spring-boot-starters/spring-boot-starter-liquibase/src/main/resources/META-INF/spring.provides b/spring-boot-starters/spring-boot-starter-liquibase/src/main/resources/META-INF/spring.provides new file mode 100644 index 0000000000..cec4231369 --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-liquibase/src/main/resources/META-INF/spring.provides @@ -0,0 +1 @@ +provides: liquibase-core \ No newline at end of file diff --git a/spring-boot-starters/spring-boot-starter-parent/pom.xml b/spring-boot-starters/spring-boot-starter-parent/pom.xml index a18cfc637a..7ffc194cec 100644 --- a/spring-boot-starters/spring-boot-starter-parent/pom.xml +++ b/spring-boot-starters/spring-boot-starter-parent/pom.xml @@ -121,6 +121,11 @@ spring-boot-starter-jetty ${spring-boot.version} + + org.springframework.boot + spring-boot-starter-liquibase + ${spring-boot.version} + org.springframework.boot spring-boot-starter-logging