diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzer.java index ac38b0da13..c6121e12f3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzer.java @@ -16,8 +16,6 @@ package org.springframework.boot.autoconfigure.jdbc; -import java.util.Objects; - import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.DataSourceBeanCreationException; import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; import org.springframework.boot.diagnostics.FailureAnalysis; @@ -33,6 +31,7 @@ import org.springframework.util.StringUtils; * * @author Andy Wilkinson * @author Patryk Kostrzewa + * @author Stephane Nicoll */ class DataSourceBeanCreationFailureAnalyzer extends AbstractFailureAnalyzer @@ -40,48 +39,55 @@ class DataSourceBeanCreationFailureAnalyzer private Environment environment; + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + @Override protected FailureAnalysis analyze(Throwable rootFailure, DataSourceBeanCreationException cause) { return getFailureAnalysis(cause); } - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; - } - private FailureAnalysis getFailureAnalysis(DataSourceBeanCreationException cause) { + StringBuilder description = new StringBuilder(); + boolean datasourceUrlSpecified = this.environment.containsProperty( + "spring.datasource.url"); + description.append("Failed to auto-configure a DataSource: "); + if (!datasourceUrlSpecified) { + description.append("'spring.datasource.url' is not specified and "); + } + description.append(String.format( + "no embedded datasource could be auto-configured.%n")); + description.append(String.format("%nReason: %s%n", cause.getMessage())); - final EmbeddedDatabaseConnection connection = cause.getConnection(); - final String action; - - if (EmbeddedDatabaseConnection.NONE == connection) { - action = "If you want an embedded database " - + "please put a supported one on the classpath."; + StringBuilder action = new StringBuilder(); + action.append(String.format("Consider the following:%n")); + if (EmbeddedDatabaseConnection.NONE == cause.getConnection()) { + action.append(String.format("\tIf you want an embedded database (H2, HSQL or " + + "Derby), please put it on the classpath.%n")); } else { - action = "If you have database settings to be loaded " - + "from a particular profile you may need to activate it" - + getActiveProfiles(); + action.append(String.format("\tReview the configuration of %s%n.", cause.getConnection())); } - return new FailureAnalysis(cause.getMessage(), action, cause); + action.append("\tIf you have database settings to be loaded from a particular " + + "profile you may need to activate it").append(getActiveProfiles()); + return new FailureAnalysis(description.toString(), action.toString(), cause); } private String getActiveProfiles() { - - final StringBuilder message = new StringBuilder(); - if (Objects.nonNull(this.environment)) { - String[] profiles = this.environment.getActiveProfiles(); - if (ObjectUtils.isEmpty(profiles)) { - message.append(" (no profiles are currently active)."); - } - else { - message.append(" (the profiles "); - message.append(StringUtils.arrayToCommaDelimitedString(profiles)); - message.append(" are currently active)."); - } + StringBuilder message = new StringBuilder(); + String[] profiles = this.environment.getActiveProfiles(); + if (ObjectUtils.isEmpty(profiles)) { + message.append(" (no profiles are currently active)."); + } + else { + message.append(" (the profiles "); + message.append(StringUtils.arrayToCommaDelimitedString(profiles)); + message.append(" are currently active)."); } return message.toString(); } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java index 81651f3d30..b09c421e6f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java @@ -44,7 +44,6 @@ import org.springframework.util.StringUtils; * @author Stephane Nicoll * @author Benedikt Ritter * @author Eddú Meléndez - * @author Patryk Kostrzewa * @since 1.1.0 */ @ConfigurationProperties(prefix = "spring.datasource") @@ -519,7 +518,6 @@ public class DataSourceProperties DataSourceBeanCreationException(String message, EmbeddedDatabaseConnection connection) { - super(message); this.connection = connection; } @@ -527,5 +525,7 @@ public class DataSourceProperties public EmbeddedDatabaseConnection getConnection() { return this.connection; } + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzerTests.java index a3bce95f31..a8d382b256 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -26,6 +26,7 @@ import org.springframework.boot.testsupport.runner.classpath.ClassPathExclusions import org.springframework.boot.testsupport.runner.classpath.ModifiedClassPathRunner; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; +import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; @@ -33,27 +34,49 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link DataSourceBeanCreationFailureAnalyzer}. * * @author Andy Wilkinson + * @author Stephane Nicoll */ @RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions({ "h2-*.jar", "hsqldb-*.jar" }) public class DataSourceBeanCreationFailureAnalyzerTests { + private final MockEnvironment environment = new MockEnvironment(); + @Test public void failureAnalysisIsPerformed() { FailureAnalysis failureAnalysis = performAnalysis(TestConfiguration.class); - assertThat(failureAnalysis.getDescription()).isEqualTo("Failed to determine a suitable driver class"); - assertThat(failureAnalysis.getAction()).isEqualTo("If you want an embedded database please put a supported one on the classpath."); + assertThat(failureAnalysis.getDescription()).contains( + "'spring.datasource.url' is not specified", + "no embedded datasource could be auto-configured", + "Failed to determine a suitable driver class"); + assertThat(failureAnalysis.getAction()).contains( + "If you want an embedded database (H2, HSQL or Derby), please put it on the classpath", + "If you have database settings to be loaded from a particular profile you may need to activate it", + "(no profiles are currently active)"); + } + + @Test + public void failureAnalysisIsPerformedWithActiveProfiles() { + this.environment.setActiveProfiles("first", "second"); + FailureAnalysis failureAnalysis = performAnalysis(TestConfiguration.class); + assertThat(failureAnalysis.getAction()).contains( + "(the profiles first,¬second are currently active)"); } private FailureAnalysis performAnalysis(Class configuration) { BeanCreationException failure = createFailure(configuration); assertThat(failure).isNotNull(); - return new DataSourceBeanCreationFailureAnalyzer().analyze(failure); + DataSourceBeanCreationFailureAnalyzer failureAnalyzer = new DataSourceBeanCreationFailureAnalyzer(); + failureAnalyzer.setEnvironment(this.environment); + return failureAnalyzer.analyze(failure); } private BeanCreationException createFailure(Class configuration) { try { - new AnnotationConfigApplicationContext(configuration).close(); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.setEnvironment(this.environment); + context.register(configuration); + context.refresh(); return null; } catch (BeanCreationException ex) {