Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to switch from a hardcoded static config file to a .properties file?

I have some code that uses a class holding a huge lot of hardcoded constans. This is what it looks like:

class Constants{
    public static final String name1 = "value1";
    public static final String name2 = "value2";
    public static final Integer value3 = 3;
    ... and so on
}

These constants are used everywhere in the code like Constants.name1.

What I need to do now is to make it possible to specify values for these constants in a configuration file, probably a *.properties file.

My question is: what is the best way to do it, having to rewrite as little code as possible?

I thought of using a single configuration class which reads properties from file when instantiated, but then I'll have to replace all the static calls of values to calls to an instance of that class and I'll have to alter existing methods to pass this config instance into them. Is there a better way?

like image 537
svz Avatar asked Jul 23 '13 11:07

svz


People also ask

How do I create a .properties file?

Create a properties fileRight-click and select Add New Properties File. A new properties file will be added to your project. The new file will be selected and highlighted in the list. Type a name for your properties file, for example, "Properties".

What does ${ mean in a properties file?

The properties files are processed in the order in which they appear on the command line. Each properties file can refer to properties that have already been defined by a previously processed properties file, using ${varname} syntax.


3 Answers

Here is a piece of a code I have used in the past - that can be adapted to your example:

public enum Configuration {

    PROPERTY1("property1.name", "default_value_1"),
    PROPERTY2("property2.name", "default_value_2");

    private final String key;
    private String defaultValue;

    Configuration(String key) {
        this(key, NA);
    }

    Configuration(String key, String defaultValue) {
        this.key = key;
        this.defaultValue = defaultValue;
    }
    private final static Logger logger = LoggerFactory.getLogger(Configuration.class);
    private final static String NA = "n.a.";
    private final static String CONFIG_FILE = "properties/config.properties";
    private final static String NOT_A_VALID_KEY = "Not a valid property key";
    private final static Map<Configuration, String> configuration = new EnumMap<>(Configuration.class);

    static {
        readConfigurationFrom(CONFIG_FILE);
    }

    private static void readConfigurationFrom(String fileName) {
        logger.info("Reading resource: {}", fileName);
        try (InputStream resource = Configuration.class.getClassLoader().getResourceAsStream(fileName);) {
            Properties properties = new Properties();
            properties.load(resource); //throws a NPE if resource not founds
            for (String key : properties.stringPropertyNames()) {
                configuration.put(getConfigurationKey(key), properties.getProperty(key));
            }
        } catch (IllegalArgumentException | IOException | NullPointerException e) {
            logger.error("Error while reading the properties file {}", fileName, e);
            populateDefaultValues();
        }
    }

    private static Configuration getConfigurationKey(String key) {
        for (Configuration c : values()) {
            if (c.key.equals(key)) {
                return c;
            }
        }
        throw new IllegalArgumentException(NOT_A_VALID_KEY + ": " + key);
    }

    private static void populateDefaultValues() {
        for (Configuration c : values()) {
            configuration.put(c, c.defaultValue);
        }
    }

    /**
     * @return the property corresponding to the key or null if not found
     */
    public String get() {
        return configuration.get(this);
    }
}
like image 158
assylias Avatar answered Oct 15 '22 19:10

assylias


Load the properties from the file using Properties.load(...) and assign the constants from those properties.

class Constants{
  public static final String name1; 
  public static final String name2;
  public static final Integer value3;

  static{
    Properties p = new Properties();
    try ( FileInputStream stream = new FileInputStream( new File("path/to/file.properties") )) {          
      p.load( stream );
    }catch( Exception e ){
      //handle exceptions
    }

    name1 = p.getProperty( "name1" );
    name2 = p.getProperty( "name2" );
    value3 = Integer.valueOf( p.getProperty( "value3" ) );
} 

Note that this is just a quick and dirty solution and makes a lot of assumptions. It would be better to handle the individual exceptions and you'd also have to handle the NumberFormatException that might be thrown by Integer.valueOf(...) if the configuration is either empty or not a number.

Another note: you might also try and use some non-static or at least non-final configuration in order to be able to change properties at runtime.

Edit: I added autoclose for the the stream, but note that prior to Java 7 you'd have to handle that yourself.

like image 20
Thomas Avatar answered Oct 15 '22 21:10

Thomas


As a quick hack, you can read the properties file in the static initializer of the class, then you don't have to change the static nature of the class fields immediately, but I suggest you do that anyway over time.

Create a new class that holds all of your old constant values. From now on, inject this configuration object into your new code.

class NewConstants {
    public final String name1;
    public final String name2;
    public final Integer value3;
    ... and so on

   public NewConstants ( Properties props )
   {
       name1 = props.getProperty( "name1" );
       ...
   }
}

Now refactor your old Constants class

class Constants (
    public static final String name1;
    public static final String name2;
    public static final Integer value3;

    static {
        Properties props = new Poperties( );
        props.load( ... );

        NewConstants newConstants = new NewConstants( props );

        name1 = newConstants.getName1( );
        name2 = newConstants.getName2( );

        ...
    }
}
like image 28
Alexander Pogrebnyak Avatar answered Oct 15 '22 21:10

Alexander Pogrebnyak