-
Notifications
You must be signed in to change notification settings - Fork 25
JsonGenerator#close() is not idempotent and may cause data corruption. #20
Description
If we close a JsonGenerator twice, the internal buffer is returned twice to the shared pool.
Next, the same buffer is wrongly shared between two distinct generators.
This result into hard to predict errors including data-corruption.
Test-case:
StringWriter sw = new StringWriter();
JsonGeneratorFactory factory = Json.createGeneratorFactory(Collections.emptyMap());
try (JsonGenerator generator = factory.createGenerator(sw)) {
generator.writeStartObject();
generator.writeEnd();
generator.close(); // Culprit
System.out.println(sw.toString()); // Correct: {}
} // Try/resource/close
StringWriter sw1 = new StringWriter();
StringWriter sw2 = new StringWriter();
try (JsonGenerator generator1 = factory.createGenerator(sw1);
JsonGenerator generator2 = factory.createGenerator(sw2)) {
generator1.writeStartObject();
generator1.write("key", "value");
generator2.writeStartArray();
generator2.write("item");
generator2.write("item2");
generator1.write("key2", "value2");
generator2.writeEnd();
generator1.writeEnd();
}
System.out.println(sw1); // This output: ["item","item2,]key2":"value2"}
System.out.println(sw2); // This output: ["item","item2,]
Explanation:
As you can see, both generated strings are completely broken.
The culprit in this test-case is the pre-emptive "generator.close()" line.
This line cause the JsonGeneratorImpl to recycle buffer to the pool.
Next, the try/resource close this generator again, which cause the same internal buffer to be recycled a second time.
Few line laters, when creating two new distinct generators, they internally receives the same buffer from the pool which cause their output to be mixed...
Well, the quick workarround is to make sure that a generator is not closed more than necessary in the program..
But, because JsonGenerator implements Closeable, the close() method is supposed to be idempotent as specified in the Closeabled javadoc:
- If the stream is already closed then invoking this method has no effect.
This is recalled in the javadoc of the super interface AutoCloseable that a Closeable is required to be idempotent:
So, closing the JsonGenerator more than once should be tolerated and should not corrupt the internal buffer pool.
( It seems that the same issue occurs in other places such as JsonParser#close() )
Thanks,
Kind Regards,
Cédric.