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: