DeltaSpike Configuration: auto registration of CDI ConfigSources
DeltaSpike configuration provides this API:
@ApplicationScoped
public class MyConfig{
@Inject
@ConfigProperty(name = "app.weather.url")
private String entry;
public String getEntry() {
return entry;
}
}
Of course, it supports standard basic types needed for a configuration solution and the values are read from a ConfigSource chain. A configSource is basically a value provider looked-up from a key.
This allows you to register your enterprise source (this awesome Oracle database) and use a system properties source for testing which is far easier to setup on your machine :)
DeltaSpike provides some abstract classes to make writing a ConfigSource easy. In this post I will use MapConfigSource which basically allows you to use the source as a map, and that is something great for a key/value logic.
Here is one implementation:
public static class ConfigRepo extends MapConfigSource {
public ConfigRepo() {
super(new ConcurrentHashMap<>());
}
@Override
public String getConfigName() {
return "blog-config";
}
}
To register it, you can use standard JavaSE SPI mecanism (META-INF/services/org.apache.deltaspike.core.spi.config.ConfigSource) or call org.apache.deltaspike.core.api.config.ConfigResolver.addConfigSources(sources).
DeltaSpike config is usable and is used in Extension, that is why it has this ConfigResolver “JavaSE” API. However, if you don’t care about that part - which is quite common for application configuration where you mainly only care about getting injected values in business services - you can enhance this API.
@Source or mark our ConfigSource
First of all, we will create a @Source API to mark our ConfigSources:
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD})
public @interface Source {
}
Register @Source ConfigSources
Then we will write a small CDI extension (don’t forget to register it putting its fully qualified name in META-INF/services/javax.enterprise.inject.spi.Extension) capturing beans with this annotation and adding them to DeltaSpike once extensions are started:
public class SourceExtension implements Extension {
private final Collection<Bean<?>> sources = new ArrayList<>();
void findConfigSources(@Observes final ProcessBean<?> bean, final BeanManager bm) {
if (!bean.getAnnotated().isAnnotationPresent(Source.class)) {
return;
}
if (!bm.isNormalScope(bean.getBean().getScope())) {
throw new IllegalArgumentException("Only normal scoped ConfigSource can be decorated with @Source: " + bean.getBean());
}
sources.add(bean.getBean());
}
void registerSources(@Observes final AfterDeploymentValidation adv, final BeanManager bm) {
ConfigResolver.addConfigSources(
sources.stream()
.map(b -> ConfigSource.class.cast(bm.getReference(b, ConfigSource.class, bm.createCreationalContext(null))))
.collect(toList()));
}
public Collection<Bean<?>> getBeans() {
return beans;
}
}
Note: if you dig into the implementation you will see that I didn’t handle @Dependent. There is no blocker to do so, but simply don’t forget to release the creational context during BeforeShutdown event. For this post - or even in real life ;) - it wouldn’t have brought much.
So, this extension :
- captures all @Source beans (findConfigSources())
- then, once the extensions validated (AfterDeploymentValidation), make a lookup of these instances in CDI context
- and finally adds them to the ConfigResolver to allow CDI to use them.
Managing the configuration
What does that mean? It means that from this point we can use a CDI ConfigSource. In other words, you can consider your ConfigSource as a ConfigRepository and inject it everywhere in your CDI application:
public class ConfigCrud {
@Inject
private ConfigRepo config;
@Test
public void update(final String newUrl) {
config.getProperties()
.put("app.weather.url", newUrl);
}
}
Of course the new value will not be used if you already instantiated an @ApplicationScoped bean. But for @RequestScoped beans, it will work and you will get dynamic configuration. So in about 50 lines of code you can handle your configuration like any EE code (@PersistenceContext usage is just one step forward).
What’s next ?
Next step will be to allow @ApplicationScoped instances to reload the configuration. This way, you will get a complete and dynamic configuration solution:
- Configuration API through @ConfigProperty
- Advanced and easy configuration management through your ConfigSource
- Dynamic configuration through this reload mechanism.
I will deal with this reloading in the next article and show you that reloading the configuration doesn’t mean listening for it in all configuration consumers :)
From the same author:
In the same category:

