Skip to content

Improve FailureAnalyzer for embedded datasource #11953

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,72 @@

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;
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
*/
class DataSourceBeanCreationFailureAnalyzer
extends AbstractFailureAnalyzer<DataSourceBeanCreationException> {
extends AbstractFailureAnalyzer<DataSourceBeanCreationException>
implements EnvironmentAware {

private 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);
}

@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}

private FailureAnalysis getFailureAnalysis(DataSourceBeanCreationException cause) {

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.";
}
else {
action = "If you have database settings to be loaded "
+ "from a particular profile you may need to activate it"
+ getActiveProfiles();
}
return new FailureAnalysis(cause.getMessage(), action, 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).");
}
}
return message.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,8 @@
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;

/**
Expand All @@ -47,16 +44,15 @@
* @author Stephane Nicoll
* @author Benedikt Ritter
* @author Eddú Meléndez
* @author Patryk Kostrzewa
* @since 1.1.0
*/
@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.
*/
Expand Down Expand Up @@ -166,11 +162,6 @@ public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}

@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}

@Override
public void afterPropertiesSet() throws Exception {
this.embeddedDatabaseConnection = EmbeddedDatabaseConnection
Expand Down Expand Up @@ -244,8 +235,9 @@ public String determineDriverClassName() {
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;
}
Expand Down Expand Up @@ -290,8 +282,9 @@ public String determineUrl() {
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;
}
Expand Down Expand Up @@ -522,37 +515,17 @@ public void setProperties(Map<String, String> properties) {

static class DataSourceBeanCreationException extends BeanCreationException {

DataSourceBeanCreationException(EmbeddedDatabaseConnection connection,
Environment environment, String property) {
super(getMessage(connection, environment, property));
}
private final EmbeddedDatabaseConnection 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();
DataSourceBeanCreationException(String message,
EmbeddedDatabaseConnection connection) {

super(message);
this.connection = connection;
}

public EmbeddedDatabaseConnection getConnection() {
return this.connection;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,8 @@ public class DataSourceBeanCreationFailureAnalyzerTests {
@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()).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.");
}

private FailureAnalysis performAnalysis(Class<?> configuration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public void determineUrlWithNoEmbeddedSupport() throws Exception {
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();
}

Expand Down