Skip to content

Jsonb close contract should be revisited #346

Open
@marschall

Description

@marschall

The Json methods that take IO streams should have their #close() contract revisited.

The current situation is:

  • The methods taking an InputStream or OutputStream as an argument specify
    "Upon a successful completion, the stream will be closed by this method."
  • No contract exits for the method taking a Reader or Writer as an argument.

The issues with this are:

  1. If the method throws an exception it is not specified wether the streams are closed. This leaves it unclear whether the caller is responsible for calling #close() in these cases.
  2. It is inconsistent that the byte oriented streams (InputStream / OutputStream) are closed but the character oriented streams (Reader / Writer) are not.

Secondly, it is debatable whether Json should call #close() on the streams passed at all or whether that should be left to the caller. In my personal view it is idiomatic Java if the creator of a "resource", eg. IO stream, is responsible for its closing. Meaning generally calling code should look like this

try (var ioStream = createIoStream()) {
  jsonb.toJson(object, ioStream);
}

This also integrates well with static analysis tools looking for resource leaks. If a user wants to keep the IO stream open, eg. to implement line oriented JSON, they can do this by simply not calling #close(). Otherwise they would have to wrap the IO stream with one suppressing #close().

This is similar to C where in general code calling malloc is also responsible for calling free. Functions are in general not expected to clean up memory they are passed.

I could find no written rule or recommendation for this but the majority of the JDK code I can find does not close IO streams passed as an argument and leaves calling #close() to the caller.

Examples from the JDK where calling #close() is left to the caller:

  • Properties#load(Reader)
  • KeyStore#load(InputStream, char[])
  • javax.imageio.ImageIO#read(InputStream)
  • java.util.logging.LogManager#readConfiguration(InputStream)
  • javax.tools.Tool#run(InputStream, OutputStream, OutputStream, String...)

Examples from the JDK where #close() is only called if the method returns without throwing an exception (strict interpretation of the current Jsonb API contract)

  • Properties#loadFromXML(InputStream)

Examples from the JDK with no specified contract for calling #close(), likely left to the caller:

  • javax.script.ScriptEngine#eval(Reader)
  • javax.xml.parsers.DocumentBuilder#parse(InputStream)

See also eclipse-ee4j/yasson#586

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions