1

I wanted to write to a property file. But it silently never worked. Just from the code behavior I could not notice it. I always had to open the properties file and look if the value changed. But it never did. So actually I would expect to get an exception . The problem seemed to be that I did not close the InputStream before opening the OutputStream. But I never got to know that. It cost me 3 days because I would expect either OutputStream or store function to give me some feedback. Have a look at the code.

File file = ResourceUtils.getFile("classpath:First.properties");
FileInputStream in = new FileInputStream(file);
Properties props = new Properties();
props.load(in);
System.out.println(props.getProperty("country"));
in.close();  // This I always forgot

FileOutputStream out = new FileOutputStream(file);
props.setProperty("country", "germany");
props.store(out, null);
System.out.println(props.getProperty("country"));
out.close();
jschnasse
  • 8,526
  • 6
  • 32
  • 72
Juliette
  • 39
  • 3
  • 6
    1. Use try-with-resources. 2. Your problem with forgetting to close resources is gone. – Amongalen May 13 '20 at 07:42
  • Are you trying to access a file that's in your binaries (classpath)? I think you need a real file on the file system for this to work... – deHaar May 13 '20 at 07:42
  • What exactly is the question? – Lino May 13 '20 at 07:52
  • @deHaar it seemed it also worked on the classpath. I just put in in the resources folder. Someone else also said it will not work on classpath, but seemingly this seems to be wrong. Because it worked with the above code. – Juliette May 13 '20 at 09:43

2 Answers2

1

A forgotten close() statement cannot cause an exception. From the perspective of your stream everything is okay. It just didn't wrote to its destination yet. Why should it? Even when the whole program terminates there is no guaranty that the stream closes and writes its internal buffers out.[1]

You always have to call flush() or close() actively. The underlying implementation will then perform the actual write operation.

This mistake is so common that there is an extra Java-Feature to handle it. It is called try-with-resources and prevents programmers from the evil consequences of missing close() statements.

Example:

//use try-with-resources on out
private void saveProperties(Properties properties, String path) {
    try(PrintStream out = new PrintStream(new FileOutputStream(path))) {
        printProperties(properties,out);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
// you can pass System.out as well to print to console
private void printProperties(Properties properties, PrintStream out) {
    try {
        properties.store(out, null);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
//properties.load leaves the stream open, so you have to use try-with-resources
private Properties readProperties(String path) {
    try (FileInputStream in = new FileInputStream(path)) {
        Properties properties = new Properties();
        properties.load(in);
        return properties;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Related posts on Java Properties:

Related posts on Java Streams:

[1] See: Josh Bloch, Effective Java,(2nd ed.), Page 27.

Avoid finalizers.[...] It is entirely possible, even likely, that a program terminates without executing finalizers on some objects that are no longer reachable.

jschnasse
  • 8,526
  • 6
  • 32
  • 72
  • I think my code above was also working for properties inside of jar. – Juliette May 13 '20 at 11:19
  • I just wanted to collect some code I found useful in the past - for future reference and as answer to your question. The code showed in your question can be enhanced by using try-with-resources and exception handling. This can save you a lot of time in the future. Good luck! – jschnasse May 13 '20 at 16:32
  • ok thanks. But the store method should indicate me that it could not store the value . Instead it has a void return and also it does not throw any exception. – Juliette May 14 '20 at 07:13
  • A missing close is a programming failure. It is your responsibility to `flush()`, `close()` streams properly. You can use the `try-with-resources` idiom - like showed in my code example - to indicate that the stream must be closed as soon as the surrounding method terminates. – jschnasse May 14 '20 at 07:18
  • Concerning the behaviour of the `Properties.store()` Method. The mehod does what it describes. It stores Properties to an `OutputStream` or `Writer`! But it leaves it to you to properly close the stream/writer. You can use the `printProperties()` method I have provided. It will throw a RuntimeException if something goes wrong. https://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#store(java.io.Writer,%20java.lang.String) – jschnasse May 14 '20 at 07:41
1

As for the actual question "why does it not throw an exception", it's because there are cases you want the Stream to remain open.

    class FileWriteSample implements Closeable {
        FileOutputStream writeTo;

        public FileWriteSample(String filename) throws IOException {
            writeTo = new FileOutputStream(filename);
            // should we expect an Exception here because we don't close the Stream?
            // we're planning to use it later on
        }
        public void write(String s) {
            // write to stream
        }
        public void close() throws IOException {
            writeTo.close();
        }
    }
daniu
  • 14,137
  • 4
  • 32
  • 53
  • but i would expect OutputStream to throw an exception when it could not write to my properties file because obviously I forgot to close the InputStream . But OutputStream just does not give any feedback – Juliette May 14 '20 at 08:08