[jackson-jaxrs-providers] 136/162: Implement #49, addition of `JaxRSFeature.ALLOW_EMPTY_INPUT`
Timo Aaltonen
tjaalton at moszumanska.debian.org
Mon Sep 8 22:16:36 UTC 2014
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 <tatu.saloranta at iki.fi>
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 at 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 at 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
More information about the pkg-java-commits
mailing list