Java properties containing dollar delimited variables

6.7k views Asked by At

I'm storing my app settings in properties file that I use in Ant and in the Java app. Maybe it's not good pratice, but I find it very handy to avoid duplications. The file contains variables such as:

usefulstuff.dir = ${user.home}/usefulstuff

So that other people can run the program on *nix systems, provided that they have the usefulstuff folder in their home directory.

Now, the fascinating thing is that this properties file works fine in Ant (the variable gets resolved to /home/username), while when I load the same file directly in the Java app, I get a string containing ${user.home}/usefulstuff, which is not very useful indeed.

I load the props with this code in Ant:

   <loadproperties srcFile="myProps.properties"/>

And in the Java app:

    FileInputStream ins = new FileInputStream(propFilePath);
    myProps.load(ins);
    ins.close();

Am I missing anything? Maybe is there a better way to load properties in a Java app than load()?

2

There are 2 answers

0
Jon Skeet On BEST ANSWER

I don't think it's particularly "fascinating" that this works in Ant - Ant is deliberately written to do so:

Properties are key-value-pairs where Apache Ant tries to expand ${key} to value at runtime.

and

Ant provides access to all system properties as if they had been defined using a <property> task. For example, ${os.name} expands to the name of the operating system.

If you want the same behaviour, you'll need to implement the same sort of logic. It's possible that you could use the classes from Ant directly, if they do what you want - and if you're happy to ship the relevant binaries (and abide by the licence).

Otherwise, you might want to use a regular expression to find all the matches - or (probably simpler) iterate over all of the system properties and do a simple replacement on them.

0
Binil Thomas On

As Jon said, it should be straighforward to write the property handling yourself. For eg:

import java.util.*;

public class PropertiesTest
{
    public static void main(String[] args)
    {
        Properties props = new Properties();
        props.setProperty("foo", "foo/${os.name}/baz/${os.version}");
        props.setProperty("bar", "bar/${user.country}/baz/${user.country}");

        System.out.println("BEFORE:");
        printProperties(props);

        resolveSystemProperties(props);

        System.out.println("\n\nAFTER:");
        printProperties(props);
    }

    static void resolveSystemProperties(Properties props)
    {
        Map<String, String> sysProps = readSystemProperties();
        Set<String> sysPropRefs = sysProps.keySet();

        Enumeration names = props.propertyNames();
        while (names.hasMoreElements())
        {
            String name = (String) names.nextElement();
            String value = props.getProperty(name);
            for (String ref : sysPropRefs)
            {
                if (value.contains(ref))
                {
                    value = value.replace(ref, sysProps.get(ref));
                }
            }
            props.setProperty(name, value);
        }
    }

    static Map<String, String> readSystemProperties()
    {
        Properties props = System.getProperties();
        Map<String, String> propsMap = 
            new HashMap<String, String>(props.size());

        Enumeration names = props.propertyNames();
        while (names.hasMoreElements())
        {
            String name = (String) names.nextElement();
            propsMap.put("${" + name + "}", props.getProperty(name));
        }
        return propsMap;
    }

    static void printProperties(Properties props)
    {
        Enumeration names = props.propertyNames();
        while (names.hasMoreElements())
        {
            String name = (String) names.nextElement();
            String value = props.getProperty(name);
            System.out.println(name + " => " + value);
        }
    }
}