Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
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
|
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.
Archived Discussions (Read only)