Configuring applications

Application behavior that cannot be hardcoded but needs to be defined dynamically is realized via configuration. It depends on the application and the nature of the dynamic behavior how the configuration is implemented.

What aspects need to be configurable? Is it sufficient to define configuration files that are already part of the shipped artifact? Does the packaged application need to be configurable from the outside? Is there a requirement for changing behavior during runtime?

Configuration that does not need to change after the application has been built can easily be implemented in the project itself, that is, in the source code. Assuming we require more flexibility.

In a Java environment, the arguably most straightforward way is to provide property files that contain key-value pairs of configuration values. The configured values need to be retrieved in order to be used in the code. It certainly is possible to write Java components that programmatically provide property values. In a Java EE environment, dependency injection will be used to retrieve such components. At the time of writing, no Java EE standard supports out-of-the-box configuration yet. However, using CDI features provide this functionality in a few lines of code. The following shows a possible solution, that enables to inject configuration values identified by keys:

@Stateless
public class CarManufacturer {

    @Inject
    @Config("car.default.color")
    String defaultColor;

    public Car manufactureCar(Specification spec) {
        // use defaultColor
    }
}

In order to unambiguously inject configuration values, for example, provided as strings, qualifier such as @Config are required. This custom qualifier is defined in our application. The goal is to inject values identified by the provided key:

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Config {

    @Nonbinding
    String value();
}

A CDI producer is responsible for retrieving and providing specific configuration values:

import javax.enterprise.inject.spi.InjectionPoint;
import java.io.*;
import java.util.Properties;

@ApplicationScoped
public class ConfigurationExposer {

    private final Properties properties = new Properties();

    @PostConstruct
    private void initProperties() {
        try (InputStream inputStream = ConfigurationExposer.class
                .getResourceAsStream("/application.properties")) {
            properties.load(inputStream);
        } catch (IOException e) {
            throw new IllegalStateException("Could not init configuration", e);
        }
    }

    @Produces
    @Config("")
    public String exposeConfig(InjectionPoint injectionPoint) {
        Config config = injectionPoint.getAnnotated().getAnnotation(Config.class);
        if (config != null)
            return properties.getProperty(config.value());
        return null;
    }
}

The reference key in the @Config annotation is a non-binding attribute since all injected values are handled by our CDI producer method. The InjectionPoint provided by CDI contains information about the location where the dependency injection is specified. The producer retrieves the annotation with the actual configuration key and uses it to look up the configured property. The properties file application.properties is expected to reside in the classpath. This approach comprises configuration values that need to be available at runtime. Since the properties map is initiated once, the values will not change after they have been loaded. The configuration exposer bean is application-scoped to only load the required values into the properties map once.

If a scenario requires changing the configuration at runtime, the producer method would have to reload the configuration file. The scope of the producer method defines the life cycle of the configured value, how often the method will be called.

This example implements configuration using plain Java EE. There are some third-party CDI extensions available that provide similar, as well as more sophisticated, functionality. At the time of writing, an often used example for such a solution is Apache Deltaspike.

Besides the enterprise technology, an important aspect to consider, as well, is the environment in which the container runs; especially, as container technologies set certain constraint on the runtime environment. Chapter 5, Container and Cloud Environments with Java EE covers the topic of modern environments and their impact on the Java EE runtime, including how to design dynamic configuration.

The power of CDI producers lays in their flexibility. Any source of configuration can easily be attached to expose configured values.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.248.162