Merge pull request #6613 from eddumelendez:gh-6610

* pr/6613:
  Polish contribution
  Add support for multiple beans in the Flyway and Liquibase endpoints
This commit is contained in:
Stephane Nicoll 2016-12-22 11:45:51 +01:00
commit 4b23528eb4
4 changed files with 150 additions and 50 deletions

View File

@ -55,7 +55,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionEvaluationRepor
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
@ -188,27 +187,28 @@ public class EndpointAutoConfiguration {
} }
@Configuration @Configuration
@ConditionalOnSingleCandidate(Flyway.class) @ConditionalOnBean(Flyway.class)
@ConditionalOnClass(Flyway.class) @ConditionalOnClass(Flyway.class)
static class FlywayEndpointConfiguration { static class FlywayEndpointConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public FlywayEndpoint flywayEndpoint(Flyway flyway) { public FlywayEndpoint flywayEndpoint(Map<String, Flyway> flyways) {
return new FlywayEndpoint(flyway); return new FlywayEndpoint(flyways);
} }
} }
@Configuration @Configuration
@ConditionalOnSingleCandidate(SpringLiquibase.class) @ConditionalOnBean(SpringLiquibase.class)
@ConditionalOnClass(SpringLiquibase.class) @ConditionalOnClass(SpringLiquibase.class)
static class LiquibaseEndpointConfiguration { static class LiquibaseEndpointConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public LiquibaseEndpoint liquibaseEndpoint(SpringLiquibase liquibase) { public LiquibaseEndpoint liquibaseEndpoint(
return new LiquibaseEndpoint(liquibase); Map<String, SpringLiquibase> liquibases) {
return new LiquibaseEndpoint(liquibases);
} }
} }

View File

@ -17,15 +17,17 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.MigrationInfo; import org.flywaydb.core.api.MigrationInfo;
import org.flywaydb.core.api.MigrationState; import org.flywaydb.core.api.MigrationState;
import org.flywaydb.core.api.MigrationType; import org.flywaydb.core.api.MigrationType;
import org.springframework.boot.actuate.endpoint.FlywayEndpoint.FlywayMigration; import org.springframework.boot.actuate.endpoint.FlywayEndpoint.FlywayReport;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -38,23 +40,54 @@ import org.springframework.util.Assert;
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties(prefix = "endpoints.flyway") @ConfigurationProperties(prefix = "endpoints.flyway")
public class FlywayEndpoint extends AbstractEndpoint<List<FlywayMigration>> { public class FlywayEndpoint extends AbstractEndpoint<List<FlywayReport>> {
private final Flyway flyway; private final Map<String, Flyway> flyways;
public FlywayEndpoint(Flyway flyway) { public FlywayEndpoint(Flyway flyway) {
this(Collections.singletonMap("default", flyway));
}
public FlywayEndpoint(Map<String, Flyway> flyways) {
super("flyway"); super("flyway");
Assert.notNull(flyway, "Flyway must not be null"); Assert.notEmpty(flyways, "Flyways must be specified");
this.flyway = flyway; this.flyways = flyways;
} }
@Override @Override
public List<FlywayMigration> invoke() { public List<FlywayReport> invoke() {
List<FlywayReport> reports = new ArrayList<FlywayReport>();
for (Map.Entry<String, Flyway> entry : this.flyways.entrySet()) {
List<FlywayMigration> migrations = new ArrayList<FlywayMigration>(); List<FlywayMigration> migrations = new ArrayList<FlywayMigration>();
for (MigrationInfo info : this.flyway.info().all()) { for (MigrationInfo info : entry.getValue().info().all()) {
migrations.add(new FlywayMigration(info)); migrations.add(new FlywayMigration(info));
} }
return migrations; reports.add(new FlywayReport(entry.getKey(), migrations));
}
return reports;
}
/**
* Flyway report for one datasource.
*/
public static class FlywayReport {
private final String name;
private final List<FlywayMigration> migrations;
public FlywayReport(String name, List<FlywayMigration> migrations) {
this.name = name;
this.migrations = migrations;
}
public String getName() {
return this.name;
}
public List<FlywayMigration> getMigrations() {
return this.migrations;
}
} }
/** /**

View File

@ -16,6 +16,8 @@
package org.springframework.boot.actuate.endpoint; package org.springframework.boot.actuate.endpoint;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -27,6 +29,7 @@ import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection; import liquibase.database.jvm.JdbcConnection;
import liquibase.integration.spring.SpringLiquibase; import liquibase.integration.spring.SpringLiquibase;
import org.springframework.boot.actuate.endpoint.LiquibaseEndpoint.LiquibaseReport;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -37,26 +40,33 @@ import org.springframework.util.Assert;
* @since 1.3.0 * @since 1.3.0
*/ */
@ConfigurationProperties(prefix = "endpoints.liquibase") @ConfigurationProperties(prefix = "endpoints.liquibase")
public class LiquibaseEndpoint extends AbstractEndpoint<List<Map<String, ?>>> { public class LiquibaseEndpoint extends AbstractEndpoint<List<LiquibaseReport>> {
private final SpringLiquibase liquibase; private final Map<String, SpringLiquibase> liquibases;
public LiquibaseEndpoint(SpringLiquibase liquibase) { public LiquibaseEndpoint(SpringLiquibase liquibase) {
this(Collections.singletonMap("default", liquibase));
}
public LiquibaseEndpoint(Map<String, SpringLiquibase> liquibase) {
super("liquibase"); super("liquibase");
Assert.notNull(liquibase, "Liquibase must not be null"); Assert.notEmpty(liquibase, "Liquibase must be specified");
this.liquibase = liquibase; this.liquibases = liquibase;
} }
@Override @Override
public List<Map<String, ?>> invoke() { public List<LiquibaseReport> invoke() {
StandardChangeLogHistoryService service = new StandardChangeLogHistoryService(); List<LiquibaseReport> reports = new ArrayList<LiquibaseReport>();
try {
DatabaseFactory factory = DatabaseFactory.getInstance(); DatabaseFactory factory = DatabaseFactory.getInstance();
DataSource dataSource = this.liquibase.getDataSource(); StandardChangeLogHistoryService service = new StandardChangeLogHistoryService();
for (Map.Entry<String, SpringLiquibase> entry : this.liquibases.entrySet()) {
try {
DataSource dataSource = entry.getValue().getDataSource();
JdbcConnection connection = new JdbcConnection(dataSource.getConnection()); JdbcConnection connection = new JdbcConnection(dataSource.getConnection());
try { try {
Database database = factory.findCorrectDatabaseImplementation(connection); Database database = factory.findCorrectDatabaseImplementation(connection);
return service.queryDatabaseChangeLogTable(database); reports.add(new LiquibaseReport(entry.getKey(),
service.queryDatabaseChangeLogTable(database)));
} }
finally { finally {
connection.close(); connection.close();
@ -67,4 +77,31 @@ public class LiquibaseEndpoint extends AbstractEndpoint<List<Map<String, ?>>> {
} }
} }
return reports;
}
/**
* Liquibase report for one datasource.
*/
public static class LiquibaseReport {
private final String name;
private final List<Map<String, ?>> changeLogs;
public LiquibaseReport(String name, List<Map<String, ?>> changeLogs) {
this.name = name;
this.changeLogs = changeLogs;
}
public String getName() {
return this.name;
}
public List<Map<String, ?>> getChangeLogs() {
return this.changeLogs;
}
}
} }

View File

@ -23,6 +23,8 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import javax.sql.DataSource;
import liquibase.integration.spring.SpringLiquibase; import liquibase.integration.spring.SpringLiquibase;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import org.junit.After; import org.junit.After;
@ -50,6 +52,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionEvaluationRepor
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration; import org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration;
import org.springframework.boot.autoconfigure.info.ProjectInfoProperties; import org.springframework.boot.autoconfigure.info.ProjectInfoProperties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.bind.PropertySourcesBinder; import org.springframework.boot.bind.PropertySourcesBinder;
@ -65,7 +68,6 @@ import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.validation.BindException; import org.springframework.validation.BindException;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link EndpointAutoConfiguration}. * Tests for {@link EndpointAutoConfiguration}.
@ -222,12 +224,13 @@ public class EndpointAutoConfigurationTests {
} }
@Test @Test
public void flywayEndpointIsDisabledWhenThereAreMultipleFlywayBeans() { public void testFlywayEndpointWithMultipleFlywayBeans() {
this.context = new AnnotationConfigApplicationContext(); this.context = new AnnotationConfigApplicationContext();
this.context.register(MultipleFlywayBeansConfig.class, this.context.register(MultipleFlywayBeansConfig.class,
EndpointAutoConfiguration.class); FlywayAutoConfiguration.class, EndpointAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
assertThat(this.context.getBeansOfType(FlywayEndpoint.class)).hasSize(0); assertThat(this.context.getBeansOfType(Flyway.class)).hasSize(2);
assertThat(this.context.getBeansOfType(FlywayEndpoint.class)).hasSize(1);
} }
@Test @Test
@ -242,12 +245,13 @@ public class EndpointAutoConfigurationTests {
} }
@Test @Test
public void liquibaseEndpointIsDisabledWhenThereAreMultipleSpringLiquibaseBeans() { public void testLiquibaseEndpointWithMultipleSpringLiquibaseBeans() {
this.context = new AnnotationConfigApplicationContext(); this.context = new AnnotationConfigApplicationContext();
this.context.register(MultipleLiquibaseBeansConfig.class, this.context.register(MultipleLiquibaseBeansConfig.class,
EndpointAutoConfiguration.class); LiquibaseAutoConfiguration.class, EndpointAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
assertThat(this.context.getBeansOfType(LiquibaseEndpoint.class)).hasSize(0); assertThat(this.context.getBeansOfType(SpringLiquibase.class)).hasSize(2);
assertThat(this.context.getBeansOfType(LiquibaseEndpoint.class)).hasSize(1);
} }
private void load(Class<?>... config) { private void load(Class<?>... config) {
@ -330,32 +334,58 @@ public class EndpointAutoConfigurationTests {
} }
@Configuration static class DataSourceConfig {
static class MultipleFlywayBeansConfig {
@Bean @Bean
Flyway flywayOne() { public DataSource dataSourceOne() {
return mock(Flyway.class); return DataSourceBuilder.create().url("jdbc:hsqldb:mem:changelogdbtest")
.username("sa").build();
} }
@Bean @Bean
Flyway flywayTwo() { public DataSource dataSourceTwo() {
return mock(Flyway.class); return DataSourceBuilder.create().url("jdbc:hsqldb:mem:changelogdbtest2")
.username("sa").build();
} }
} }
@Configuration @Configuration
static class MultipleLiquibaseBeansConfig { static class MultipleFlywayBeansConfig extends DataSourceConfig {
@Bean @Bean
SpringLiquibase liquibaseOne() { public Flyway flywayOne() {
return mock(SpringLiquibase.class); Flyway flyway = new Flyway();
flyway.setDataSource(dataSourceOne());
return flyway;
} }
@Bean @Bean
SpringLiquibase liquibaseTwo() { public Flyway flywayTwo() {
return mock(SpringLiquibase.class); Flyway flyway = new Flyway();
flyway.setDataSource(dataSourceTwo());
return flyway;
}
}
@Configuration
static class MultipleLiquibaseBeansConfig extends DataSourceConfig {
@Bean
public SpringLiquibase liquibaseOne() {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.yaml");
liquibase.setDataSource(dataSourceOne());
return liquibase;
}
@Bean
public SpringLiquibase liquibaseTwo() {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.yaml");
liquibase.setDataSource(dataSourceTwo());
return liquibase;
} }
} }