Skip to content

Commit c0d4296

Browse files
committed
Fix #2382: allow AutoCloseable closing with SerializationFeature.CLOSE_CLOSEABLE
1 parent 0802c04 commit c0d4296

File tree

6 files changed

+53
-28
lines changed

6 files changed

+53
-28
lines changed

release-notes/VERSION

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ Versions: 3.x (for earlier see VERSION-2.x)
4343
#2040: Remove `JsonSerializer.isEmpty()` from 3.0
4444
#2176: Add `JsonMapper.shared()` static method
4545
#2177: Change parent type of `JsonProcessingException` to be `RuntimeException`
46+
#2382: Support `AutoCloseable` with `SerializationFeature.CLOSE_CLOSEABLE`
47+
(requested by @Logic-32)
4648
#2405: Make `java.sql.Date` serializer default to same output as `java.util.Date`
4749
#2411: `valueToTree()` during serialization (via `SerializerProvider()`)
4850
(requested by XakepSDK@github)

src/main/java/tools/jackson/databind/ObjectMapper.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -1232,7 +1232,8 @@ public void writeValue(JsonGenerator g, Object value) throws JacksonException
12321232
}
12331233
}
12341234
*/
1235-
if (config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
1235+
if (config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE)
1236+
&& (value instanceof AutoCloseable)) {
12361237
_writeCloseableValue(g, value, config);
12371238
} else {
12381239
_serializationContext(config).serializeValue(g, value);
@@ -1876,7 +1877,8 @@ protected final void _configAndWriteValue(SerializationContextExt prov,
18761877
JsonGenerator g, Object value)
18771878
throws JacksonException
18781879
{
1879-
if (prov.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
1880+
if (prov.isEnabled(SerializationFeature.CLOSE_CLOSEABLE)
1881+
&& (value instanceof AutoCloseable)) {
18801882
_configAndWriteCloseable(prov, g, value);
18811883
return;
18821884
}
@@ -1897,10 +1899,10 @@ private final void _configAndWriteCloseable(SerializationContextExt prov,
18971899
JsonGenerator g, Object value)
18981900
throws JacksonException
18991901
{
1900-
Closeable toClose = (Closeable) value;
1902+
AutoCloseable toClose = (AutoCloseable) value;
19011903
try {
19021904
prov.serializeValue(g, value);
1903-
Closeable tmpToClose = toClose;
1905+
AutoCloseable tmpToClose = toClose;
19041906
toClose = null;
19051907
tmpToClose.close();
19061908
} catch (Exception e) {

src/main/java/tools/jackson/databind/ObjectWriter.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -950,9 +950,9 @@ public void writeValue(JsonGenerator g, Object value)
950950
{
951951
_assertNotNull("g", g);
952952
if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE)
953-
&& (value instanceof Closeable))
953+
&& (value instanceof AutoCloseable))
954954
{
955-
Closeable toClose = (Closeable) value;
955+
AutoCloseable toClose = (AutoCloseable) value;
956956
try {
957957
_prefetch.serialize(g, value, _serializationContext());
958958
if (_config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) {
@@ -966,6 +966,8 @@ public void writeValue(JsonGenerator g, Object value)
966966
toClose.close();
967967
} catch (IOException e) {
968968
throw JacksonIOException.construct(e, g);
969+
} catch (Exception e) {
970+
ClassUtil.closeOnFailAndThrowAsJacksonE(g, e);
969971
}
970972
} else {
971973
_prefetch.serialize(g, value, _serializationContext());
@@ -1101,7 +1103,8 @@ public byte[] writeValueAsBytes(Object value) throws JacksonException
11011103
protected final void _configAndWriteValue(SerializationContextExt ctxt,
11021104
JsonGenerator gen, Object value) throws JacksonException
11031105
{
1104-
if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
1106+
if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE)
1107+
&& (value instanceof AutoCloseable)) {
11051108
_writeCloseable(gen, value);
11061109
return;
11071110
}
@@ -1121,10 +1124,10 @@ protected final void _configAndWriteValue(SerializationContextExt ctxt,
11211124
private final void _writeCloseable(JsonGenerator gen, Object value)
11221125
throws JacksonException
11231126
{
1124-
Closeable toClose = (Closeable) value;
1127+
AutoCloseable toClose = (AutoCloseable) value;
11251128
try {
11261129
_prefetch.serialize(gen, value, _serializationContext());
1127-
Closeable tmpToClose = toClose;
1130+
AutoCloseable tmpToClose = toClose;
11281131
toClose = null;
11291132
tmpToClose.close();
11301133
} catch (Exception e) {

src/main/java/tools/jackson/databind/SerializationFeature.java

+19-14
Original file line numberDiff line numberDiff line change
@@ -129,22 +129,27 @@ public enum SerializationFeature implements ConfigFeature
129129
/**********************************************************************
130130
*/
131131

132-
/**
133-
* Feature that determines whether <code>close</code> method of
134-
* serialized <b>root level</b> objects (ones for which <code>ObjectMapper</code>'s
135-
* writeValue() (or equivalent) method is called)
136-
* that implement {@link java.io.Closeable}
137-
* is called after serialization or not. If enabled, <b>close()</b> will
138-
* be called after serialization completes (whether succesfully, or
139-
* due to an error manifested by an exception being thrown). You can
140-
* think of this as sort of "finally" processing.
141-
*<p>
142-
* NOTE: only affects behavior with <b>root</b> objects, and not other
143-
* objects reachable from the root object. Put another way, only one
144-
* call will be made for each 'writeValue' call.
132+
/**
133+
* Feature that determines whether {@code close()} method of
134+
* serialized <b>root level</b> objects (ones for which {@link ObjectMapper}'s
135+
* (and {@link ObjectWriter}'s)
136+
* writeValue() (or equivalent) method is called)
137+
* that implement {@link java.lang.AutoCloseable}
138+
* is called after serialization or not. If enabled, <b>close()</b> will
139+
* be called after serialization completes (whether successfully, or
140+
* due to an error manifested by an exception being thrown). You can
141+
* think of this as sort of "finally" processing.
142+
*<p>
143+
* NOTE: only affects behavior with <b>root</b> objects, and not other
144+
* objects reachable from the root object. Put another way, only one
145+
* call will be made for each 'writeValue' call.
146+
*<p>
147+
* NOTE: in Jackson 2.x this used to only apply to {@link java.io.Closeable}s,
148+
* but now it also applies to {@link java.lang.AutoCloseable}s as well (as
149+
* of Jackson 3.0).
145150
*<p>
146151
* Feature is disabled by default.
147-
*/
152+
*/
148153
CLOSE_CLOSEABLE(false),
149154

150155
/**

src/main/java/tools/jackson/databind/util/ClassUtil.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ public static void closeOnFailAndThrowAsJacksonE(JsonGenerator g, Exception fail
391391
* secondary exception without masking original one.
392392
*/
393393
public static void closeOnFailAndThrowAsJacksonE(JsonGenerator g,
394-
Closeable toClose, Exception fail)
394+
AutoCloseable toClose, Exception fail)
395395
throws JacksonException
396396
{
397397
if (g != null) {

src/test/java/tools/jackson/databind/ser/SerializationFeaturesTest.java

+17-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
public class SerializationFeaturesTest
1919
extends DatabindTestUtil
2020
{
21-
static class CloseableBean implements Closeable
21+
static class CloseableBean implements AutoCloseable
2222
{
2323
public int a = 3;
2424

@@ -43,7 +43,7 @@ private static class StringListBean {
4343
/**********************************************************
4444
*/
4545

46-
private final ObjectMapper MAPPER = new ObjectMapper();
46+
private final ObjectMapper MAPPER = newJsonMapper();
4747

4848
@SuppressWarnings("resource")
4949
@Test
@@ -54,10 +54,23 @@ public void testCloseCloseable() throws IOException
5454
MAPPER.writeValueAsString(bean);
5555
assertFalse(bean.wasClosed);
5656

57-
// but can enable it:
57+
// via writer as well
5858
bean = new CloseableBean();
5959
MAPPER.writer()
60-
.with(SerializationFeature.CLOSE_CLOSEABLE)
60+
.writeValueAsString(bean);
61+
assertFalse(bean.wasClosed);
62+
63+
// but can enable it:
64+
ObjectMapper mapper2 = jsonMapperBuilder()
65+
.enable(SerializationFeature.CLOSE_CLOSEABLE)
66+
.build();
67+
bean = new CloseableBean();
68+
mapper2.writeValueAsString(bean);
69+
assertTrue(bean.wasClosed);
70+
71+
// and same via writer
72+
bean = new CloseableBean();
73+
mapper2.writer()
6174
.writeValueAsString(bean);
6275
assertTrue(bean.wasClosed);
6376

0 commit comments

Comments
 (0)