Newsletter sign-up
View all newsletters

Sign up for our Enterprise Java Newsletter

Enterprise Java

Smartly load your properties

Strive for disk location-independent code nirvana

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone

Page 2 of 3

  ClassLoader.getResourceAsStream ("some/pkg/resource.properties");
  Class.getResourceAsStream ("/some/pkg/resource.properties");
  ResourceBundle.getBundle ("some.pkg.resource");


Additionally, if the code is in a class within a some.pkg Java package, then the following works as well:

  Class.getResourceAsStream ("resource.properties");


Note the subtle differences in parameter formatting for these methods. All getResourceAsStream() methods use slashes to separate package name segments, and the resource name includes the file extension. Compare that with resource bundles where the resource name looks more like a Java identifier, with dots separating package name segments (the .properties extension is implied here). Of course, that is because a resource bundle does not have to be backed by a .properties file: it can be a class, for a example.

To slightly complicate the picture, java.lang.Class's getResourceAsStream() instance method can perform package-relative resource searches (which can be handy as well, see "Got Resources?"). To distinguish between relative and absolute resource names, Class.getResourceAsStream() uses leading slashes for absolute names. In general, there's no need to use this method if you are not planning to use package-relative resource naming in code.

It is easy to get mixed up in these small behavioral differences for ClassLoader.getResourceAsStream(), Class.getResourceAsStream(), and ResourceBundle.getBundle(). The following table summarizes the salient points to help you remember:

Behavioral differences

Method Parameter format Lookup failure behavior Usage example
ClassLoader.
getResourceAsStream()
"/"-separated names; no leading "/" (all names are absolute) Silent (returns null) this.getClass().getClassLoader()
.getResourceAsStream
("some/pkg/resource.properties")
Class.
getResourceAsStream()
"/"-separated names; leading "/" indicates absolute names; all other names are relative to the class's package Silent (returns null) this.getClass()
.getResourceAsStream
("resource.properties")
ResourceBundle.
getBundle()
"."-separated names; all names are absolute; .properties suffix is implied Throws unchecked
java.util.MissingResourceException
ResourceBundle.getBundle
("some.pkg.resource")


From data streams to java.util.Properties

You might have noticed that some previously mentioned methods are half measures only: they return InputStreams and nothing resembling a list of name-value pairs. Fortunately, loading data into such a list (which can be an instance of java.util.Properties) is easy enough. Because you will find yourself doing this over and over again, it makes sense to create a couple of helper methods for this purpose.

The small behavioral difference among Java's built-in methods for classpath resource loading can also be a nuisance, especially if some resource names were hardcoded but you now want to switch to another load method. It makes sense to abstract away little things like whether slashes or dots are used as name separators, etc. Without further ado, here's my PropertyLoader API that you might find useful (available with this article's download):

public abstract class PropertyLoader
{
    /**
     * Looks up a resource named 'name' in the classpath. The resource must map
     * to a file with .properties extention. The name is assumed to be absolute
     * and can use either "/" or "." for package segment separation with an
     * optional leading "/" and optional ".properties" suffix. Thus, the
     * following names refer to the same resource:
     * <pre>
     * some.pkg.Resource
     * some.pkg.Resource.properties
     * some/pkg/Resource
     * some/pkg/Resource.properties
     * /some/pkg/Resource
     * /some/pkg/Resource.properties
     * </pre>
     * 
     * @param name classpath resource name [may not be null]
     * @param loader classloader through which to load the resource [null
     * is equivalent to the application loader]
     * 
     * @return resource converted to java.util.Properties [may be null if the
     * resource was not found and THROW_ON_LOAD_FAILURE is false]
     * @throws IllegalArgumentException if the resource was not found and
     * THROW_ON_LOAD_FAILURE is true
     */
    public static Properties loadProperties (String name, ClassLoader loader)
    {
        if (name == null)
            throw new IllegalArgumentException ("null input: name");
        
        if (name.startsWith ("/"))
            name = name.substring (1);
            
        if (name.endsWith (SUFFIX))
            name = name.substring (0, name.length () - SUFFIX.length ());
        
        Properties result = null;
        
        InputStream in = null;
        try
        {
            if (loader == null) loader = ClassLoader.getSystemClassLoader ();
            
            if (LOAD_AS_RESOURCE_BUNDLE)
            {    
                name = name.replace ('/', '.');
                // Throws MissingResourceException on lookup failures:
                final ResourceBundle rb = ResourceBundle.getBundle (name,
                    Locale.getDefault (), loader);
                
                result = new Properties ();
                for (Enumeration keys = rb.getKeys (); keys.hasMoreElements ();)
                {
                    final String key = (String) keys.nextElement ();
                    final String value = rb.getString (key);
                    
                    result.put (key, value);
                } 
            }
            else
            {
                name = name.replace ('.', '/');
                
                if (! name.endsWith (SUFFIX))
                    name = name.concat (SUFFIX);
                                
                // Returns null on lookup failures:
                in = loader.getResourceAsStream (name);
                if (in != null)
                {
                    result = new Properties ();
                    result.load (in); // Can throw IOException
                }
            }
        }
        catch (Exception e)
        {
            result = null;
        }
        finally
        {
            if (in != null) try { in.close (); } catch (Throwable ignore) {}
        }
        
        if (THROW_ON_LOAD_FAILURE && (result == null))
        {
            throw new IllegalArgumentException ("could not load [" + name + "]"+
                " as " + (LOAD_AS_RESOURCE_BUNDLE
                ? "a resource bundle"
                : "a classloader resource"));
        }
        
        return result;
    }
    
    /**
     * A convenience overload of {@link #loadProperties(String, ClassLoader)}
     * that uses the current thread's context classloader.
     */
    public static Properties loadProperties (final String name)
    {
        return loadProperties (name,
            Thread.currentThread ().getContextClassLoader ());
    }
        
    private static final boolean THROW_ON_LOAD_FAILURE = true;
    private static final boolean LOAD_AS_RESOURCE_BUNDLE = false;
    private static final String SUFFIX = ".properties";
} // End of class


The Javadoc comment for the loadProperties() method shows that the method's input requirements are quite relaxed: it accepts a resource name formatted according to any of the native method's schemes (except for package-relative names possible with Class.getResourceAsStream()) and normalizes it internally to do the right thing.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comments (17)
Login
Forgot your account info?

ResourcesBy Anonymous on December 10, 2009, 8:48 amWhen using Resources try to put the resource being loaded in the resource directory of the project.

Reply | Read entire comment

A new set of extensions to the properties syntaxBy Anonymous on November 6, 2009, 6:19 pmProperties have not really been updated since Java 1.0. A new open source project is filling the gap - with variable substitution, inclusion, lists, nesting, etc....

Reply | Read entire comment

its seems helping lets seeBy Anonymous on October 16, 2009, 11:47 amits seems helping lets see

Reply | Read entire comment

GoodBy Anonymous on August 4, 2009, 4:49 amThanks ,excellent presentaion of very important details

Reply | Read entire comment

Best Strategy Indeed!By idauki414 on July 29, 2009, 9:59 amthanks for sharing the best strategy for loading property and configuration files in Java.

Reply | Read entire comment

View all comments

Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources