3

Is it correct to say that properties files used in execution by a running JAR best belong in META-INF? The notion being to fit with the Standard Directory Layout specified by Maven.

Assuming that structure is desired, how is the properties file loaded from META-INF?

thufir@doge:~/NetBeansProjects/json_parser$ 
thufir@doge:~/NetBeansProjects/json_parser$ java -jar build/libs/json_parser-all.jarAug 18, 2017 6:44:24 AM net.bounceme.doge.json.Main main
INFO: starting
Aug 18, 2017 6:44:24 AM net.bounceme.doge.json.Main run
INFO: actually
Aug 18, 2017 6:44:24 AM net.bounceme.doge.json.PropertiesReader tryGetProps
INFO: properties
Aug 18, 2017 6:44:24 AM net.bounceme.doge.json.PropertiesReader getProps
INFO: properties
Exception in thread "main" java.lang.NullPointerException
    at java.util.Properties$LineReader.readLine(Properties.java:434)
    at java.util.Properties.load0(Properties.java:353)
    at java.util.Properties.load(Properties.java:341)
    at net.bounceme.doge.json.PropertiesReader.getProps(PropertiesReader.java:26)
    at net.bounceme.doge.json.PropertiesReader.tryGetProps(PropertiesReader.java:16)
    at net.bounceme.doge.json.Main.run(Main.java:18)
    at net.bounceme.doge.json.Main.main(Main.java:12)
thufir@doge:~/NetBeansProjects/json_parser$ 
thufir@doge:~/NetBeansProjects/json_parser$ jar tf build/libs/json_parser-all.jarMETA-INF/
META-INF/MANIFEST.MF
META-INF/maven/
META-INF/maven/javax.json/
META-INF/maven/javax.json/javax.json-api/
META-INF/maven/javax.json/javax.json-api/pom.properties
META-INF/maven/javax.json/javax.json-api/pom.xml
javax/
javax/json/
javax/json/Json.class
javax/json/JsonArray.class
javax/json/JsonArrayBuilder.class
javax/json/JsonBuilderFactory.class
javax/json/JsonException.class
javax/json/JsonMergePatch.class
javax/json/JsonNumber.class
javax/json/JsonObject.class
javax/json/JsonObjectBuilder.class
javax/json/JsonPatch$Operation.class
javax/json/JsonPatch.class
javax/json/JsonPatchBuilder.class
javax/json/JsonPointer.class
javax/json/JsonReader.class
javax/json/JsonReaderFactory.class
javax/json/JsonString.class
javax/json/JsonStructure.class
javax/json/JsonValue$ValueType.class
javax/json/JsonValue.class
javax/json/JsonValueImpl.class
javax/json/JsonWriter.class
javax/json/JsonWriterFactory.class
javax/json/spi/
javax/json/spi/JsonProvider.class
javax/json/stream/
javax/json/stream/JsonCollectors.class
javax/json/stream/JsonGenerationException.class
javax/json/stream/JsonGenerator.class
javax/json/stream/JsonGeneratorFactory.class
javax/json/stream/JsonLocation.class
javax/json/stream/JsonParser$Event.class
javax/json/stream/JsonParser.class
javax/json/stream/JsonParserFactory.class
javax/json/stream/JsonParsingException.class
module-info.class
net/
net/bounceme/
net/bounceme/doge/
net/bounceme/doge/json/
net/bounceme/doge/json/Main.class
net/bounceme/doge/json/Marshaller.class
net/bounceme/doge/json/PropertiesReader.class
net/bounceme/doge/json/JsonReader.class
json.json
properties.properties
META-INF/properties.properties
thufir@doge:~/NetBeansProjects/json_parser$ 
thufir@doge:~/NetBeansProjects/json_parser$ jar tf build/libs/json_parser-all.jar | grep properties.properties
properties.properties
META-INF/properties.properties
thufir@doge:~/NetBeansProjects/json_parser$ 

and the relevant code:

package net.bounceme.doge.json;

import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

public class PropertiesReader {

    private static final Logger log = Logger.getLogger(PropertiesReader.class.getName());

    public Properties tryGetProps(String propertiesFileName) {
        log.info(propertiesFileName);
        Properties properties = new Properties();
        try {
            properties = getProps(propertiesFileName);
        } catch (IOException ex) {
            Logger.getLogger(PropertiesReader.class.getName()).log(Level.SEVERE, null, ex);
        }
        return properties;
    }

    private Properties getProps(String propertiesFileName) throws IOException {
        log.info(propertiesFileName);
        Properties properties = new Properties();
        properties.load(PropertiesReader.class.getResourceAsStream("/META-INF/" + propertiesFileName));
        log.info(properties.toString());
        return properties;
    }
}

This is in relation to using init properly from gradle and based on general information.

I tried removing the leading / as suggested -- same result as above.

glytching
  • 44,936
  • 9
  • 114
  • 120
Thufir
  • 8,216
  • 28
  • 125
  • 273
  • 2
    "Is it correct to say that properties files used in execution by a running JAR best belong in META-INF?". I would say no. IMHO `META-INF` should rather contains information about the JAR itself (hence the pom.properties and pom.xml Maven puts in), so that frameworks/app servers will better know how to handle this (think about the MANIFEST.MF for OSGi, the META-INF/ejb-jar.xml...). That said, it is just another package, so loading a properties file should work the same way. – Tome Aug 18 '17 at 14:27

1 Answers1

6

You're not obliged to store your properties files in META-INF, indeed it's not even very common to do so (in my experience at least). In support of this contention the Java JAR Specification doesn't include properties files amongst the expected contents of META-INF.

The code below shows several approach to loading properties from a JAR, whether they are in META-INF or not.

Given a JAR file with this content:

jar -tvf properties.jar
   0 Fri Aug 18 15:35:52 IST 2017 META-INF/
  68 Fri Aug 18 15:35:52 IST 2017 META-INF/MANIFEST.MF
   4 Fri Aug 18 15:35:32 IST 2017 META-INF/bProps.properties
   4 Fri Aug 18 15:35:18 IST 2017 aProps.properties

You can load aProps.properties and bProps.properties like so:

@Test
public void canWriteAndRead() throws IOException {
    ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
    // this works
    loadProperties(systemClassLoader.getResource("aProps.properties"));
    // this works
    loadProperties(systemClassLoader.getResource("META-INF/bProps.properties"));
    // this does not work
    loadProperties(systemClassLoader.getResource("/aProps.properties"));
    // this does not work
    loadProperties(systemClassLoader.getResource("/META-INF/bProps.properties"));

    ClassLoader classLoader = getClass().getClassLoader();
    // this works
    loadProperties(classLoader.getResource("aProps.properties"));
    // this works
    loadProperties(classLoader.getResource("META-INF/bProps.properties"));
    // this does not work
    loadProperties(classLoader.getResource("/aProps.properties"));
    // this does not work
    loadProperties(classLoader.getResource("/META-INF/bProps.properties"));

    // this works
    loadProperties(getClass().getResourceAsStream("/aProps.properties"));
    // this works
    loadProperties(getClass().getResourceAsStream("/META-INF/bProps.properties"));
    // this does not work
    loadProperties(getClass().getResourceAsStream("aProps.properties"));
    // this does not work
    loadProperties(getClass().getResourceAsStream("META-INF/bProps.properties"));
}

private void loadProperties(InputStream incoming) throws IOException {
    if (incoming != null) {
        Properties properties = new Properties();
        properties.load(incoming);
        for (String s : properties.stringPropertyNames()) {
            System.out.println(s);
        }
    }
}

private void loadProperties(URL incoming) throws IOException {
    if (incoming != null) {
        Properties properties = new Properties();
        properties.load(incoming.openStream());
        for (String s : properties.stringPropertyNames()) {
            System.out.println(s);
        }
    }
}
glytching
  • 44,936
  • 9
  • 114
  • 120
  • TL;DR. No, I will, thanks for digging up the official docs. Still wonder why Maven has that convention. Maybe it's more geared towards WAR files, but they've generalized it for some reason. I thought it seemed odd. (I had a slight error with my attempt: https://stackoverflow.com/a/45759697/262852 so that's fixed, incidentally.) – Thufir Aug 18 '17 at 15:10