Java: Using key names from properties file without using hardcoded strings as keys

6.2k views Asked by At

Problem statement

I have a properties file that's accessed throughout my java project.

Example contents of my .properties file:

appName=MyApp
appType=TypeA

Let's say I access the single property, appName, all throughout my java project

props.getProperty("appName");

I don't want to iterate through the properties file to get the property value; I'm just simply getting a single property value from the properties file. But I don't like the fact that I have to access the property by using a hardcoded string because it can lead to maintenance issues (i.e. changing all instances of the hardcoded string).

My current approach

In my current approach, I have a utility class that creates static final variables representing the key names in the properties file, and then I use that variable to access the property value:

public static final String APP_NAME = "appName";
...
props.getProperty(APP_NAME);

But this seems like overkill because it's being redundant, and still a potential maintenance concern. The key already exists in the properties file, and I'm declaring them again in my utility class.

Is there a more "maintenance-free" way of accessing the key name in my code when using the get methods to access property values?

3

There are 3 answers

0
Tim Boudreau On BEST ANSWER

No, you're doing it right. And it's actually fairly maintenance-free.

Using Java Enums would be slightly preferable - i.e.

public class PropertiesWrapper {
   private final Properties props;
   ...
   public String get(MyEnum key) {  return props.get(key.toString());
}

The reason is that even if you make that String a constant, you can never change it without recompiling all code which uses the constant - because the compiler will replace the constant with "appName" at compile-time.

If you use enums and remove an enum constant, code will still need recompiling, but it won't appear to be fine when it's actually now asking for the wrong thing. Also, by using toString() instead of name() to get the property name, you are free to override toString() in your enum to return something different than the constant name.

The down-side of using enums is that the system can't look up anything which was not known at compile-time without some alternate way to access the Properties.

2
Daniel Kaplan On

There might be a library out there that will read a properties file and generate it into a source file with getters on it. You'd then have to compile that source file with your code. That would be a pretty nifty library. But, if this doesn't exist, I don't think there's any other way to do this.

Even if it exists, I don't see how it would be able to know that key1 is a String and key2 is an Integer. You'd still probably have to cast somewhere. That, or maintain a separate metadata file and then you're back to more maintenance.

The problem is, you can change the property file keys at any time and the compiler has no way of knowing you did it.

The best I can give you are libraries meant for reading configuration files. Check out the Apache Configuration library.

0
Janus Varmarken On

A colleague and I are facing a very similar issue at the moment, and we did some research to see if this could be solved using reflection. Unfortunately, we did not manage to solve it, but I will try to elaborate on our problem and strategy. Perhaps someone clever can use our baseline to build something that overcomes the limitations that set us back.

Problem statement: We want to read values in a properties file and assign these values to fields in a Java object (a settings object). The key names do not matter for us and as such it would be optimal to simply use the names of the Java fields as the key names.

This would bring two great benefits:

  1. First, it would allow us to remove a lot of redundant string constants.
  2. Second, it would allow us to define field assignment in a single place as our settings classes are implemented using inheritance.

We planned on defining a method in the super class that would use reflection to get all instance fields. Onward from here, our strategy would be to loop through all fields and:

  1. get the name the field,
  2. use this name to look up a property in the properties file,
  3. get the type of the field and use this type to convert the string value read from the properties file and subsequently assign it to the current field.

Using this approach, one would be able to add a new property simply by adding a new instance field. No extra code for reading nor writing the new property would be required.

Unfortunately this is not feasible due to two issues:

  1. The Java compiler can remove information about field names, see this answer.
  2. Depending on the JVM, using Class#getDeclaredFields() might deny access.