This is an automated email from the git hooks/post-receive script. tjaalton pushed a commit to branch master in repository jackson-jaxrs-providers.
commit 772ef5b6f51a4ac73fa3469618c6db9b97b5619b Author: Tatu Saloranta <[email protected]> Date: Sun Apr 6 17:15:03 2014 -0700 Implement #49, addition of `JaxRSFeature.ALLOW_EMPTY_INPUT` --- .../fasterxml/jackson/jaxrs/base/ProviderBase.java | 72 +++++++++++++++++----- .../fasterxml/jackson/jaxrs/cfg/JaxRSFeature.java | 28 +++++++++ .../jackson/jaxrs/json/TestCanDeserialize.java | 44 +++++++++++-- pom.xml | 6 ++ release-notes/VERSION | 13 ++-- 5 files changed, 138 insertions(+), 25 deletions(-) diff --git a/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java index 6ebacf7..75653e2 100644 --- a/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java +++ b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java @@ -2,8 +2,10 @@ package com.fasterxml.jackson.jaxrs.base; import java.io.*; import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; import java.lang.reflect.Type; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; import javax.ws.rs.core.*; import javax.ws.rs.ext.MessageBodyReader; @@ -32,6 +34,15 @@ public abstract class ProviderBase< public final static String HEADER_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options"; /** + * Since class <code>javax.ws.rs.core.NoContentException</code> only exists in + * JAX-RS 2.0, but we need 1.1 compatibility, need to (unfortunately!) dynamically + * load class. + */ + protected final static String CLASS_NAME_NO_CONTENT_EXCEPTION = "javax.ws.rs.core.NoContentException"; + + protected final static String NO_CONTENT_MESSAGE = "No content (empty input stream)"; + + /** * Looks like we need to worry about accidental * data binding for types we shouldn't be handling. This is * probably not a very good way to do it, but let's start by @@ -77,6 +88,8 @@ public abstract class ProviderBase< StreamingOutput.class, Response.class }; + protected final static int JAXRS_FEATURE_DEFAULTS = JaxRSFeature.collectDefaults(); + /* /********************************************************** /* General configuration @@ -84,6 +97,12 @@ public abstract class ProviderBase< */ /** + * Helper object used for encapsulating configuration aspects + * of {@link ObjectMapper} + */ + protected final MAPPER_CONFIG _mapperConfig; + + /** * Map that contains overrides to default list of untouchable * types: <code>true</code> meaning that entry is untouchable, * <code>false</code> that is is not. @@ -128,7 +147,7 @@ public abstract class ProviderBase< /* Excluded types /********************************************************** */ - + public final static HashSet<ClassKey> _untouchables = DEFAULT_UNTOUCHABLES; public final static Class<?>[] _unreadableClasses = DEFAULT_UNREADABLES; @@ -152,27 +171,19 @@ public abstract class ProviderBase< */ protected final LRUMap<AnnotationBundleKey, EP_CONFIG> _writers = new LRUMap<AnnotationBundleKey, EP_CONFIG>(16, 120); - - /* - /********************************************************** - /* General configuration - /********************************************************** - */ - - /** - * Helper object used for encapsulating configuration aspects - * of {@link ObjectMapper} - */ - protected final MAPPER_CONFIG _mapperConfig; + + protected final AtomicReference<IOException> _noContentExceptionRef + = new AtomicReference<IOException>(); /* /********************************************************** /* Life-cycle /********************************************************** */ - + protected ProviderBase(MAPPER_CONFIG mconfig) { _mapperConfig = mconfig; + _jaxRSFeatures = JAXRS_FEATURE_DEFAULTS; } /** @@ -182,9 +193,10 @@ public abstract class ProviderBase< * Should NOT be used by any code explicitly; only exists * for proxy support. */ - @Deprecated // just to denote it should NOT be directly called; will not be removed + @Deprecated // just to denote it should NOT be directly called; will NOT be removed protected ProviderBase() { _mapperConfig = null; + _jaxRSFeatures = JAXRS_FEATURE_DEFAULTS; } /* @@ -760,8 +772,19 @@ public abstract class ProviderBase< JsonParser jp = _createParser(reader, entityStream); // If null is returned, considered to be empty stream + // 05-Apr-2014, tatu: As per [Issue#49], behavior here is configurable. if (jp == null || jp.nextToken() == null) { - return null; + if (JaxRSFeature.ALLOW_EMPTY_INPUT.enabledIn(_jaxRSFeatures)) { + return null; + } + /* 05-Apr-2014, tatu: Trick-ee. NoContentFoundException only available in JAX-RS 2.0... + * so need bit of obfuscated code to reach it. + */ + IOException fail = _noContentExceptionRef.get(); + if (fail == null) { + fail = _createNoContentException(); + } + throw fail; } // [Issue#1]: allow 'binding' to JsonParser if (((Class<?>) type) == JsonParser.class) { @@ -846,6 +869,23 @@ public abstract class ProviderBase< return JsonParser.class == type; } + /** + * @since 2.4 + */ + protected IOException _createNoContentException() + { + Class<?> cls = null; + try { + cls = Class.forName(CLASS_NAME_NO_CONTENT_EXCEPTION); + Constructor<?> ctor = cls.getDeclaredConstructor(String.class); + if (ctor != null) { + return (IOException) ctor.newInstance(NO_CONTENT_MESSAGE); + } + } catch (Exception e) { // no can do... + } + return new IOException(NO_CONTENT_MESSAGE); + } + /* /********************************************************** /* Private/sub-class helper methods diff --git a/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/JaxRSFeature.java b/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/JaxRSFeature.java index 9e415fb..c351ca6 100644 --- a/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/JaxRSFeature.java +++ b/base/src/main/java/com/fasterxml/jackson/jaxrs/cfg/JaxRSFeature.java @@ -11,6 +11,24 @@ public enum JaxRSFeature implements ConfigFeature { /* /****************************************************** + /* Input handling + /****************************************************** + */ + + /** + * Feature related to + * <a href="https://github.com/FasterXML/jackson-jaxrs-providers/issues/49">Issue #49</a>: + * whether empty input is considered legal or not. + * If set to true, empty content is allowed and will be read as Java 'null': if false, + * an {@link java.io.IOException} will be thrown. + *<p> + * NOTE: in case of JAX-RS 2.0, specific exception will be <code>javax.ws.rs.core.NoContentException</code>; + * but this is not defined in JAX-RS 1.x. + */ + ALLOW_EMPTY_INPUT(true), + + /* + /****************************************************** /* HTTP headers /****************************************************** */ @@ -39,9 +57,19 @@ public enum JaxRSFeature implements ConfigFeature _defaultState = defaultState; } + public static int collectDefaults() { + int flags = 0; + for (JaxRSFeature f : values()) { + if (f.enabledByDefault()) { flags |= f.getMask(); } + } + return flags; + } + @Override public boolean enabledByDefault() { return _defaultState; } @Override public int getMask() { return (1 << ordinal()); } + + public boolean enabledIn(int flags) { return (flags & getMask()) != 0; } } diff --git a/json/src/test/java/com/fasterxml/jackson/jaxrs/json/TestCanDeserialize.java b/json/src/test/java/com/fasterxml/jackson/jaxrs/json/TestCanDeserialize.java index dafb45c..5da49e2 100644 --- a/json/src/test/java/com/fasterxml/jackson/jaxrs/json/TestCanDeserialize.java +++ b/json/src/test/java/com/fasterxml/jackson/jaxrs/json/TestCanDeserialize.java @@ -9,13 +9,15 @@ import java.util.Map; import javax.ws.rs.core.MediaType; +import com.fasterxml.jackson.jaxrs.cfg.JaxRSFeature; + /** * Unit test to check [JACKSON-540] */ public class TestCanDeserialize extends JaxrsTestBase { @SuppressWarnings({ "unchecked", "rawtypes" }) - public void testCanSerialize() throws IOException { + public void testCanDeserialize() throws IOException { Map<String, Object> object = new LinkedHashMap<String, Object>(); JacksonJsonProvider prov = new JacksonJsonProvider(); @@ -29,15 +31,47 @@ public class TestCanDeserialize extends JaxrsTestBase { } @SuppressWarnings({ "unchecked", "rawtypes" }) - public void testCanSerializeEmpty() throws IOException { - Map<String, Object> object = new LinkedHashMap<String, Object>(); + public void testCanDeserializeEmpty() throws IOException { JacksonJsonProvider prov = new JacksonJsonProvider(); InputStream stream = new ByteArrayInputStream(new byte[0]); + Class<Object> type = _type(Map.class); - object = (Map) prov.readFrom(Object.class, object.getClass(), new Annotation[0], + Map<String, Object> result = (Map) prov.readFrom(type, type, new Annotation[0], MediaType.APPLICATION_JSON_TYPE, null, stream); - assertNull(object); + assertNull(result); } + + /** + * Unit test for verifying functioning of {@link JaxRSFeature#ALLOW_EMPTY_INPUT}. + */ + public void testFailingDeserializeEmpty() throws IOException { + JacksonJsonProvider prov = new JacksonJsonProvider(); + prov.disable(JaxRSFeature.ALLOW_EMPTY_INPUT); + + InputStream stream = new ByteArrayInputStream(new byte[0]); + Class<Object> type = _type(Map.class); + try { + prov.readFrom(type, type, new Annotation[0], + MediaType.APPLICATION_JSON_TYPE, null, stream); + fail("Should not succeed with passing of empty input"); + } catch (IOException e) { + verifyException(e, "no content"); + + final String clsName = e.getClass().getName(); + if ("javax.ws.rs.core.NoContentException".equals(clsName)) { + // Ideally, we'd get this + } else if (e.getClass() == IOException.class) { + // but for JAX-RS 1.x this'll do + } else { + fail("Unexpected exception type: "+clsName); + } + } + } + + @SuppressWarnings("unchecked") + private Class<Object> _type(Class<?> cls) { + return (Class<Object>) cls; + } } diff --git a/pom.xml b/pom.xml index 4390919..571b01c 100644 --- a/pom.xml +++ b/pom.xml @@ -62,8 +62,14 @@ --> <dependency> <groupId>javax.ws.rs</groupId> + <!-- 05-Apr-2014, tatu: JAX-RS 2.x has different artifact-id, "javax.ws.rs-api" + --> <artifactId>jsr311-api</artifactId> <version>1.1.1</version> +<!-- + <artifactId>javax.ws.rs-api</artifactId> + <version>2.0</version> +--> <scope>provided</scope> </dependency> diff --git a/release-notes/VERSION b/release-notes/VERSION index d4e9c47..a37b92b 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -5,16 +5,21 @@ Sub-modules: jackson-jaxrs-smile-provider jackson-jaxrs-xml-provider -2.3.3 (xx-xxx-2013) +2.4.0 (xx-xxx-2014) -#41: Try to resolve problems with RESTeasy, missing `_configForWriting` - override. - (reported by `tbroyer@github`) +#49: Add `JaxRSFeature.ALLOW_EMPTY_INPUT`, disabling of which can prevent + mapping of empty input into Java null value ------------------------------------------------------------------------ === History: === ------------------------------------------------------------------------ +2.3.3 (xx-xxx-2014) + +#41: Try to resolve problems with RESTeasy, missing `_configForWriting` + override. + (reported by `tbroyer@github`) + 2.3.2 (01-Mar-2014) #40: Allow use of "text/x-json" content type by default -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jackson-jaxrs-providers.git _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

