In virtually all IT environments, program behavior can be controlled at run time by properties. To facilitate obtaining (and occasionally preserving) the values of these properties, Java provides the Properties class.

The Properties class is Hashtable<String, String> with benefits. (And since we haven’t mentioned Hashtable before, it’s basically the same as a HashMap that cannot contain nulls as either a key or a value.)

Specifically, a Properties instance can be read from an InputStream or Reader, or written to an OutputStream or Writer, either as a flat file or as XML.

Properties are used to specify things like:

  • The source directory for input data
  • An email address to which the final run status is sent
  • A subject line for these emails
  • A directory to receive output data
  • Database address, logon, and password values
  • The fully qualified class name of an interface implementation
  • You name it

Typically, properties are stored in a file with the extension, “.properties”, and contains lines that look like this:

net.hardknockjava.report.to=myclient@gmail.com
net.hardknockjava.report.from=noreply@hardknockjava.net
net.hardknockjava.thisprogram.inputdir=/net/hardknockjava/lesson18
net.hardknockjava.thisprogram.dataformat=dd/MM/yyyy

Such a file is typically stored in a directory specified in the execution classpath. As such, it can be loaded by an instance of another class we haven’t talked about: ClassLoader. As the name implies, ClassLoader is responsible for loading classes (i.e., compiled class files) into the system, and it looks for such files in the directories and JARs specified by the execution classpath. And once loaded, every class has a reference to the ClassLoader that loaded it.

But ClassLoader can also provide access to any other file in the execution classpath as well: an image, a video, an audio, and–a properties file. As long as it’s in the execution classpath, a ClassLoader can find it.

So let’s download Lesson 18 Example 01, import it to your Eclipse workspace, and see how properties work.

Using Properties

In our sample project, you’ll find a directory named data, containing two files:

  • sample.properties, which contains:

prop1 = This is the first property
org.hardknockjava.sample = thesample

  • whisky.jar, which contains a file named data/sevencrown.properties that contains:

distiller=Seagrams
country.of.origin=Canada
age.in.years=12

Start by trying to run class PropertiesExample as a Java application by right-clicking on file PropertiesExample.java in the Package Explorer and selecting Run As…/Java Application in the popup menu. The run will fail, but you’ll now have a launch configuration you can modify.

Completing the Launch Configuration

To make our program succeed, we need to include the locations of our properties files in the execution classpath.

  1. Select the command Run/Run Configurations… from the main menu. Since PropertiesExample is the last run was of PropertiesExample, it should already be selected in the Run Configurations dialog:

    Run Configurations dialog Main tab

  2. Click on the Dependencies tab, and then on Classpath Entries.

    Run Configurations dialog Dependencies tab

  3. Click on Advanced and, with Add Folders selected, click Ok.

    Advanced Options dialog

  4. Expand the Lesson-18-Example-01 node of the tree, select the data node, and click Ok.

    Folder Selection dialog

  5. Click on Add JARs…. Expand the Lesson-18-Example-01/data node, select whisky.jar, and click Ok. We need to do this because while adding the data directory to the classpath gives the ClassLoader the structure of that directory, it says nothing about the structure of the JAR contents.

    Run Configurations dialog Dependencies tab

    And now your Run Configurations dialog should look like this:

    JAR Selection dialog

    and the command to execute this program looks something like this:

    javaw.exe -classpath
    "C:\eclipse-workspace\Lesson-18-Example-01\bin;
    C:\eclipse-workspace\Lesson-18-Example-01\data;
    C:\eclipse-workspace\Lesson-18-Example-01\data\whisky.jar"
    org.hardknockjava.lesson18.example01.PropertiesExample

  6. Click Run and this is your output:
-- listing properties --
prop1=This is the first property
org.hardknockjava.sample=thesample
-- listing properties --
prop1=This is the first property
age.in.years=12
country.of.origin=Canada
org.hardknockjava.sample=thesample
distiller=Seagrams


obtained age.in.years=12
importer without default=null
importer with default=Al Capone


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Whisky Properties</comment>
<entry key="age.in.years">12</entry>
<entry key="country.of.origin">Canada</entry>
<entry key="distiller">Seagrams</entry>
</properties>

Deep Dive

Now let’s look at the code that produced this output given the properties files we provided.

    public static void main(String[] args) throws IOException {
        Properties sampleProps = new Properties();
        InputStream sampleStream = 
                PropertiesExample.class.getClassLoader()
                      .getResourceAsStream("sample.properties");
        sampleProps.load(sampleStream);
        sampleProps.list(System.out);

Notice how the program calls upon the ClassLoader that loaded PropertiesExample to get an InputStream to the file, sample.properties. (Of course, the file might be named anything, but we’re following naming conventions.) We don’t need to know the location of sample.properties: the ClassLoader will find it as long as it’s on the execution classpath–which it is, because we so constructed our launch configuration.

The load() method reads the file into sampleProps, and the list() method writes them to an OutputStream–in this case, System.out.

-- listing properties --
prop1=This is the first property
org.hardknockjava.sample=thesample

Next, let’s load whiskyProps.

        Properties whiskyProps = new Properties(sampleProps);
        InputStream whiskeyStream = 
               PropertiesExample.class.getClassLoader()
                 .getResourceAsStream("data/sevencrown.properties");
        whiskyProps.load(whiskeyStream);
        whiskyProps.list(System.out);

What’s different here?

First, we pass the whiskyProps constructor sampleProps as a default. This means that if you ask for a particular property value in whiskyProps that isn’t defined there, the system will look for it in sampleProps–which could conceivably have a set of default properties too, and so on. Thus you can start with a set of default property values in one Properties instance, then load overrides on top of them in another Properties instance in which you’ll actually search for the property values you need.

Second, adding a JAR to the execution classpath is like adding the root directory within that JAR to the classpath. Our getResourceAsStream() call needs to know where within that root directory the file sevencrown.properties can be found: within the subdirectory data inside the JAR. Unlike with subdirectories on disk, there’s no way to add a subdirectory within a JAR to the execution classpath.

Finally, notice that whiskyProps.list() displays property values that aren’t in sevencrown.properties: prop1 and org.hardknockjava.sample. These are the values from sample.properties, which are listed because sampleProps is the set of default properties for whiskyProps.

-- listing properties --
prop1=This is the first property
age.in.years=12
country.of.origin=Canada
org.hardknockjava.sample=thesample
distiller=Seagrams

Of course, you’ll usually want to get a property value by its name:

   String age = whiskyProps.getProperty("age.in.years");

… which returns the String value “12”.

The getProperty() method also allows you to specify a default value in case the desired property doesn’t appear in the Properties instance (or its default Properties instance, and so on):

        String importer = "initial";
        importer = whiskyProps.getProperty("importer");
        System.out.println("importer without default=" + importer);
        importer = whiskyProps.getProperty("importer", "Al Capone");
        System.out.println("importer with default=" + importer);

Since “importer” is not specified in either Properties instance (whiskyProps or its default, sampleProps), the first getProperty() call returns null. But the second returns “Al Capone”, which has been passed as a default value.

obtained age.in.years=12
importer without default=null
importer with default=Al Capone

Finally, just for the fun of it, we write whiskyProps to an OutputStream in XML format, suitable for use with the loadFromXML() method. You might store the Properties instance if you’ve modified it.

    whiskyProps.storeToXML(System.out, "Whisky Properties");
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Whisky Properties</comment>
<entry key="age.in.years">12</entry>
<entry key="country.of.origin">Canada</entry>
<entry key="distiller">Seagrams</entry>
</properties>

Notice that the output doesn’t include values from sampleProps, despite its use as a default Properties instance. If you were storing a Properties instance, you wouldn’t want the default values either.

There is also a store() method to store the instance in a format suitable for use with load().

Properties Methods

Here are some of the more useful methods for the Properties class.

MethodFunction
String getProperty(String key)Returns the property value specified by key, or null if no such property exists in the Properties instance or default instances.
String getProperty(String key, String defaultValue)Same as getProperty() above, but returns defaultValue instead of null if no such property exists.
void load(InputStream in)
void load(Reader rdr)
Loads the Properties instance from the specified source, where the values are in lines of text of the format:
key=value
void loadFromXML(InputStream in)Same as load(), but the input is in the XML format illustrated above.
Object setProperty(String key, String value)Sets the property with the specified key to the specified value, and returns the previous value of the named property or null.
void store(OutputStream out, String comment)
void store(Writer writer, String comment)
Writes the properties to the specified sink in a format suitable for use with either of the load() methods.
void storeToXML(OutputStream out, String comment)Writes the properties to the specified sink in a format suitable for use with loadFromXML().
Set<String> stringPropertyNames()Returns a Set of the property key names, including those from default Properties instances.

What You Need To Know

  • Properties instances are key/value pairs of Strings.
  • Properties instances are usually populated from files in the execution classpath, but can actually come from any data source that can be accessed by an InputStream or Reader.
  • The ClassLoader class has methods for finding a file in the execution classpath and returning an InputStream or URL that provides access to it.
  • The keys and values in a Properties instance let users provide necessary parameters to a program at run time.
  • One Properties instance can be created with values that override those in an existing Properties instance.

Next: Sending Email