37

I have a simple java application that loads a properties file from the current package.

this.getClass().getResourceAsStream("props.properties");

This works fine when the property file I want is in the current package. However, I want to package this application as a JAR and define and override with a new properties file where I use it. Is there a way to load the first resource named "props.properties" that is on the classpath?

I want it to be as easy to override the properties file via command line:

java.exe -classpath props.properties;myJar.jar com.test.MyApp

I don't want to have to unpack the JAR and modify the properties file to change something. I feel like I'm missing something obvious...

schmimd04
  • 1,444
  • 3
  • 14
  • 23

4 Answers4

31

The javadoc for Class.getResourceAsStream() documents the lookup logic:

If the name begins with a '/' ('\u002f'), then the absolute name of the resource is the portion of the name following the '/'.

Otherwise, the absolute name is of the following form:
modified_package_name/name

Where the modified_package_name is the package name of this object with '/' substituted for '.' ('\u002e').

So in other words, the resource name passed to the method should look like /com/package/p2/props.properties if the props.properties is stored in the com.package.p2 package instead of the current class's.

turbanoff
  • 2,439
  • 6
  • 42
  • 99
matt b
  • 138,234
  • 66
  • 282
  • 345
13

I'm sure it's too late for the answer but it could be interesting for googlers this small code snippet helpers to load a properties file from any where in the Classpath.

ClassLoader cl = ClassLoader.getSystemClassLoader();
    if (cl != null) {
        URL url = cl.getResource(CONF_PROPERTIES);
        if (url == null) {
            url = cl.getResource("/" + CONF_PROPERTIES);
        }
        if (url != null) {
            try {
                InputStream in = url.openStream();
                props = new Properties();
                props.load(in);
            } catch (IOException e) {
                // Log the exception
            } finally {
               // close opened resources
            }

        }
    }
Abderrazak BOUADMA
  • 1,526
  • 2
  • 16
  • 38
  • 2
    Is there a specific reason you don't use getResourceAsStream? – Angelo Fuchs Apr 13 '12 at 09:40
  • I use this kind of initialization as I'm developing a EE application. During development, My application is aware of that file throw Eclipse Classpath. But at the creation of the EAR, the application is aware of that file throw the ClassPath configuration throw WebSphere. I'll appreciate another elegant solution as well. – Abderrazak BOUADMA Apr 13 '12 at 13:13
  • I see. I would not have expected this to throw different Exceptions (or at least would expect that they both have a common parent, like IOException). Interesting approach though. – Angelo Fuchs Apr 13 '12 at 18:37
  • 1
    I don't think this actually loads a properties file from anywhere in the Classpath; the documentation cited in matt b's answer says that it will load the file from either the current package or the root, but not from **anywhere** in the classpath. – Tim Oct 24 '13 at 22:44
  • 1
    It does not work for me in maven project, when the resource is in test folder. – Pavel Niedoba Feb 07 '17 at 17:13
4

If all else fails you could use two different file names, say props-default.properties inside myJar.jar and props.properties to override on the command-line. In your code, you'd try loading the props.properties file first and fallback to props-default.properties if it wasn't found.

Mohammad Faisal
  • 5,783
  • 15
  • 70
  • 117
Mike Tunnicliffe
  • 10,674
  • 3
  • 31
  • 46
1

I'm not sure, but maybe: ClassLoader.getResourceAsStream()

EDIT:

I don't think this is significantly different to this.getClass().getResourceAsStream() from the question, since as mentioned you still have to get the ClassLoader you want to use to load the resource.

Since you provide the resource in the -classpath in your example, it should be available from the same class loader as your "main" class (in the SUN JVM, that's sun.misc.Launcher$AppClassLoader, not sure if this can/does vary for other JVM implementations).

Gerold Broser
  • 14,080
  • 5
  • 48
  • 107
Mike Tunnicliffe
  • 10,674
  • 3
  • 31
  • 46
  • Sounds about right: You want to ask a ClassLoader that's the "boss" of the one that loaded your class, so it may have access to more packages. Another possibility is to try `ClassLoader.getSystemClassLoader()` - as the name implies, it's the "boss" of the others. – Carl Smotricz Jul 20 '10 at 20:48
  • 1
    there is no static method `getResourceAsStream()` in the `ClassLoader` class. – matt b Jul 20 '10 at 20:55
  • and the system classloader will likely load resources from outside of your application's classpath, i.e. looking in the bootstrap directories in the JRE install path – matt b Jul 20 '10 at 20:56
  • 1
    correct, `ClassLoader.getSystemResourceAsStream()` is shorthand for `ClassLoader.getSystemClassLoader().getResourceAsStream()`, which returns null for both `props.properties` and `/com/test/props.properties` – schmimd04 Jul 20 '10 at 21:00
  • A hacky way would be to load it straight from the Application ClassLoader (which is responsible for "-classpath"): sun.misc.Launcher$AppClassLoader.getAppClassLoader(sun.misc.Launcher$ExtClassLoader.getExtClassLoader()).getResourceAsStream(...); probably not portable. – Mike Tunnicliffe Jul 20 '10 at 21:01