diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/LiquibaseChangelogMissingFailureAnalyzer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/LiquibaseChangelogMissingFailureAnalyzer.java new file mode 100644 index 0000000000..723bf0cc3b --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/LiquibaseChangelogMissingFailureAnalyzer.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2020 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 + * + * https://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.diagnostics.analyzer; + +import java.io.FileNotFoundException; + +import liquibase.exception.ChangeLogParseException; + +import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; +import org.springframework.boot.diagnostics.FailureAnalysis; + +/** + * An {@link AbstractFailureAnalyzer} that analyzes exceptions of type + * {@link ChangeLogParseException} caused by a Liquibase changelog not being present. + * + * @author Sebastiaan Fernandez + */ +class LiquibaseChangelogMissingFailureAnalyzer extends AbstractFailureAnalyzer { + + @Override + protected FailureAnalysis analyze(Throwable rootFailure, ChangeLogParseException cause) { + FileNotFoundException exception = findCause(cause, FileNotFoundException.class); + if (exception != null) { + return new FailureAnalysis(getDescription(cause), + "Make sure a Liquibase changelog is present at the configured path", cause); + } + return null; + } + + private String getDescription(ChangeLogParseException cause) { + return "Liquibase failed to start because no changelog could be found at: " + extractChangelogPath(cause); + } + + private String extractChangelogPath(ChangeLogParseException cause) { + return cause.getMessage().substring("Error parsing ".length()); + } + +} diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories index 8fb578e0f2..2506bdd9a2 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories @@ -56,7 +56,8 @@ org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer org.springframework.boot.diagnostics.analyzer.IncompatibleConfigurationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer,\ -org.springframework.boot.diagnostics.analyzer.PatternParseFailureAnalyzer +org.springframework.boot.diagnostics.analyzer.PatternParseFailureAnalyzer,\ +org.springframework.boot.diagnostics.analyzer.LiquibaseChangelogMissingFailureAnalyzer # FailureAnalysisReporters org.springframework.boot.diagnostics.FailureAnalysisReporter=\ diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/LiquibaseChangelogMissingFailureAnalyzerTest.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/LiquibaseChangelogMissingFailureAnalyzerTest.java new file mode 100644 index 0000000000..cfb2021465 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/LiquibaseChangelogMissingFailureAnalyzerTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2012-2020 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 + * + * https://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.diagnostics.analyzer; + +import javax.sql.DataSource; + +import liquibase.integration.spring.SpringLiquibase; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.boot.diagnostics.FailureAnalysis; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link LiquibaseChangelogMissingFailureAnalyzer} + * + * @author Sebastiaan Fernandez + */ +class LiquibaseChangelogMissingFailureAnalyzerTest { + + @Test + void changelogParseExceptionDueToChangelogNotPresent() { + FailureAnalysis analysis = performAnalysis(); + assertThat(analysis.getDescription()) + .isEqualTo("Liquibase failed to start because no changelog could be found at: " + + "classpath:/db/changelog/db.changelog-master.yaml"); + assertThat(analysis.getAction()).isEqualTo("Make sure a Liquibase changelog is present at the configured path"); + } + + private FailureAnalysis performAnalysis() { + BeanCreationException failure = createFailure(); + assertThat(failure).isNotNull(); + return new LiquibaseChangelogMissingFailureAnalyzer().analyze(failure); + } + + private BeanCreationException createFailure() { + try { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(LiquibaseConfiguration.class); + context.refresh(); + context.close(); + return null; + } + catch (BeanCreationException ex) { + return ex; + } + } + + @Configuration(proxyBeanMethods = false) + static class LiquibaseConfiguration { + + @Bean + DataSource dataSource() { + return DataSourceBuilder.create().url("jdbc:hsqldb:mem:normal").username("sa").build(); + } + + @Bean + SpringLiquibase springLiquibase(DataSource dataSource) { + SpringLiquibase liquibase = new SpringLiquibase(); + liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.yaml"); + liquibase.setShouldRun(true); + liquibase.setDataSource(dataSource); + return liquibase; + } + + } + +}