From 5302d91930d1676ba105dc7f7ab3a038601153c6 Mon Sep 17 00:00:00 2001 From: Leo Li <269739606@qq.com> Date: Thu, 28 Nov 2019 11:58:58 +0800 Subject: [PATCH] Fix Liquibase endpoint's output with multiple datasources Previously, the endpoint used the same change log history service for for each SpringLiquibase bean that it processed. This resulted in pollution of the reported changes as the history of each bean was not isolated. This commit updates the endpoint to use a new history service for each SpringLiquibase bean that is processed. See gh-19171 --- .../actuate/liquibase/LiquibaseEndpoint.java | 7 ++- .../liquibase/LiquibaseEndpointTests.java | 57 +++++++++++++++++++ .../changelog/db.changelog-master-backup.yaml | 20 +++++++ 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator/src/test/resources/db/changelog/db.changelog-master-backup.yaml diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java index 61d3a87220..612560e319 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpoint.java @@ -63,9 +63,10 @@ public class LiquibaseEndpoint { while (target != null) { Map liquibaseBeans = new HashMap<>(); DatabaseFactory factory = DatabaseFactory.getInstance(); - StandardChangeLogHistoryService service = new StandardChangeLogHistoryService(); - this.context.getBeansOfType(SpringLiquibase.class) - .forEach((name, liquibase) -> liquibaseBeans.put(name, createReport(liquibase, service, factory))); + this.context.getBeansOfType(SpringLiquibase.class).forEach((name, liquibase) -> { + StandardChangeLogHistoryService service = new StandardChangeLogHistoryService(); + liquibaseBeans.put(name, createReport(liquibase, service, factory)); + }); ApplicationContext parent = target.getParent(); contextBeans.put(target.getId(), new ContextLiquibaseBeans(liquibaseBeans, (parent != null) ? parent.getId() : null)); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java index f345c485a1..b10baa0851 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java @@ -22,6 +22,9 @@ import java.util.Map; import javax.sql.DataSource; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import liquibase.integration.spring.SpringLiquibase; import org.junit.Test; import org.springframework.boot.actuate.liquibase.LiquibaseEndpoint.LiquibaseBean; @@ -41,6 +44,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author EddĂș MelĂ©ndez * @author Andy Wilkinson * @author Stephane Nicoll + * @author Leo Li */ public class LiquibaseEndpointTests { @@ -92,6 +96,20 @@ public class LiquibaseEndpointTests { }); } + @Test + public void multipleLiquibaseReportIsReturned() { + this.contextRunner.withUserConfiguration(Config.class, LiquibaseConfiguration.class).run((context) -> { + Map liquibaseBeans = context.getBean(LiquibaseEndpoint.class).liquibaseBeans() + .getContexts().get(context.getId()).getLiquibaseBeans(); + assertThat(liquibaseBeans.get("liquibase").getChangeSets()).hasSize(1); + assertThat(liquibaseBeans.get("liquibase").getChangeSets().get(0).getChangeLog()) + .isEqualTo("classpath:/db/changelog/db.changelog-master.yaml"); + assertThat(liquibaseBeans.get("liquibaseBackup").getChangeSets()).hasSize(1); + assertThat(liquibaseBeans.get("liquibaseBackup").getChangeSets().get(0).getChangeLog()) + .isEqualTo("classpath:/db/changelog/db.changelog-master-backup.yaml"); + }); + } + private boolean getAutoCommit(DataSource dataSource) throws SQLException { try (Connection connection = dataSource.getConnection()) { return connection.getAutoCommit(); @@ -108,4 +126,43 @@ public class LiquibaseEndpointTests { } + @Configuration + static class LiquibaseConfiguration { + + @Bean + DataSource dataSource() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:hsqldb:mem:test"); + config.setUsername("sa"); + return new HikariDataSource(config); + } + + @Bean + DataSource dataSourceBackup() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:hsqldb:mem:testBackup"); + config.setUsername("sa"); + return new HikariDataSource(config); + } + + @Bean + SpringLiquibase liquibase(DataSource dataSource) { + SpringLiquibase liquibase = new SpringLiquibase(); + liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.yaml"); + liquibase.setShouldRun(true); + liquibase.setDataSource(dataSource); + return liquibase; + } + + @Bean + SpringLiquibase liquibaseBackup(DataSource dataSourceBackup) { + SpringLiquibase liquibase = new SpringLiquibase(); + liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master-backup.yaml"); + liquibase.setShouldRun(true); + liquibase.setDataSource(dataSourceBackup); + return liquibase; + } + + } + } diff --git a/spring-boot-project/spring-boot-actuator/src/test/resources/db/changelog/db.changelog-master-backup.yaml b/spring-boot-project/spring-boot-actuator/src/test/resources/db/changelog/db.changelog-master-backup.yaml new file mode 100644 index 0000000000..a3a19b43af --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/resources/db/changelog/db.changelog-master-backup.yaml @@ -0,0 +1,20 @@ +databaseChangeLog: + - changeSet: + id: 1 + author: leoli + changes: + - createTable: + tableName: customerbackup + columns: + - column: + name: id + type: int + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: name + type: varchar(50) + constraints: + nullable: false