Spicy Spring: Using Groovy Configuration As PropertySource
We have many ways to provide configuration properties to a Spring (Boot) application.
We can add our own custom configuration properties format.
For example we can use Groovy's ConfigObject
object to set configuration properties.
We need to read a configuration file using ConfigSlurper
and make it available as a property source for Spring.
We need to implement two classes and add configuration file to support a Groovy configuration file in a Spring application.
First we need to write a class that extends the PropertySource
in the package org.springframework.core.env
.
This class has methods to get property values based on a given key.
There are already some subclasses for specific property sources.
There is for example also a MapPropertySource
class.
We will extend that class for our implementation, because we can pass our flattened ConfigObject
and rely on all existing functionality of the MapPropertySource
class:
// File: src/main/java/mrhaki/spring/configobject/ConfigObjectPropertySource.java
package mrhaki.spring.configobject;
import groovy.util.ConfigObject;
import org.springframework.core.env.MapPropertySource;
/**
* Property source that supports {@link ConfigObject}. The {@link ConfigObject}
* is flattened and all functionality is delegated to the
* {@link MapPropertySource}.
*/
public class ConfigObjectPropertySource extends MapPropertySource {
public ConfigObjectPropertySource(
final String name,
final ConfigObject config) {
super(name, config.flatten());
}
}
Next we must implement a class that loads the configuration file.
The configuration file must be parsed and we must our ConfigObjectPropertySource
so we can use it in our Spring context.
We need to implement the PropertySourceLoader
interface, where we can define the file extensions the loader must be applied for.
Also we override the load
method to load the actual file.
In our example we also add some extra binding variables we can use when we define our Groovy configuration file.
// File: src/main/java/mrhaki/spring/configobject/ConfigObjectPropertySourceLoader.java
package mrhaki.spring.configobject;
import groovy.util.ConfigObject;
import groovy.util.ConfigSlurper;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.util.ClassUtils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class ConfigObjectPropertySourceLoader implements PropertySourceLoader {
/**
* Allowed Groovy file extensions for configuration files.
*
* @return List of extensions for Groovy configuration files.
*/
@Override
public String[] getFileExtensions() {
return new String[]{"groovy", "gvy", "gy", "gsh"};
}
/**
* Load Groovy configuration file with {@link ConfigSlurper}.
*/
@Override
public PropertySource load(
final String name,
final Resource resource,
final String profile) throws IOException {
if (isConfigSlurperAvailable()) {
final ConfigSlurper configSlurper = profile != null ?
new ConfigSlurper(profile) :
new ConfigSlurper();
// Add some extra information that is accessible
// in the Groovy configuration file.
configSlurper.setBinding(createBinding(profile));
final ConfigObject config = configSlurper.parse(resource.getURL());
// Return ConfigObjectPropertySource if configuration
// has key/value pairs.
return config.isEmpty() ?
null :
new ConfigObjectPropertySource(name, config);
}
return null;
}
private boolean isConfigSlurperAvailable() {
return ClassUtils.isPresent("groovy.util.ConfigSlurper", null);
}
private Map createBinding(final String profile) {
final Map bindings = new HashMap<>();
bindings.put("userHome", System.getProperty("user.home"));
bindings.put("appDir", System.getProperty("user.dir"));
bindings.put("springProfile", profile);
return bindings;
}
}
Finally we need to add our ConfigObjectPropertySourceLoader
to a Spring configuration file, so it used in our Spring application.
We need to create the file spring.factories
in the META-INF
directory with the following contents:
# File: src/main/resources/META-INF/spring.factories
org.springframework.boot.env.PropertySourceLoader=\
mrhaki.spring.configobject.ConfigObjectPropertySourceLoader
Now when we create a Spring application with all our classes and configuration file in the classpath we can provide an application.groovy
file in the root of the classpath:
// File: src/main/resources/application.groovy
app {
// Using binding property appDir,
// which is added to the ConfigSlurper
// in ConfigObjectPropertySourceLoader.
startDir = appDir
// Configuration hierarchy.
message {
text = "Text from Groovy configuration!"
}
}
environments {
// When starting application with
// -Dspring.profiles.active=blog this
// environment configuration is used.
blog {
app {
message {
text = "Groovy Goodness"
}
}
}
}
Written with Spring Boot 1.3.5.RELEASE