Skip to content

Instantly share code, notes, and snippets.

@michael-simons
Created September 18, 2018 15:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michael-simons/bf5834b82b9159bf3267c188946d79cf to your computer and use it in GitHub Desktop.
Save michael-simons/bf5834b82b9159bf3267c188946d79cf to your computer and use it in GitHub Desktop.
A quick scribble how to provide defaults for configuration properties in Spring Boot without defaulting. to them in code or condig
# This is wrong
things.something-else=129
# This is missing
things.intervall=23
things.interval = 4711
things.something-else = 64
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.propswithdefault.MyFailureAnalyzer
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>propswithdefault</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>propswithdefault</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>10</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.example.propswithdefault;
import static java.util.stream.Collectors.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotNull;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.bind.BindException;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.validation.BindValidationException;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.origin.Origin;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties(prefix = "things")
@Validated
class MyConfigProperties {
@NotNull
Integer interval;
@Max(128)
Integer somethingElse;
public Integer getInterval() {
return interval;
}
public void setInterval(Integer interval) {
this.interval = interval;
}
public Integer getSomethingElse() {
return somethingElse;
}
public void setSomethingElse(Integer somethingElse) {
this.somethingElse = somethingElse;
}
@Override public String toString() {
return "MyConfigProperties{" +
"interval=" + interval +
'}';
}
}
class MyFailureAnalyzer extends AbstractFailureAnalyzer<BindException> {
String readPlainDefaults() throws IOException {
try (BufferedReader r = new BufferedReader(
new InputStreamReader(PropswithdefaultApplication.class.getResourceAsStream("/defaults.properties")))) {
return r.lines().collect(joining(System.lineSeparator()));
}
}
String readDefaultsViaNewEnvironment() throws IOException {
Properties defaults = new Properties();
defaults.load(PropswithdefaultApplication.class.getResourceAsStream("/defaults.properties"));
// Build a throw-away environment
StandardEnvironment s = new StandardEnvironment() {
@Override protected void customizePropertySources(MutablePropertySources propertySources) {
super.customizePropertySources(propertySources);
// Add all your default property osources
propertySources.addLast(new PropertiesPropertySource("defaults", defaults));
}
};
// Bind to your config as necessary
return Binder.get(s)
.bind("things", MyConfigProperties.class).get()
// And format as desired
.toString();
}
@Override
protected FailureAnalysis analyze(Throwable rootFailure, BindException cause) {
var description = new StringBuilder("Could not bind configuration.").append(System.lineSeparator());
if (cause.getCause() instanceof BindValidationException) {
BindValidationException bindValidationException = (BindValidationException) cause.getCause();
bindValidationException.getValidationErrors().getAllErrors().stream().filter(FieldError.class::isInstance)
.forEach(validationError -> {
FieldError error = (FieldError) validationError;
Origin origin = Origin.from(error);
description.append(String.format("%n Property: %s",
error.getObjectName() + "." + error.getField()));
description.append(String.format("%n Value: %s", error.getRejectedValue()));
if (origin != null) {
description.append(String.format("%n Origin: %s", origin));
}
description.append(System.lineSeparator());
});
}
var action = "Here are some defaults you can use" + System.lineSeparator();
try {
action += readPlainDefaults();
} catch (IOException e) {
e.printStackTrace();
}
return new FailureAnalysis(description.toString(), action, cause);
}
}
@SpringBootApplication
@EnableConfigurationProperties(MyConfigProperties.class)
public class PropswithdefaultApplication {
public static void main(String[] args) {
SpringApplication.run(PropswithdefaultApplication.class, args);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment