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 072ea93810..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 @@ -19,23 +19,75 @@ package org.springframework.boot.autoconfigure.jdbc; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.DataSourceBeanCreationException; import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; import org.springframework.boot.diagnostics.FailureAnalysis; +import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * An {@link AbstractFailureAnalyzer} for failures caused by a * {@link DataSourceBeanCreationException}. * * @author Andy Wilkinson + * @author Patryk Kostrzewa + * @author Stephane Nicoll */ class DataSourceBeanCreationFailureAnalyzer - extends AbstractFailureAnalyzer { + extends AbstractFailureAnalyzer + implements EnvironmentAware { + + private Environment environment; + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } @Override protected FailureAnalysis analyze(Throwable rootFailure, DataSourceBeanCreationException cause) { - String message = cause.getMessage(); - String description = message.substring(0, message.indexOf('.')).trim(); - String action = message.substring(message.indexOf('.') + 1).trim(); - return new FailureAnalysis(description, action, cause); + return getFailureAnalysis(cause); + } + + 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())); + + 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.append(String.format("\tReview the configuration of %s%n.", cause.getConnection())); + } + 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() { + 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 63ff671b9f..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 @@ -32,11 +32,8 @@ import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.DataSourceInitializationMode; import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.env.Environment; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -51,12 +48,10 @@ import org.springframework.util.StringUtils; */ @ConfigurationProperties(prefix = "spring.datasource") public class DataSourceProperties - implements BeanClassLoaderAware, EnvironmentAware, InitializingBean { + implements BeanClassLoaderAware, InitializingBean { private ClassLoader classLoader; - private Environment environment; - /** * Name of the datasource. Default to "testdb" when using an embedded database. */ @@ -166,11 +161,6 @@ public class DataSourceProperties this.classLoader = classLoader; } - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; - } - @Override public void afterPropertiesSet() throws Exception { this.embeddedDatabaseConnection = EmbeddedDatabaseConnection @@ -244,8 +234,9 @@ public class DataSourceProperties driverClassName = this.embeddedDatabaseConnection.getDriverClassName(); } if (!StringUtils.hasText(driverClassName)) { - throw new DataSourceBeanCreationException(this.embeddedDatabaseConnection, - this.environment, "driver class"); + throw new DataSourceBeanCreationException( + "Failed to determine a suitable driver class", + this.embeddedDatabaseConnection); } return driverClassName; } @@ -290,8 +281,9 @@ public class DataSourceProperties String url = (databaseName == null ? null : this.embeddedDatabaseConnection.getUrl(databaseName)); if (!StringUtils.hasText(url)) { - throw new DataSourceBeanCreationException(this.embeddedDatabaseConnection, - this.environment, "url"); + throw new DataSourceBeanCreationException( + "Failed to determine suitable jdbc url", + this.embeddedDatabaseConnection); } return url; } @@ -522,35 +514,16 @@ public class DataSourceProperties static class DataSourceBeanCreationException extends BeanCreationException { - DataSourceBeanCreationException(EmbeddedDatabaseConnection connection, - Environment environment, String property) { - super(getMessage(connection, environment, property)); + private final EmbeddedDatabaseConnection connection; + + DataSourceBeanCreationException(String message, + EmbeddedDatabaseConnection connection) { + super(message); + this.connection = connection; } - private static String getMessage(EmbeddedDatabaseConnection connection, - Environment environment, String property) { - StringBuilder message = new StringBuilder(); - message.append("Cannot determine embedded database " + property - + " for database type " + connection + ". "); - message.append("If you want an embedded database please put a supported " - + "one on the classpath. "); - message.append("If you have database settings to be loaded from a " - + "particular profile you may need to active it"); - if (environment != null) { - String[] profiles = environment.getActiveProfiles(); - if (ObjectUtils.isEmpty(profiles)) { - message.append(" (no profiles are currently active)"); - } - else { - message.append(" (the profiles \"" - + StringUtils.arrayToCommaDelimitedString( - environment.getActiveProfiles()) - + "\" are currently active)"); - - } - } - message.append("."); - return message.toString(); + 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 081d53ef68..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,31 +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( - "Cannot determine embedded database driver class for database type NONE"); - assertThat(failureAnalysis.getAction()).isEqualTo("If you want an embedded " - + "database please put a supported one on the classpath. If you have " - + "database settings to be loaded from a particular profile you may " - + "need to active it (no profiles are currently active)."); + 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) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java index f0d8dcbb9c..d49dc76dd3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java @@ -72,7 +72,7 @@ public class DataSourcePropertiesTests { new FilteredClassLoader("org.h2", "org.apache.derby", "org.hsqldb")); properties.afterPropertiesSet(); this.thrown.expect(DataSourceProperties.DataSourceBeanCreationException.class); - this.thrown.expectMessage("Cannot determine embedded database url"); + this.thrown.expectMessage("Failed to determine suitable jdbc url"); properties.determineUrl(); }