The aim of this post is to show you how, using CDI, you can inject Java properties entries (those in *.properties files) directly into your Java class. This is somewhat similar to the Spring’s @Value annotation I needed in my plain Java EE project.

The whole code can be found on GitHub so feel free to clone it, use it or make any changes you like.

Context

Ok, so basically in a Java EE environment we already have a way to define some properties that are deployment-specific – it’s the <env-entry> element in the descriptor.

So, if you use EJB you have ejb-jar.xml where you can define the <env-entry> element to define key, type and a default value (for more information take a look at David Blevins’ resource.)

You can then refer to these values using @Resource annotation in your EJBs.

If you can, you can also define property using web.xml and its <env-entry> element.

Nevertheless, we want something simpler – with Java EE 6 you don’t have to have web.xml or ejb-jar.xml. Moreover, we want to inject resources not only in the EJBs but in any managed Java class.

We want some CDI magic.

Overall description

We want to use code something like this in our classes:

@Inject
@ConfigurationValue
String myProp;

this dependency should inject a property with key myProp into our annotated class field.

The @Inject annotation is the official javax.inject.Inject one and the @ConfigurationValue is our own custom one we’ll use to distinct classes fields that should have values injected from the properties.

Qualifier

The @ConfigurationValue is pretty straightforward. It is a CDI Qualifier, so it helps us to define a type of injection to apply to it:

package com.piotrnowicki.cdiconfig;

import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;

/**
 * Defines method / variable that should be injected with value read from some arbitrary resource (e.g. from
 * <code>properties</code> file.)
 * 
 * @author Piotr Nowicki
 * 
 */
@Qualifier
@Retention(RUNTIME)
@Target({ TYPE, METHOD, FIELD, PARAMETER })
public @interface ConfigurationValue {
    /**
     * Key that will be searched when injecting the value.
     */
    @Nonbinding
    String value() default "";

    /**
     * Defines if value for the given key must be defined.
     */
    @Nonbinding
    boolean required() default true;
}

Note the @Nonbinding annotations on the attributes. By default, attributes of CDI Qualifier are used to further filter the injected candidate. In this case, we want just to set additional information to the producer method and not to use the binding functionality of the attributes.

Ok, so we have qualifier and a client that uses the value. But who will actually provide us with the appropriate value?

Producer Methods

Now we need to define a producer who will produce the objects we want to inject. In other words, it should look in the properties files and grab the appropriate entry for us.

package com.piotrnowicki.cdiconfig;

import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;

/**
* Produces {@link ConfigurationValue} annotated fields. It's responsible for supporting conversion between
* types (mainly from String to any other type required by the user.)
*
* <p>
* These producers should not be interested where the fields are read from. It's the {@link PropertyResolver}
* who is responsible for the configuration loading.
* </p>
*
* @see PropertyResolver
*
* @author Piotr Nowicki
*
*/
public class ConfigurationValueProducer {

    @Inject
    PropertyResolver resolver;

    /**
* Main producer method - tries to find a property value using following keys:
*
* <ol>
* <li><code>key</code> property of the {@link ConfigurationValue} annotation (if defined but no key is
* found - returns null),</li>
* <li>fully qualified field class name, e.g. <code>eu.awaketech.MyBean.myField</code> (if value is null,
* go along with the last resort),</li>
* <li>field name, e.g. <code>myField</code> for the example above (if the value is null, no can do -
* return null)</li>
* </ol>
*
* @param ip
* @return value of the injected property or null if no value could be found.
*/
    @Produces
    @ConfigurationValue
    public String getStringConfigValue(InjectionPoint ip) {

        String fqn = ip.getMember().getDeclaringClass().getName() + "." + ip.getMember().getName();

        // Trying with explicit key defined on the field
        String key = ip.getAnnotated().getAnnotation(ConfigurationValue.class).value();
        boolean isKeyDefined = !key.trim().isEmpty();

        boolean valueRequired = ip.getAnnotated().getAnnotation(ConfigurationValue.class).required();

        if (isKeyDefined) {
            return resolver.getValue(key);
        }

        // Falling back to fully-qualified field name resolving.
        key = fqn;
        String value = resolver.getValue(fqn);

        // No luck... so perhaps just the field name?
        if (value == null) {
            key = ip.getMember().getName();
            value = resolver.getValue(key);
        }

        // No can do - no value found but you've said it's required.
        if (value == null && valueRequired) {
            throw new IllegalStateException("No value defined for field: " + fqn
                    + " but field was marked as required.");
        }

        return value;
    }

    /**
* Produces {@link Double} type of property from {@link String} type.
*
* <p>
* Will throw {@link NumberFormatException} if the value cannot be parsed into a {@link Double}
* </p>
*
* @param ip
* @return value of the injected property or null if no value could be found.
*
* @see ConfigurationValueProducer#getStringConfigValue(InjectionPoint)
*/
    @Produces
    @ConfigurationValue
    public Double getDoubleConfigValue(InjectionPoint ip) {
        String value = getStringConfigValue(ip);

        return (value != null) ? Double.valueOf(value) : null;
    }

    /**
* Produces {@link Integer} type of property from {@link String} type.
*
* <p>
* Will throw {@link NumberFormatException} if the value cannot be parsed into an {@link Integer}
* </p>
*
* @param ip
* @return value of the injected property or null if no value could be found.
*
* @see ConfigurationValueProducer#getStringConfigValue(InjectionPoint)
*/
    @Produces
    @ConfigurationValue
    public Integer getIntegerConfigValue(InjectionPoint ip) {
        String value = getStringConfigValue(ip);

        return (value != null) ? Integer.valueOf(value) : null;
    }
}

The code and comments should be enough to understand what it’s doing.

Notice the PropertyResolver that is injected in the above code. This class is a CDI singleton bean that is responsible for actually accessing the properties files.

After its initialization, it allows you to fetch a property with given key (you can think of this class as a Map<String, Object> wrapper.) Its actual implementation can be found below:

Property Files Resolver

package com.piotrnowicki.cdiconfig;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.inject.Singleton;

/**
* <p>
* Reads all valid property files within the classpath and prepare them to be fetched.
* </p>
*
* <p>
* This class <strong>can</strong> be accessed concurrently by multiple clients. The inner representation of
* properties <strong>should not</strong> be leaked out; if this is absolutely required, use unmodifiable
* collection.
* </p>
*
* <p>
* This resolver <strong>doesn't pay attention</strong> to multiple properties defined with the same name in
* different files. It's impossible to determine which one will take precedence, so the responsibility for
* name-clash is a deployer concern.
* </p>
*
* @author Piotr Nowicki
*
*/
@Singleton
public class PropertyResolver {

    // TODO: Change it to some hierarchical structure if required.
    Map<String, Object> properties = new HashMap<>();

    /**
* Initializes the properties by reading and uniforming them.
*
* This method is called by the container only. It's not supposed to be invoked by the client directly.
*
* @throws IOException
* in case of any property file access problem
* @throws URISyntaxException
*/
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @PostConstruct
    private void init() throws IOException {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();

        List<File> propertyFiles = getPropertyFiles(cl);

        for (File file : propertyFiles) {
            Properties p = new Properties();
            p.load(new FileInputStream(file));

            // TODO: If required - notify if added key was already present in the map
            properties.putAll(new HashMap<String, Object>((Map) p));
        }
    }

    /**
* Gets flat-file properties files accessible from the root of the given classloader.
*
* @param cl
* classpath to be used when scanning for files.
*
* @return found property files.
*
* @throws IOException
* if there was a problem while accessing resources using the <code>cl</code>.
*/
    List<File> getPropertyFiles(ClassLoader cl) throws IOException {
        List<File> result = new ArrayList<>();

        Enumeration<URL> resources = cl.getResources("");

        while (resources.hasMoreElements()) {
            File resource = getFileFromURL(resources.nextElement());

            File[] files = resource.listFiles(new PropertyFileFilter());
            result.addAll(Arrays.asList(files));
        }

        return result;
    }

    /**
* Converts URL resource to a File. Makes sure that invalid URL characters (e.g. whitespaces) won't
* prevent us from accessing the valid file location.
*
* @param url
* URL to be transformed
*
* @return File pointing to the given <code>url</code>.
*/
    File getFileFromURL(URL url) {
        File result;

        try {
            result = new File(url.toURI());
        } catch (URISyntaxException e) {
            result = new File(url.getPath());
        }

        return result;
    }

    /**
* Returns property held under specified <code>key</code>. If the value is supposed to be of any other
* type than {@link String}, it's up to the client to do appropriate casting.
*
* @param key
* @return value for specified <code>key</code> or null if not defined.
*/
    public String getValue(String key) {
        Object value = properties.get(key);

        return (value != null) ? String.valueOf(value) : null;
    }
}

It scans all *.properties in your classpath root, reads them and combines together. The properties file can have any name.

Property Files Filter

Now the last part that is quite useful in our case is the FileFilter that allows us to list only *.properties files within the root of the classpath. I’ve come up with the following implementation:

package com.piotrnowicki.cdiconfig;

import java.io.File;
import java.io.FileFilter;

/**
* Selects only files that are within <code>WEB-INF</code> directory and have <code>properties</code>
* extension.
*
* @author Piotr Nowicki
*/
public class PropertyFileFilter implements FileFilter {

    @Override
    public boolean accept(File pathname) {

        // TODO: Is this even possible in real-life?
        if (pathname == null) {
            return false;
        }

        // We're not investigating sub-directories
        boolean isDirectory = pathname.isDirectory();

        /*
* FIXME: Change to something more appropriate - user can have a dir named "classes" on the regular
* path - should it also be scanned for 'properties'?
*/
        boolean isWebInfResource = pathname.getAbsolutePath().contains("classes/");

        if (isDirectory || !isWebInfResource) {
            return false;
        }

        String extension = getExtension(pathname.getName());

        if (extension.equals("properties")) {
            return true;
        } else {
            return false;
        }
    }

    /**
* <p>
* Returns filename extension. Returns empty String if no extension is defined. E.g.:
* <ul>
* <li><code>myFile.dat</code>, returns <code>dat</code></li>
* <li><code>myFile.with.dots.properties</code>, returns <code>properties</code></li>
* </ul>
* </p>
*
* <p>
* This method never returns null and is null-argument safe.
* </p>
*
* @param filename
* @return extension of the <code>filename</code> without the trailing dot.
*/
    protected String getExtension(String filename) {
        if (filename == null) {
            return "";
        }

        int lastDotIdx = filename.lastIndexOf(".");

        if (lastDotIdx == -1) {
            return "";
        }

        return filename.substring(lastDotIdx + 1);
    }
}

This class has nothing to do with the CDI itself – it’s just a helper class for listing only interesting to us files.

Unit and Integration Tests

I decided to go with TestNG instead of JUnit because I very like the Data Provider feature of the TestNG. For those of you who are not familiar with this feature, take a look at the exemplary test:

package com.piotrnowicki.cdiconfig;

import static org.fest.assertions.Assertions.assertThat;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import com.piotrnowicki.cdiconfig.PropertyResolver;

public class PropertyResolverTest extends Arquillian {

    @Inject
    PropertyResolver cut;

    @Deployment
    public static Archive<?> createDeployment() throws IOException {
        JavaArchive archive = ShrinkWrap.create(JavaArchive.class).addClass(PropertyResolver.class);

        return archive;
    }

    @Test(dataProvider="getFileFromURLProvider")
    public void getFileFromURL(URL url, File expected) {
        File actual = cut.getFileFromURL(url);

        assertThat(actual).isEqualTo(expected);
    }

    @DataProvider(name = "getFileFromURLProvider")
    Object[][] getFileFromURLProvider() throws MalformedURLException {
        List<Object[]> data = new ArrayList<>();

        data.add(new Object[] { new URL("file:///testMyFile"), new File("/testMyFile") });
        data.add(new Object[] { new URL("file:///myDir/file with whitespaces"), new File("/myDir/file with whitespaces") });
        data.add(new Object[] { new URL("file:///file%20with%20whitespaces"), new File("/file with whitespaces") });

        return data.toArray(new Object[0][0]);
    }

    @Test(dataProvider = "keyProvider")
    public void getValue(String key, String expected) {
        String actual = cut.getValue(key);

        assertThat(actual).isEqualTo(expected);
    }

    @DataProvider(name = "keyProvider")
    Object[][] dataProvider() {
        List<Object[]> data = new ArrayList<>();

        data.add(new Object[] { "myProp", "myVal" });
        data.add(new Object[] { "myProp2", "myVal2" });
        data.add(new Object[] { "myProp3", null });

        return data.toArray(new Object[0][0]);
    }
}

You can define your Data Provider method and use it to generate test data (and expected result in my case) for your test methods.

It allows me to cleanly test method behaviour for different arguments. At the same time, TestNG will save information about each of the test methods invocation – so for Data Provider with 3 different data sets, you’ll get information about 3 executions of a test method in the report (each with different arguments.) It’s quite useful to retain this kind of information.

Second thing that I use very often are fluent assertions. I don’t care if its Hamcrest or FEST. I just love the fluent API and the way you can easily read the test assertions and know exactly what it does.

Last thing about this snippet is that if you want to use Arquillian to conduct your integration tests, you should extend the org.jboss.arquillian.testng.Arquillian class. It’s an equivalent of the @RunWith(Arquillian.class) in the JUnit’s world.

It’s worth mentioning, as it took me a few minutes to dig this information.

Nice thing about using Arquillian in your tests is that you can easily simulate a client code of your component just as done here.

Side notes

Below you can find few remarks about problems I’ve stumbled upon when writing this example.

slf4j Hell

I’ve got some weird SLF4J errors when executing the Arquillian test:

java.lang.NoSuchMethodError: 
    org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;Ljava/lang/Throwable;)V

If you’ve ever used SLF4J with log4j and got this error – you probably know it’s because of the messed up versions of slf4j on your classpath. The point is, I’ve inspected the pom.xml file and there was no conflicting versions (well, there was but between versions 1.6.1 and 1.6.4, however the error I’ve posted above is related with differences between versions 1.5.x and 1.6.x.)

It seemed that my Eclipse project had a Glassfish 3.1.2 facet set and apparently it added slf4j 1.5.x libraries on the classpath. This resulted in those nasty exceptions.

Removing the facet solved the problem.

Java EE API in Maven

Arquillian User Guide is very right about using org.jboss.spec:jboss-javaee-6.0 artifact in your pom.xml instead of the javax:javaee-api.

Don’t think twice – if you plan to use Arquillian – just use the coordinates they posted in the user guide instead of Java EE API with empty method bodies.

Accessing Files in Java EE

I’ve seen discussions about allowance of accessing filesystem from within the Java EE applications. I do believe that this restriction is talking about accessing plain filesystem rather than fetching the resources from the classpath, so I don’t believe it breaks the Java EE specification.

Test it Without Downloading the Sources

This code is mainly for exemplary purposes, but if instead of checking the sources and fiddling with how it’s implemented, you just want to see it running, you can download the jar from here, and put it in your WEB-INF/lib directory or define the following Maven dependency:

<dependency>
    <groupId>com.piotrnowicki</groupId>
    <artifactId>cdiconfig</artifactId>
    <version>0.1</version>
</dependency>

And set my website as a repository for artifacts:

<repository>
    <id>piotrnowicki.com</id>
    <url>http://maven.piotrnowicki.com</url>
</repository>

Either way, remember to make your web application CDI managed by putting beans.xml in your WEB-INF directory!

Did you like this? Share it:

21 thoughts on “Inject Java Properties in Java EE Using CDI

  1. Cool stuff but won’t work on WebSphere 8.0 due to the fact that InjectionPoint is always null there :D Planned to be fixed in 8.0.0.4 http://www-304.ibm.com/support/docview.wss?uid=swg1PM51802

    I was trying to introduce similar features in my current project, but due to this limitations I ended up with bunch of Qualifiers instead of one with proper metadata :D So is WAS 8.0 fully certified Java EE 6 environment or not? :D

    Reply
    • Uh, that’s damn sad… Wonder how the TCK looks like – perhaps it should be much more strict than right now?

      BTW – in overall – do you like using WAS or is it a business constraint you have?

      Reply
      • Are you kidding me? :D I hate it. I’ve never seen such counter productive environment (I worked with WebLogic, JBoss AS, Glassfish and Tomcat). It’s the business choice. No one was ever fired for choosing “the big vendor”.

        Reply
        • Haha, that’s a great corporate decision! ;-)))

          Wonder if this is THE ONLY reason people talk about when choosing “big” app server. More often I can hear complaints about such environments. Do you know if it’s in any way more scalable, has better performance, better support, etc. than JBoss or GF?

          (yammer mode: ON) When will the people that chooses tools for developers will go away and just let us do our jobs? ;-)))

          Reply
  2. Hi Piotr!

    In Apache DeltaSpike we also already have a similar features: @ConfigProperty.

    It’s based on our ConfigResolver approach. We also thought about resolving the effective configuration by using CDI events, but those would not be available at bootstrap and thus you cannot use them in CDI Extensions. The actual injection works in a similar way like in your example.

    LieGrue,
    strub

    https://git-wip-us.apache.org/repos/asf?p=incubator-deltaspike.git;a=tree;f=deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config;h=f6825da6bcad1f193d380cd3c740ff8b59774cc6;hb=HEAD

    Reply
    • Hello Mark!

      That’s great to hear – didn’t know about DeltaSpike until now — quite gnarly stuff you got there! Just wanted to say that it would be great if we could gather some real-life use-cases for Java EE apps and wrap them in one ‘utility’ package making it available for everyone and here it is :-)

      Such use-cases might involve the example showed in this post or even some more specific ones like dynamic query generation, predefined profiles like in Spring 3.1 and so on. +1 for DeltaSpike!

      Cheers!

      Reply
  3. Hi,

    I’m seeing this idea pretty frequently now in CDI demos, but it’s a really strange approach that the name of the injection point should determine which property you’re pulling out. Field/variable names can be refactored for good reasons, and then suddenly the application breaks – which isn’t what anyone expects from simply renaming a variable! So then you end up annotating the fields with strings, which is clumsy again.

    Plus, .property files suck, along with the awful Properties class – no type safety, only scalars.

    A much nicer configuration approach is demonstrated by DropWizard: you define a POJO that contains whatever configuration you need (also lists or maps if you like), and let that be filled in through data binding from a YAML file. This POJO, of course, can be injected nicely. It’s typesafe, it allows you to use more advanced data structured, it can be fail fast (throw a RuntimeException when data binding fails, instead of when a value is used), and YAML in its simplest form is just the same as a properties file, so nothing scary there.

    Reply
    • Hi Marius,

      Thanks for the comment! It’s nice to see another point of view.

      Firstly, the name of the field used as a default property entry key is more about showing how can you use the InjectionPoint object rather than a good-practice example. I decided to put it there, as from what I know in Spring you need to add some Reflection magic to get such information: http://stackoverflow.com/questions/9685316/what-is-the-spring-di-equivalent-of-cdis-injectionpoint
      It’s nice to know what CDI can offer you in this matter.

      Nevertheless, I agree, this is not the greatest solution because of the future maintenance. However, isn’t the solution you pointed, with YAML, struggling the same? Either you can use the field/getter derived name or to specify it as a string value. You get type safety out-of-the-box and it’s great, but you’re still vulnerable of name changes. Or perhaps I missed something here?

      Also note that in the shown example, during the injection definition, you also define that the property should be of a specified type – e.g. a Double – not a String. In case it cannot be properly cast – you’ll also be notified about that.

      You’ve also pointed that “[DropWizard’s approach] (…) can be fail fast (throw a RuntimeException when data binding fails, instead of when a value is used)”.
      In the shown example, if the injected value is required, it will also be checked during the injection. You don’t have to use the injected field to get the exception; but it’s CDI part that gives you that.

      Lastly, most usages of properties (so data that changes from deployment to deployment) I’ve seen were rather simple data. No complex configuration, no nested maps of lists or stuff like that. I guess that’s why it’s so widely used – it’s easy and simple.

      Nevertheless, I have never personally used YAML (and didn’t see it our production applications), but it looks pretty neat, so I’m sure that after reading your comment I will definitely use it in some other example – just for the sake of knowledge and fun! :-)

      So, thanks once again and cheers!

      Reply
  4. Pingback: Članak – Inject Java Properties in Java EE Using CDI «         HUJAK – Hrvatska udruga Java korisnika

  5. Pingback: Inject Java Properties in Java EE using CDI | Zenida's blog

  6. It’s a beauty.

    I use jboss-as-7.1.1.Final on windows.

    Small changes are needed to get it working on windows.

    PropertyFileFilter#accept(File pathname)
    // TODO: Is this even possible in real-life?
    if (pathname == null) {
    return false;
    }

    // We're not investigating sub-directories
    boolean isDirectory = pathname.isDirectory();

    /*
    * FIXME: Change to something more appropriate - user can have a dir
    * named "classes" on the regular path - should it also be scanned for
    * 'properties'?
    */
    <strong>boolean isWebInfResource = pathname.getAbsolutePath().contains(
    "classes" + File.separator);</strong>

    if (isDirectory || !isWebInfResource) {
    return false;
    }

    String extension = getExtension(pathname.getName());

    if (extension.equals("properties")) {
    return true;
    } else {
    return false;
    }

    PropertyResolver#getFileFromURL(URL url)
    File result;
    try {
    result = new File(url.toURI());

    } catch (java.lang.IllegalArgumentException e) {
    try {
    result = new File(url.toURI().toString()
    .replaceAll(“vfs:/”, “”));
    } catch (Exception e2) {
    result = null;
    }

    } catch (URISyntaxException e) {
    result = new File(url.getPath());
    }
    return result;

    Reply
  7. Nice job, but why don’t you include any scope annotation, something like @ApplicationScoped?? Isn’t it necessary?

    Reply
    • Hello Optimus,

      I think you’re referring to ConfigurationValueProducer bean right? You can make it producers an @ApplicationScoped which might be quite reasonable (you will produce values only once for the lifetime of your application) but it’s not required in this case.

      AFAIR the @Dependent pseud-scope is used as the default for the producers and it should work this way.

      Thanks for your input!

      Regards,
      Piotr

      Reply
  8. Very interesting… I’m developing now a project, and I found this post ’cause I’m searching a way to inject some properties I need into a JPA Entity bean. Is it possible to create a field (maybe a @Transient one) for an Entity with the method defined on this post?

    Thank you in advance, and congrats for the post!

    Reply
    • Hello Alex,

      I think it might be possible to do so (AFAIR JPA entities can be treated as CDI beans) but I’m not sure if it’s the right way to do it. You can try it but personally I would prefer to leave configuration data in some configuration bean instead of spreading it across entities.

      Cheers!

      Reply
  9. Hi,

    Consider you are testing (CDI-Unit) a service that has some values loaded from a property file. You would normally put that property resource in test/resources.

    Since that file is there only for testing purposes somewhere in java/resources there should be the main property file that is used by the application.

    Because every property file is loaded keys will be overwritten. Not sure of the order, for my app the java/resources file overwrites the one from test. So I every time I would want to test something I have to update the file from java/resources.

    All in all you cannot provided a context contained property file since they step on each other.

    Is this intended?

    Reply
    • Hello Cristian,

      Of course you’re right – you can’t have any reasonable expectations how the properties will be loaded. Both resources from main/resources and test/resources can be mixed and you’ll end with unpredictable results.

      This post and code are, however, not meant to be a full-blown fits-all solution. It is rather an example how CDI can be used to achieve key-value properties injection.

      Note the comment in the above code:

      // TODO: Change it to some hierarchical structure if required.
      Map<String, Object> properties = new HashMap<>();
      

      This points out that you can modify how properties are loaded by your application. To achieve what you’d like to get (and most probably what is expected to have from such solution) would be to alter the way properties are read, name them accordingly to test or production ones, change the way they are loaded or change the way they are stored.

      Hope this answers your question.

      Thanks for your input!

      Cheers, Piotr

      PS. Of course if you’d like – you can post a push-request to this solution. It’s freely available so any contribution is very warm welcomed!

      Reply
  10. Hi Piotr,

    This is almost exactly what I’m looking for par 1 requirement.
    I need to be able to change some properties at runtime.
    Any idea how I could re-inject or add some sort of Property Change listener? Or is CDI not the right fit for this requirement?

    Cheers,
    DesK

    Reply
  11. Pingback: Inject Properties using CDI | Google Trender

  12. Very Nice solution !
    Just one question – Currently we are reading the property file which is on the project class path, Is it possible to access property file from outside of war/ear for multiple application servers like tomee where we specify the property file path in server environment variable ?
    E.g. – -Dui.servicepoint.configuration.file = “C:\Proj\conf\configuration.properties

    I don’t want to use spring framework here, Just a plain java code.

    Thanks !

    Reply

Leave a reply

required


*

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>