Created
September 18, 2018 15:54
-
-
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is wrong | |
things.something-else=129 | |
# This is missing | |
things.intervall=23 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
things.interval = 4711 | |
things.something-else = 64 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
org.springframework.boot.diagnostics.FailureAnalyzer=\ | |
com.example.propswithdefault.MyFailureAnalyzer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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