Skip to content

Commit 96657ef

Browse files
committed
Fix #764
1 parent 83c5694 commit 96657ef

File tree

5 files changed

+139
-39
lines changed

5 files changed

+139
-39
lines changed

release-notes/VERSION-2.x

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ JSON library.
3333
#759: JsonGenerator to provide current value to the context before starting objects
3434
(reported by Illia O)
3535
#763: `JsonFactory.createParser()` with `File` may leak `InputStream`s
36+
#764: `JsonFactory.createGenerator()` with `File` may leak `OutputStream`s
3637

3738
2.13.3 (14-May-2022)
3839

src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java

+36-19
Original file line numberDiff line numberDiff line change
@@ -1199,24 +1199,30 @@ public void close() throws IOException
11991199
{
12001200
super.close();
12011201

1202-
/* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
1203-
* scopes.
1204-
*/
1202+
// 05-Dec-2008, tatu: To add [JACKSON-27], need to close open scopes.
12051203
// First: let's see that we still have buffers...
1206-
if ((_outputBuffer != null)
1207-
&& isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
1208-
while (true) {
1209-
JsonStreamContext ctxt = getOutputContext();
1210-
if (ctxt.inArray()) {
1211-
writeEndArray();
1212-
} else if (ctxt.inObject()) {
1213-
writeEndObject();
1214-
} else {
1215-
break;
1204+
IOException flushFail = null;
1205+
try {
1206+
if ((_outputBuffer != null)
1207+
&& isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
1208+
while (true) {
1209+
JsonStreamContext ctxt = getOutputContext();
1210+
if (ctxt.inArray()) {
1211+
writeEndArray();
1212+
} else if (ctxt.inObject()) {
1213+
writeEndObject();
1214+
} else {
1215+
break;
1216+
}
12161217
}
12171218
}
1219+
_flushBuffer();
1220+
} catch (IOException e) {
1221+
// 10-Jun-2022, tatu: [core#764] Need to avoid failing here; may
1222+
// still need to close the underlying output stream
1223+
flushFail = e;
12181224
}
1219-
_flushBuffer();
1225+
12201226
_outputTail = 0; // just to ensure we don't think there's anything buffered
12211227

12221228
/* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
@@ -1226,15 +1232,26 @@ && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
12261232
* may not be properly recycled if we don't close the writer.
12271233
*/
12281234
if (_outputStream != null) {
1229-
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
1230-
_outputStream.close();
1231-
} else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
1232-
// If we can't close it, we should at least flush
1233-
_outputStream.flush();
1235+
try {
1236+
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
1237+
_outputStream.close();
1238+
} else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
1239+
// If we can't close it, we should at least flush
1240+
_outputStream.flush();
1241+
}
1242+
} catch (IOException | RuntimeException e) {
1243+
if (flushFail != null) {
1244+
e.addSuppressed(flushFail);
1245+
}
1246+
throw e;
12341247
}
12351248
}
12361249
// Internal buffer(s) generator has can now be released as well
12371250
_releaseBuffers();
1251+
1252+
if (flushFail != null) {
1253+
throw flushFail;
1254+
}
12381255
}
12391256

12401257
@Override

src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java

+34-16
Original file line numberDiff line numberDiff line change
@@ -981,20 +981,27 @@ public void close() throws IOException
981981

982982
// 05-Dec-2008, tatu: To add [JACKSON-27], need to close open scopes
983983
// First: let's see that we still have buffers...
984-
if (_outputBuffer != null
985-
&& isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
986-
while (true) {
987-
JsonStreamContext ctxt = getOutputContext();
988-
if (ctxt.inArray()) {
989-
writeEndArray();
990-
} else if (ctxt.inObject()) {
991-
writeEndObject();
992-
} else {
993-
break;
984+
IOException flushFail = null;
985+
try {
986+
if ((_outputBuffer != null)
987+
&& isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
988+
while (true) {
989+
JsonStreamContext ctxt = getOutputContext();
990+
if (ctxt.inArray()) {
991+
writeEndArray();
992+
} else if (ctxt.inObject()) {
993+
writeEndObject();
994+
} else {
995+
break;
996+
}
994997
}
995998
}
999+
_flushBuffer();
1000+
} catch (IOException e) {
1001+
// 10-Jun-2022, tatu: [core#764] Need to avoid failing here; may
1002+
// still need to close the underlying output stream
1003+
flushFail = e;
9961004
}
997-
_flushBuffer();
9981005
_outputHead = 0;
9991006
_outputTail = 0;
10001007

@@ -1005,15 +1012,26 @@ && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
10051012
* may not be properly recycled if we don't close the writer.
10061013
*/
10071014
if (_writer != null) {
1008-
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
1009-
_writer.close();
1010-
} else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
1011-
// If we can't close it, we should at least flush
1012-
_writer.flush();
1015+
try {
1016+
if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
1017+
_writer.close();
1018+
} else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
1019+
// If we can't close it, we should at least flush
1020+
_writer.flush();
1021+
}
1022+
} catch (IOException | RuntimeException e) {
1023+
if (flushFail != null) {
1024+
e.addSuppressed(flushFail);
1025+
}
1026+
throw e;
10131027
}
10141028
}
10151029
// Internal buffer(s) generator has can now be released as well
10161030
_releaseBuffers();
1031+
1032+
if (flushFail != null) {
1033+
throw flushFail;
1034+
}
10171035
}
10181036

10191037
@Override

src/test/java/com/fasterxml/jackson/core/json/InputStreamInitTest.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ static class FailingJsonFactory extends JsonFactory {
3131
public FailingInputStream lastStream;
3232

3333
@Override
34-
protected InputStream _fileInputStream(File f) throws IOException {
34+
protected InputStream _fileInputStream(File f) {
3535
return (lastStream = new FailingInputStream());
3636
}
3737

3838
@Override
39-
protected InputStream _optimizedStreamFromURL(URL url) throws IOException {
39+
protected InputStream _optimizedStreamFromURL(URL url) {
4040
return (lastStream = new FailingInputStream());
4141
}
4242
}
@@ -47,7 +47,7 @@ public void testForFile() throws Exception
4747
try {
4848
/*JsonParser p =*/ jsonF.createParser(new File("/tmp/test.json"));
4949
fail("Should not pass");
50-
} catch (IOException e) {
50+
} catch (Exception e) {
5151
verifyException(e, "Will not read");
5252
}
5353
assertNotNull(jsonF.lastStream);
@@ -60,7 +60,7 @@ public void testForURL() throws Exception
6060
try {
6161
/*JsonParser p =*/ jsonF.createParser(new URL("http://localhost:80/"));
6262
fail("Should not pass");
63-
} catch (IOException e) {
63+
} catch (Exception e) {
6464
verifyException(e, "Will not read");
6565
}
6666
assertNotNull(jsonF.lastStream);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.fasterxml.jackson.core.json;
2+
3+
import java.io.*;
4+
5+
import com.fasterxml.jackson.core.JsonEncoding;
6+
import com.fasterxml.jackson.core.JsonFactory;
7+
import com.fasterxml.jackson.core.JsonGenerator;
8+
9+
//[core#764] (and [databind#3508]
10+
public class OutputStreamInitTest
11+
extends com.fasterxml.jackson.core.BaseTest
12+
{
13+
static class FailingOutputStream extends OutputStream {
14+
public int written = 0;
15+
public boolean failWrites = false;
16+
public boolean closed = false;
17+
18+
public void startFailingWrites() {
19+
failWrites = true;
20+
}
21+
22+
@Override
23+
public void close() {
24+
closed = true;
25+
}
26+
27+
@Override
28+
public void write(int b) throws IOException {
29+
++written;
30+
if (failWrites) {
31+
throw new IOException("No writes!");
32+
}
33+
}
34+
}
35+
36+
static class FailingJsonFactory extends JsonFactory {
37+
private static final long serialVersionUID = 1L;
38+
39+
public FailingOutputStream lastStream;
40+
41+
@Override
42+
protected OutputStream _fileOutputStream(File f) {
43+
return (lastStream = new FailingOutputStream());
44+
}
45+
}
46+
47+
public void testForFile() throws Exception
48+
{
49+
final FailingJsonFactory jsonF = new FailingJsonFactory();
50+
try {
51+
JsonGenerator g = jsonF.createGenerator(new File("/tmp/test.json"),
52+
JsonEncoding.UTF8);
53+
g.writeString("foo");
54+
jsonF.lastStream.startFailingWrites();
55+
g.close();
56+
fail("Should not pass");
57+
} catch (Exception e) {
58+
verifyException(e, "No writes");
59+
}
60+
assertNotNull(jsonF.lastStream);
61+
assertTrue(jsonF.lastStream.closed);
62+
}
63+
64+
}

0 commit comments

Comments
 (0)