Skip to content

Jsonb close contract should be revisited #346

@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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions