From 2e2836ed4a8c4590e12638a4b9c27bed93073ce8 Mon Sep 17 00:00:00 2001 From: Tyler Gregg Date: Tue, 17 May 2022 15:42:59 -0700 Subject: [PATCH] Ensure IonReader instances created within IonFactory are always resource-managed. --- .../jackson/dataformat/ion/IonFactory.java | 11 ++- .../dataformat/ion/IonFactoryTest.java | 82 +++++++++++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 ion/src/test/java/com/fasterxml/jackson/dataformat/ion/IonFactoryTest.java diff --git a/ion/src/main/java/com/fasterxml/jackson/dataformat/ion/IonFactory.java b/ion/src/main/java/com/fasterxml/jackson/dataformat/ion/IonFactory.java index 9b421897b..0c7843af8 100644 --- a/ion/src/main/java/com/fasterxml/jackson/dataformat/ion/IonFactory.java +++ b/ion/src/main/java/com/fasterxml/jackson/dataformat/ion/IonFactory.java @@ -360,14 +360,17 @@ protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException { IonReader ion = _system.newReader(in); - return new IonParser(ion, _system, ctxt, getCodec(), _ionParserFeatures); + return new IonParser(ion, _system, + _createContext(_createContentReference(ion), true), getCodec(), _ionParserFeatures); } @Override protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException { - return new IonParser(_system.newReader(r), _system, ctxt, getCodec(), _ionParserFeatures); + IonReader ion = _system.newReader(r); + return new IonParser(ion, _system, + _createContext(_createContentReference(ion), true), getCodec(), _ionParserFeatures); } @Override @@ -381,7 +384,9 @@ protected JsonParser _createParser(char[] data, int offset, int len, IOContext c protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException { - return new IonParser(_system.newReader(data, offset, len), _system, ctxt, getCodec(), _ionParserFeatures); + IonReader ion = _system.newReader(data, offset, len); + return new IonParser(ion, _system, + _createContext(_createContentReference(ion), true), getCodec(), _ionParserFeatures); } @Override diff --git a/ion/src/test/java/com/fasterxml/jackson/dataformat/ion/IonFactoryTest.java b/ion/src/test/java/com/fasterxml/jackson/dataformat/ion/IonFactoryTest.java new file mode 100644 index 000000000..3f6167382 --- /dev/null +++ b/ion/src/test/java/com/fasterxml/jackson/dataformat/ion/IonFactoryTest.java @@ -0,0 +1,82 @@ +package com.fasterxml.jackson.dataformat.ion; + +import com.amazon.ion.IonReader; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringReader; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(Parameterized.class) +public class IonFactoryTest { + + // 4-byte Ion 1.0 IVM followed by int 0. + private static final byte[] BINARY_INT_0 = new byte[] {(byte) 0xE0, 0x01, 0x00, (byte) 0xEA, 0x20}; + private static final String TEXT_INT_0 = "0"; + + private enum InputSource { + BYTE_ARRAY() { + @Override + IonParser newParser(IonFactory factory) throws IOException { + return (IonParser) factory.createParser(BINARY_INT_0); + } + }, + CHAR_ARRAY() { + @Override + IonParser newParser(IonFactory factory) throws IOException { + return (IonParser) factory.createParser(TEXT_INT_0.toCharArray()); + } + }, + READER() { + @Override + IonParser newParser(IonFactory factory) throws IOException { + return (IonParser) factory.createParser(new StringReader(TEXT_INT_0)); + } + }, + INPUT_STREAM() { + @Override + IonParser newParser(IonFactory factory) throws IOException { + return (IonParser) factory.createParser(new ByteArrayInputStream(BINARY_INT_0)); + } + }, + ION_READER() { + @Override + IonParser newParser(IonFactory factory) { + return factory.createParser(factory.getIonSystem().newReader(BINARY_INT_0)); + } + }, + ION_VALUE() { + @Override + IonParser newParser(IonFactory factory) { + return factory.createParser(factory.getIonSystem().newInt(0)); + } + }; + + abstract IonParser newParser(IonFactory factory) throws IOException; + } + + @Parameterized.Parameters(name = "{0}") + public static InputSource[] parameters() { + return InputSource.values(); + } + + @Parameterized.Parameter + public InputSource inputSource; + + @Test + public void testIonParserIsResourceManaged() throws IOException { + IonFactory factory = new IonFactory(); + IonParser parser = inputSource.newParser(factory); + // When the user provides an IonReader, it is not resource-managed, meaning that the user retains the + // responsibility to close it. In all other cases, the IonReader is created internally, is resource-managed, + // and is closed automatically in IonParser.close(). + assertEquals(inputSource != InputSource.ION_READER, parser._ioContext.isResourceManaged()); + assertTrue(IonReader.class.isAssignableFrom(parser._ioContext.contentReference().getRawContent().getClass())); + parser.close(); + } +}