Skip to content

Commit f3c2564

Browse files
committed
Ensure IonReader instances created within IonFactory are always resource-managed.
1 parent 0d73a95 commit f3c2564

File tree

2 files changed

+79
-3
lines changed

2 files changed

+79
-3
lines changed

ion/src/main/java/com/fasterxml/jackson/dataformat/ion/IonFactory.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -360,14 +360,17 @@ protected JsonParser _createParser(InputStream in, IOContext ctxt)
360360
throws IOException
361361
{
362362
IonReader ion = _system.newReader(in);
363-
return new IonParser(ion, _system, ctxt, getCodec(), _ionParserFeatures);
363+
return new IonParser(ion, _system,
364+
_createContext(_createContentReference(ion), true), getCodec(), _ionParserFeatures);
364365
}
365366

366367
@Override
367368
protected JsonParser _createParser(Reader r, IOContext ctxt)
368369
throws IOException
369370
{
370-
return new IonParser(_system.newReader(r), _system, ctxt, getCodec(), _ionParserFeatures);
371+
IonReader ion = _system.newReader(r);
372+
return new IonParser(ion, _system,
373+
_createContext(_createContentReference(ion), true), getCodec(), _ionParserFeatures);
371374
}
372375

373376
@Override
@@ -381,7 +384,9 @@ protected JsonParser _createParser(char[] data, int offset, int len, IOContext c
381384
protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt)
382385
throws IOException
383386
{
384-
return new IonParser(_system.newReader(data, offset, len), _system, ctxt, getCodec(), _ionParserFeatures);
387+
IonReader ion = _system.newReader(data, offset, len);
388+
return new IonParser(ion, _system,
389+
_createContext(_createContentReference(ion), true), getCodec(), _ionParserFeatures);
385390
}
386391

387392
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.fasterxml.jackson.dataformat.ion;
2+
3+
import com.amazon.ion.IonReader;
4+
import com.fasterxml.jackson.core.JsonParser;
5+
import org.junit.Test;
6+
7+
import java.io.ByteArrayInputStream;
8+
import java.io.StringReader;
9+
10+
import static org.junit.Assert.assertEquals;
11+
import static org.junit.Assert.assertTrue;
12+
13+
public class IonFactoryTest {
14+
15+
// 4-byte Ion 1.0 IVM followed by int 0.
16+
private static final byte[] BINARY_INT_0 = new byte[] {(byte) 0xE0, 0x01, 0x00, (byte) 0xEA, 0x20};
17+
private static final String TEXT_INT_0 = "0";
18+
19+
@Test
20+
public void byteArrayIsManaged() throws Throwable {
21+
assertResourceManaged(true, parser(f -> f.createParser(BINARY_INT_0)));
22+
}
23+
24+
@Test
25+
public void charArrayIsManaged() throws Throwable {
26+
assertResourceManaged(true, parser(f -> f.createParser(TEXT_INT_0.toCharArray())));
27+
}
28+
29+
@Test
30+
public void readerIsManaged() throws Throwable {
31+
assertResourceManaged(true, parser(f -> f.createParser(new StringReader(TEXT_INT_0))));
32+
}
33+
34+
@Test
35+
public void inputStreamIsManaged() throws Throwable {
36+
assertResourceManaged(true, parser(f -> f.createParser(new ByteArrayInputStream(BINARY_INT_0))));
37+
}
38+
39+
@Test
40+
public void ionValueIsManaged() throws Throwable {
41+
assertResourceManaged(true, parser(f -> f.createParser(f.getIonSystem().newInt(0))));
42+
}
43+
44+
@Test
45+
public void ionReaderIsNotManaged() throws Throwable {
46+
// When the user provides an IonReader, it is not resource-managed, meaning that the user retains the
47+
// responsibility to close it. In all other cases, the IonReader is created internally, is resource-managed,
48+
// and is closed automatically in IonParser.close().
49+
assertResourceManaged(false, parser(f -> f.createParser(f.getIonSystem().newReader(BINARY_INT_0))));
50+
}
51+
52+
private void assertResourceManaged(boolean expectResourceManaged, ThrowingSupplier<IonParser> supplier)
53+
throws Throwable {
54+
IonParser parser = supplier.get();
55+
assertEquals(expectResourceManaged, parser._ioContext.isResourceManaged());
56+
assertTrue(IonReader.class.isAssignableFrom(parser._ioContext.contentReference().getRawContent().getClass()));
57+
parser.close();
58+
}
59+
60+
private interface ThrowingFunction<T, R> {
61+
R apply(T t) throws Throwable;
62+
}
63+
64+
private interface ThrowingSupplier<T> {
65+
T get() throws Throwable;
66+
}
67+
68+
private static ThrowingSupplier<IonParser> parser(ThrowingFunction<IonFactory, JsonParser> f) {
69+
return () -> (IonParser) f.apply(new IonFactory());
70+
}
71+
}

0 commit comments

Comments
 (0)