Fork me on GitHub

Programming, Internet & more

Injecting properties in Java EE applications

In almost any application there are some settings that must be read from somewhere to configure the application. User names or IP addresses are great examples for such settings.
To use settings is the standard procedure to make software configurable. There are many possibilities that one can use to achieve this. One example would be to store such settings in a database. Another one, probably the most simple one, is to read settings from a file.

To make things really simple lets focus on storing the settings in a file. If you’re building a Java EE application you can make use of dependency injection with CDI.
CDI makes it actually really simple to create a provider class for your configuration files. The key is the @Produces keyword which will be looked up at runtime and injects the result of the @Produces method into other CDI enabled classes.

@Produces
public Properties provideServerProperties() {
    Properties p = readPropertiesFromFile("myfile.properties");
    return p;
}

Generic approach with annotations

The next level to this and a more generic approach is to use a dedicated annotation that can be used to mark injection points and also support multiple configuration files. I’m using an annotation called PropertiesFromFile with one property which determines the configuration file which should be used. The name of the configuration file is optional and if not provided, a file named config.properties will be used.

@Qualifier
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PropertiesFromFile {

    /**
     * This value must be a properties file in the classpath.
     */

    @Nonbinding
    String value() default "config.properties";
}

Please note that the configuration files need to be on the classpath of the application. If you’re using maven this can be achieved by putting the files in the path src\main\resources.

To use the new annotation the producer class needs to be adapted.

@Dependent
public class PropertyReader {

    @Produces
    @PropertiesFromFile
    public Properties provideServerProperties(InjectionPoint ip) {
        //get filename from annotation
        String filename = ip.getAnnotated().getAnnotation(PropertiesFromFile.class).value();
        return readProperties(filename);
    }
   
    private Properties readProperties(String fileInClasspath) {
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileInClasspath);
       
        try {
            Properties properties = new Properties();
            properties.load(is);
            return properties;
        } catch (IOException e) {
            System.err.println("Could not read properties from file " + fileInClasspath + " in classpath. " + e);
        }
       
        return null;
    }
}

At runtime when an annotation of type @PropertiesFromFile is found, CDI will look for the corresponding producer. If a found, the producer method will be called and the InjectionPoint will be used as parameter. In the producer method the filename is read from the annoation, the corresponding properties file will be loaded from the classpath and the properties will be returned.

Injecting the properties at runtime

To inject the properties it is sufficient to use the @PropertiesFromFile annotation together with @Inject in any CDI managed class.

public class StartupManager {
   
    @Inject
    @PropertiesFromFile("custom.properties")
    Properties customProperties;
}

In the class above the properties of the file custom.properties will be injected at runtime.

Summary

The usage of a custom annotation and a producer class makes it very easy to inject complex objects into other CDI managed classes at runtime. With only one simple annotation it is possible to get a lot of work done in the background and abstract repeating and non domain-specific logic away. With the above solution one can very simply inject properties from settings files into arbitrary classes.

Of course reading properties from a file is a really simple solution for configuration management but in very small applications it might be sufficient and could come in handy.

A complete working example can be found on GitHub. You can look at the log file of the application server while deploying the sample application as the properties will be printed into the log file.

In addition there is a basic servlet available at http://localhost:8080/javaee-classpath-properties/ which will print out the properties of config.properties.

Category: javaee, programming
-->

One Comment

  1. Marcelis
    Posted November 20, 2015 at 10:10 | Permalink

    I tested your solution i got the following error when i deploy it on JBoss.
    Could you help me ?
    Thank you very much.

    10:10:03,756 ERROR – MSC service thread 1-7 – – org.jboss.msc.service.fail – MSC00001: Failed to start service jboss.deployment.unit.”communicationChannelEAR.ear”.WeldService: org.jboss.msc.service.StartException in service jboss.deployment.unit.”communicationChannelEAR.ear”.WeldService: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [Properties] with qualifiers [@Default] at injection point [[field] @Inject private be.fgov.health.communicationChannel.service.impl.EBoxServiceImpl.prop]
    at org.jboss.as.weld.services.WeldService.start(WeldService.java:83)
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1811) [jboss-msc-1.0.2.GA.jar:1.0.2.GA]
    at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1746) [jboss-msc-1.0.2.GA.jar:1.0.2.GA]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_75]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_75]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_75]
    Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [Properties] with qualifiers [@Default] at injection point [[field] @Inject private be.fgov.health.communicationChannel.service.impl.EBoxServiceImpl.prop]
    at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:311)
    at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280)
    at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:143)
    at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:163)
    at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:382)
    at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:367)
    at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:379)
    at org.jboss.as.weld.WeldContainer.start(WeldContainer.java:83)
    at org.jboss.as.weld.services.WeldService.start(WeldService.java:76)
    … 5 more

Post a Comment

Your email is kept private. Required fields are marked *

*
*