diff --git a/pom.xml b/pom.xml index 8160dc8d05..e6248d9e02 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,12 @@ - + + + junit + junit + test + org.powermock powermock-module-junit4 diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index df4fc5063c..bca0d70612 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.databind.node.*; import com.fasterxml.jackson.databind.ser.*; import com.fasterxml.jackson.databind.type.*; +import com.fasterxml.jackson.databind.util.ClassUtil; import com.fasterxml.jackson.databind.util.RootNameLookup; import com.fasterxml.jackson.databind.util.StdDateFormat; import com.fasterxml.jackson.databind.util.TokenBuffer; @@ -3560,7 +3561,7 @@ protected Object _convert(Object fromValue, JavaType toValueType) } else { // pointing to event other than null DeserializationContext ctxt = createDeserializationContext(jp, deserConfig); JsonDeserializer deser = _findRootDeserializer(ctxt, toValueType); - // note: no handling of unwarpping + // note: no handling of unwrapping result = deser.deserialize(jp, ctxt); } jp.close(); @@ -3657,25 +3658,13 @@ protected final void _configAndWriteValue(JsonGenerator g, Object value) _configAndWriteCloseable(g, value, cfg); return; } - boolean closed = false; try { _serializerProvider(cfg).serializeValue(g, value); - closed = true; - g.close(); - } finally { - /* won't try to close twice; also, must catch exception (so it - * will not mask exception that is pending) - */ - if (!closed) { - /* 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of - * structures, which typically causes more damage. - */ - g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); - try { - g.close(); - } catch (IOException ioe) { } - } + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIAE(g, e); + return; } + g.close(); } /** @@ -3688,30 +3677,16 @@ private final void _configAndWriteCloseable(JsonGenerator g, Object value, Seria Closeable toClose = (Closeable) value; try { _serializerProvider(cfg).serializeValue(g, value); - JsonGenerator tmpGen = g; - g = null; - tmpGen.close(); Closeable tmpToClose = toClose; toClose = null; tmpToClose.close(); - } finally { - // Need to close both generator and value, as long as they haven't yet been closed - if (g != null) { - // 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of - // structures, which typically causes more damage. - g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); - try { - g.close(); - } catch (IOException ioe) { } - } - if (toClose != null) { - try { - toClose.close(); - } catch (IOException ioe) { } - } + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIAE(g, toClose, e); + return; } + g.close(); } - + /** * Helper method used when value to serialize is {@link Closeable} and its close() * method is to be called right after serialization has been called @@ -3725,16 +3700,11 @@ private final void _writeCloseableValue(JsonGenerator g, Object value, Serializa if (cfg.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { g.flush(); } - Closeable tmpToClose = toClose; - toClose = null; - tmpToClose.close(); - } finally { - if (toClose != null) { - try { - toClose.close(); - } catch (IOException ioe) { } - } + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIAE(null, toClose, e); + return; } + toClose.close(); } /* diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java index 1758925d05..1e71f7c4a9 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.databind.ser.*; import com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer; import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.util.ClassUtil; /** * Builder object that can be used for per-serialization configuration of @@ -903,8 +904,7 @@ public ContextAttributes getAttributes() { * Method that can be used to serialize any Java value as * JSON output, using provided {@link JsonGenerator}. */ - public void writeValue(JsonGenerator gen, Object value) - throws IOException, JsonGenerationException, JsonMappingException + public void writeValue(JsonGenerator gen, Object value) throws IOException { _configureGenerator(gen); if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) @@ -916,16 +916,11 @@ public void writeValue(JsonGenerator gen, Object value) if (_config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { gen.flush(); } - Closeable tmpToClose = toClose; - toClose = null; - tmpToClose.close(); - } finally { - if (toClose != null) { - try { - toClose.close(); - } catch (IOException ioe) { } - } + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIAE(null, toClose, e); + return; } + toClose.close(); } else { _prefetch.serialize(gen, value, _serializerProvider()); if (_config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) { @@ -1124,24 +1119,13 @@ protected final void _configAndWriteValue(JsonGenerator gen, Object value) throw _writeCloseable(gen, value); return; } - boolean closed = false; try { _prefetch.serialize(gen, value, _serializerProvider()); - closed = true; - gen.close(); - } finally { - // won't try to close twice; also, must catch exception (so it - // will not mask exception that is pending) - if (!closed) { - /* 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of - * structures, which typically causes more damage. - */ - gen.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); - try { - gen.close(); - } catch (IOException ioe) { } - } + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIAE(gen, e); + return; } + gen.close(); } /** @@ -1154,31 +1138,14 @@ private final void _writeCloseable(JsonGenerator gen, Object value) Closeable toClose = (Closeable) value; try { _prefetch.serialize(gen, value, _serializerProvider()); - JsonGenerator tmpGen = gen; - gen = null; - tmpGen.close(); Closeable tmpToClose = toClose; toClose = null; tmpToClose.close(); - } finally { - /* Need to close both generator and value, as long as they haven't yet - * been closed - */ - if (gen != null) { - /* 04-Mar-2014, tatu: But! Let's try to prevent auto-closing of - * structures, which typically causes more damage. - */ - gen.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); - try { - gen.close(); - } catch (IOException ioe) { } - } - if (toClose != null) { - try { - toClose.close(); - } catch (IOException ioe) { } - } + } catch (Exception e) { + ClassUtil.closeOnFailAndThrowAsIAE(gen, toClose, e); + return; } + gen.close(); } /** diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java b/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java index 3c78f4dfd6..ca0e27f4bd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java +++ b/src/main/java/com/fasterxml/jackson/databind/ext/Java7Support.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.PropertyName; import com.fasterxml.jackson.databind.introspect.Annotated; import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; -import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; /** * To support Java7-incomplete platforms, we will offer support for JDK 7 @@ -24,7 +23,7 @@ public abstract class Java7Support impl = (Java7Support) cls.newInstance(); } catch (Throwable t) { // 24-Nov-2015, tatu: Should we log or not? - java.util.logging.Logger.getLogger(JacksonAnnotationIntrospector.class.getName()) + java.util.logging.Logger.getLogger(Java7Support.class.getName()) .warning("Unable to load JDK7 types (annotations, java.nio.file.Path): no Java7 support added"); } IMPL = impl; diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java b/src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java index 89f6751e94..9d6a49aecd 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java +++ b/src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java @@ -10,6 +10,9 @@ import com.fasterxml.jackson.databind.introspect.AnnotatedParameter; import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams; +/** + * @since 2.8 + */ public class Java7SupportImpl extends Java7Support { @SuppressWarnings("unused") // compiler warns, just needed side-effects diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java index 9642bec023..ea67b88cca 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java @@ -50,8 +50,7 @@ public JsonSerializer createContextual(SerializerProvider serializers, BeanProperty property) throws JsonMappingException { if (property != null) { - JsonFormat.Value format = findFormatOverrides(serializers, property, - handledType()); + JsonFormat.Value format = findFormatOverrides(serializers, property, handledType()); if (format != null) { // Simple case first: serialize as numeric timestamp? JsonFormat.Shape shape = format.getShape(); diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java index 438863671e..f18beb4757 100644 --- a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java +++ b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java @@ -1,9 +1,12 @@ package com.fasterxml.jackson.databind.util; +import java.io.Closeable; +import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.*; import java.util.*; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; @@ -567,6 +570,73 @@ public static void unwrapAndThrowAsIAE(Throwable t, String msg) throwAsIAE(getRootCause(t), msg); } + /** + * Helper method that encapsulate logic in trying to close output generator + * in case of failure; useful mostly in forcing flush()ing as otherwise + * error conditions tend to be hard to diagnose. However, it is often the + * case that output state may be corrupt so we need to be prepared for + * secondary exception without masking original one. + * + * @since 2.8 + */ + public static void closeOnFailAndThrowAsIAE(JsonGenerator g, Exception fail) + throws IOException + { + /* 04-Mar-2014, tatu: Let's try to prevent auto-closing of + * structures, which typically causes more damage. + */ + g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); + try { + g.close(); + } catch (Exception e) { + fail.addSuppressed(e); + } + if (fail instanceof IOException) { + throw (IOException) fail; + } + if (fail instanceof RuntimeException) { + throw (RuntimeException) fail; + } + throw new RuntimeException(fail); + } + + /** + * Helper method that encapsulate logic in trying to close given {@link Closeable} + * in case of failure; useful mostly in forcing flush()ing as otherwise + * error conditions tend to be hard to diagnose. However, it is often the + * case that output state may be corrupt so we need to be prepared for + * secondary exception without masking original one. + * + * @since 2.8 + */ + public static void closeOnFailAndThrowAsIAE(JsonGenerator g, + Closeable toClose, Exception fail) + throws IOException + { + if (g != null) { + g.disable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT); + try { + g.close(); + } catch (Exception e) { + fail.addSuppressed(e); + } + } + if (toClose != null) { + try { + toClose.close(); + } catch (Exception e) { + fail.addSuppressed(e); + } + } + if (fail instanceof IOException) { + throw (IOException) fail; + } + if (fail instanceof RuntimeException) { + throw (RuntimeException) fail; + } + throw new RuntimeException(fail); + } + /* /********************************************************** /* Instantiation diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java index 873a5569e6..84e8c7c0b9 100644 --- a/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java +++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java @@ -1,6 +1,5 @@ package com.fasterxml.jackson.databind.creators; -import java.awt.Point; // just for convenience import java.math.BigDecimal; import java.util.*; diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java b/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java index ce98ddd871..a8342bd861 100644 --- a/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java +++ b/src/test/java/com/fasterxml/jackson/databind/filter/IgnorePropsForSerTest.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.BaseMapTest.Point; public class IgnorePropsForSerTest extends BaseMapTest