SequenceWriterLeakTest.java
package tools.jackson.databind.seq;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
import org.junit.jupiter.api.Test;
import tools.jackson.core.JsonGenerator;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.SerializationConfig;
import tools.jackson.databind.cfg.GeneratorInitializer;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.testutil.DatabindTestUtil;
import static org.junit.jupiter.api.Assertions.*;
/**
* Tests to verify that a generator we constructed ourselves -- and which owns the
* output target -- gets closed if initialization fails before any content is written.
*/
public class SequenceWriterLeakTest extends DatabindTestUtil
{
static class FailingInitializer implements GeneratorInitializer {
@Override
public void initialize(SerializationConfig config, JsonGenerator g) {
throw new IllegalStateException("Initialization failed");
}
}
static class CloseTrackingOutputStream extends ByteArrayOutputStream {
public boolean closed = false;
@Override
public void close() throws IOException {
closed = true;
super.close();
}
}
static class CloseTrackingWriter extends StringWriter {
public boolean closed = false;
@Override
public void close() throws IOException {
closed = true;
super.close();
}
}
private final ObjectMapper FAILING_MAPPER = JsonMapper.builder()
.generatorInitializer(new FailingInitializer())
.build();
private final Object VALUE = Map.of("a", 1);
@Test
public void mapperWriteValueClosesOnInitFailure() throws Exception
{
CloseTrackingOutputStream out = new CloseTrackingOutputStream();
assertThrows(IllegalStateException.class,
() -> FAILING_MAPPER.writeValue(out, VALUE));
assertTrue(out.closed, "OutputStream should have been closed by failed writeValue()");
}
@Test
public void writerWriteValueClosesOnInitFailure() throws Exception
{
CloseTrackingWriter w = new CloseTrackingWriter();
assertThrows(IllegalStateException.class,
() -> FAILING_MAPPER.writer().writeValue(w, VALUE));
assertTrue(w.closed, "Writer should have been closed by failed writeValue()");
}
@Test
public void writeValuesClosesOnInitFailure() throws Exception
{
CloseTrackingOutputStream out = new CloseTrackingOutputStream();
assertThrows(IllegalStateException.class,
() -> FAILING_MAPPER.writer().writeValues(out));
assertTrue(out.closed, "OutputStream should have been closed by failed writeValues()");
}
@Test
public void writeValuesAsArrayClosesOnInitFailure() throws Exception
{
CloseTrackingOutputStream out = new CloseTrackingOutputStream();
assertThrows(IllegalStateException.class,
() -> FAILING_MAPPER.writer().writeValuesAsArray(out));
assertTrue(out.closed, "OutputStream should have been closed by failed writeValuesAsArray()");
}
@Test
public void writeValuesAsArrayToWriterClosesOnInitFailure() throws Exception
{
CloseTrackingWriter w = new CloseTrackingWriter();
assertThrows(IllegalStateException.class,
() -> FAILING_MAPPER.writer().writeValuesAsArray(w));
assertTrue(w.closed, "Writer should have been closed by failed writeValuesAsArray()");
}
// But a caller-supplied generator is neither initialized nor closed by us
@Test
public void callerSuppliedGeneratorNotTouched() throws Exception
{
CloseTrackingOutputStream out = new CloseTrackingOutputStream();
ObjectMapper plain = newJsonMapper();
try (JsonGenerator g = plain.createGenerator(out)) {
FAILING_MAPPER.writer().writeValuesAsArray(g);
assertFalse(out.closed);
}
}
@Test
public void mapperCreateGeneratorClosesOnInitFailure() throws Exception
{
CloseTrackingOutputStream out = new CloseTrackingOutputStream();
assertThrows(IllegalStateException.class,
() -> FAILING_MAPPER.createGenerator(out));
assertTrue(out.closed, "OutputStream should have been closed by failed createGenerator()");
}
@Test
public void writerCreateGeneratorClosesOnInitFailure() throws Exception
{
CloseTrackingOutputStream out = new CloseTrackingOutputStream();
assertThrows(IllegalStateException.class,
() -> FAILING_MAPPER.writer().createGenerator(out));
assertTrue(out.closed, "OutputStream should have been closed by failed createGenerator()");
}
}