Why this post?
The basics shown by Adam are really smart. You simply
@Inject int yourConfigVariable;
and you are done. You don't have to care about properties or other configuration classes. But looking into it, you see, that you somehow need to fill your configuration from somewhere. And looking back at Antonio's post, you see that you have a lot of options doing this. The one we are most comfortable with is probably Java's Properties mechanism. Using this in combination with the code presented by Adam you end up having a Configuration.properties with an endless list of single word keys. That's not what I would call maintainable. So basically this is why the post has the title: "Putting Bien into practice" ..oO(sorry for that, Adam!) :-) Here are my approaches to the problem.
Fill your configuration from a properties file
The most basic part is to add a Configuration.properties file to your application (default package). Now we are going to modify the configuration holder a bit to make it a Properties type. Now modify Adam's fetchConfiguration() method to load it.
private Properties configData; @PostConstruct public void fetchConfiguration() { String fileName = "Configuration.properties"; configData = loadPropertiesFromClasspath(fileName); } /** * Load properties file from classpath with Java 7 :-) * @param fileName * @return properties */ public static Properties loadPropertiesFromClasspath(String fileName) { try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream( fileName)) { if (in != null) { props = new Properties(); props.load(in); } } catch (IOException ioe) { log.debug("Can't load properties.", ioe); }
Now you have to modify the @Producer methods accordingly. I'm only showing the getString() method here to show you the concept:
/** * Get a String property * @param point * @return String */ @Produces public String getString(InjectionPoint point) { String propertyPath = point.getMember().getDeclaringClass().getName()+ "."; String propertyName = point.getMember().getName(); String propertyValue = configData.getProperty(propertyPath+propertyName); return (propertyValue == null) ? "" : propertyValue; }For convenience reasons I added the name of the declaring class as propertyPath in order to have a bit more order within your property file. You use the producer methods as Adam has shown:
package net.eisele.configuration; public class HitsFlushTimer { @Inject private String hitsFlushRate; }
In this case you end up accessing a property with the key net.eisele.configuration.HitsFlushTimer.hitsFlushRate in your Configuration.properties file. One quick warning. If it happens to you, that you have to package separate ejb and war modules within an ear you probably need the javax.annotation.security.PermitAll annotation at your Configuration singleton.
Then you end up with lots of duplicates
That's probably true. If you have the same configuration over an over again (e.g. httpProxy) this would force you to have the same value for different keys in your property file. The solution seems straight forward. We need our own Qualifier for that. Let's go:
@Retention(RUNTIME) @Target({FIELD, METHOD}) @Qualifier public @interface AppProperty { @Nonbinding public String value(); }
Now we have our own Qualifier for that. Next is to change the @Producer accordingly:
@Produces @AppProperty("") public String getString(InjectionPoint point) { String property = point.getAnnotated().getAnnotation(AppProperty.class).value(); String valueForFieldName = configData.getProperty(property); return (valueForFieldName == null) ? "" : valueForFieldName; }
That's it. Now you can use something like this wherever you like:
@Inject @AppProperty("net.eisele.configuration.test2") String test2;
I know, this isn't half that elegant like Adam's one @Inject annotation. But:You don't have to guess a lot to see what's happening and where your value is coming from. I consider this a big pro in projects with more than one developer.
Yeah. Still not very maintainable.
Ok. I know. You are still talking about refactoring property names. Right? What is left to do? You could think about using a CKey Enum which encapsulates all your property keys and use this instead of simply using the keys itself. But, I would prefer to simply use the plain String keys within my code. Happy configuring now. How are you configuring your applications? Let me know! Happy to receive comments :)