[jackson-jaxrs-providers] 19/162: Refactoring
Timo Aaltonen
tjaalton at moszumanska.debian.org
Mon Sep 8 22:16:24 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 b4768d07f3e63b3577ae2d63cdae1002efe41b9b
Author: Tatu Saloranta <tsaloranta at gmail.com>
Date: Fri Mar 15 22:46:34 2013 -0700
Refactoring
---
.../fasterxml/jackson/jaxrs/base/ProviderBase.java | 534 ++++++++-----------
.../jackson/jaxrs/base/cfg/EndpointConfigBase.java | 165 ++++++
.../jackson/jaxrs/json/JacksonJsonProvider.java | 591 ++-------------------
.../jaxrs/json/annotation/EndpointConfig.java | 153 +-----
4 files changed, 439 insertions(+), 1004 deletions(-)
diff --git a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java
similarity index 65%
copy from json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java
copy to base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java
index 65ec884..61f59ff 100644
--- a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java
+++ b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/ProviderBase.java
@@ -1,77 +1,39 @@
-package com.fasterxml.jackson.jaxrs.json;
+package com.fasterxml.jackson.jaxrs.base;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
-import javax.ws.rs.*;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.StreamingOutput;
-import javax.ws.rs.ext.*;
+import javax.ws.rs.core.*;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.util.LRUMap;
+import com.fasterxml.jackson.jaxrs.base.cfg.EndpointConfigBase;
+import com.fasterxml.jackson.jaxrs.base.cfg.MapperConfiguratorBase;
import com.fasterxml.jackson.jaxrs.base.util.AnnotationBundleKey;
import com.fasterxml.jackson.jaxrs.base.util.ClassKey;
-import com.fasterxml.jackson.jaxrs.json.annotation.EndpointConfig;
-import com.fasterxml.jackson.jaxrs.json.cfg.MapperConfigurator;
-
-/**
- * Basic implementation of JAX-RS abstractions ({@link MessageBodyReader},
- * {@link MessageBodyWriter}) needed for binding
- * JSON ("application/json") content to and from Java Objects ("POJO"s).
- *<p>
- * Actual data binding functionality is implemented by {@link ObjectMapper}:
- * mapper to use can be configured in multiple ways:
- * <ul>
- * <li>By explicitly passing mapper to use in constructor
- * <li>By explictly setting mapper to use by {@link #setMapper}
- * <li>By defining JAX-RS <code>Provider</code> that returns {@link ObjectMapper}s.
- * <li>By doing none of above, in which case a default mapper instance is
- * constructed (and configured if configuration methods are called)
- * </ul>
- * The last method ("do nothing specific") is often good enough; explicit passing
- * of Mapper is simple and explicit; and Provider-based method may make sense
- * with Depedency Injection frameworks, or if Mapper has to be configured differently
- * for different media types.
- *<p>
- * Note that the default mapper instance will be automatically created if
- * one of explicit configuration methods (like {@link #configure})
- * is called: if so, Provider-based introspection is <b>NOT</b> used, but the
- * resulting Mapper is used as configured.
- *<p>
- * Note: version 1.3 added a sub-class ({@link JacksonJaxbJsonProvider}) which
- * is configured by default to use both Jackson and JAXB annotations for configuration
- * (base class when used as-is defaults to using just Jackson annotations)
- *
- * @author Tatu Saloranta
- */
- at Provider
- at Consumes(MediaType.WILDCARD) // NOTE: required to support "non-standard" JSON variants
- at Produces(MediaType.WILDCARD)
-public class JacksonJsonProvider
+public abstract class ProviderBase<
+ THIS extends ProviderBase<THIS, MAPPER, ANN, EP_CONFIG, MAPPER_CONFIG>,
+ MAPPER extends ObjectMapper,
+ ANN extends Enum<ANN>,
+ EP_CONFIG extends EndpointConfigBase<EP_CONFIG>,
+ MAPPER_CONFIG extends MapperConfiguratorBase<MAPPER_CONFIG,MAPPER,ANN>
+>
implements
MessageBodyReader<Object>,
MessageBodyWriter<Object>,
Versioned
{
/**
- * Default annotation sets to use, if not explicitly defined during
- * construction: only Jackson annotations are used for the base
- * class. Sub-classes can use other settings.
- */
- public final static Annotations[] BASIC_ANNOTATIONS = {
- Annotations.JACKSON
- };
-
- /**
* 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
@@ -80,16 +42,16 @@ public class JacksonJsonProvider
* (why ClassKey? since plain old Class has no hashCode() defined,
* lookups are painfully slow)
*/
- public final static HashSet<ClassKey> _untouchables = new HashSet<ClassKey>();
+ protected final static HashSet<ClassKey> DEFAULT_UNTOUCHABLES = new HashSet<ClassKey>();
static {
// First, I/O things (direct matches)
- _untouchables.add(new ClassKey(java.io.InputStream.class));
- _untouchables.add(new ClassKey(java.io.Reader.class));
- _untouchables.add(new ClassKey(java.io.OutputStream.class));
- _untouchables.add(new ClassKey(java.io.Writer.class));
+ DEFAULT_UNTOUCHABLES.add(new ClassKey(java.io.InputStream.class));
+ DEFAULT_UNTOUCHABLES.add(new ClassKey(java.io.Reader.class));
+ DEFAULT_UNTOUCHABLES.add(new ClassKey(java.io.OutputStream.class));
+ DEFAULT_UNTOUCHABLES.add(new ClassKey(java.io.Writer.class));
// then some primitive types
- _untouchables.add(new ClassKey(char[].class));
+ DEFAULT_UNTOUCHABLES.add(new ClassKey(char[].class));
/* 28-Jan-2012, tatu: 1.x excluded some additional types;
* but let's relax these a bit:
@@ -98,15 +60,15 @@ public class JacksonJsonProvider
* [https://github.com/FasterXML/jackson-jaxrs-json-provider/issues/12]
* better revert this back, to make them untouchable again.
*/
- _untouchables.add(new ClassKey(String.class));
- _untouchables.add(new ClassKey(byte[].class));
+ DEFAULT_UNTOUCHABLES.add(new ClassKey(String.class));
+ DEFAULT_UNTOUCHABLES.add(new ClassKey(byte[].class));
}
/**
* These are classes that we never use for reading
* (never try to deserialize instances of these types).
*/
- public final static Class<?>[] _unreadableClasses = new Class<?>[] {
+ public final static Class<?>[] DEFAULT_UNREADABLES = new Class<?>[] {
InputStream.class, Reader.class
};
@@ -114,40 +76,16 @@ public class JacksonJsonProvider
* These are classes that we never use for writing
* (never try to serialize instances of these types).
*/
- public final static Class<?>[] _unwritableClasses = new Class<?>[] {
+ public final static Class<?>[] DEFAULT_UNWRITABLES = new Class<?>[] {
OutputStream.class, Writer.class,
StreamingOutput.class, Response.class
};
/*
/**********************************************************
- /* Bit of caching
- /**********************************************************
- */
-
- /**
- * Cache for resolved endpoint configurations when reading JSON data
- */
- protected final LRUMap<AnnotationBundleKey, EndpointConfig> _readers
- = new LRUMap<AnnotationBundleKey, EndpointConfig>(16, 120);
-
- /**
- * Cache for resolved endpoint configurations when writing JSON data
- */
- protected final LRUMap<AnnotationBundleKey, EndpointConfig> _writers
- = new LRUMap<AnnotationBundleKey, EndpointConfig>(16, 120);
-
- /*
- /**********************************************************
/* General configuration
/**********************************************************
*/
-
- /**
- * Helper object used for encapsulating configuration aspects
- * of {@link ObjectMapper}
- */
- protected final MapperConfigurator _mapperConfig;
/**
* Set of types (classes) that provider should ignore for data binding
@@ -155,34 +93,6 @@ public class JacksonJsonProvider
protected HashSet<ClassKey> _cfgCustomUntouchables;
/**
- * JSONP function name to use for automatic JSONP wrapping, if any;
- * if null, no JSONP wrapping is done.
- * Note that this is the default value that can be overridden on
- * per-endpoint basis.
- */
- protected String _jsonpFunctionName;
-
- /*
- /**********************************************************
- /* Context configuration
- /**********************************************************
- */
-
- /**
- * Injectable context object used to locate configured
- * instance of {@link ObjectMapper} to use for actual
- * serialization.
- */
- @Context
- protected Providers _providers;
-
- /*
- /**********************************************************
- /* Configuration
- /**********************************************************
- */
-
- /**
* Whether we want to actually check that Jackson has
* a serializer for given type. Since this should generally
* be the case (due to auto-discovery) and since the call
@@ -200,52 +110,48 @@ public class JacksonJsonProvider
/*
/**********************************************************
- /* Construction
+ /* Excluded types
/**********************************************************
*/
+
+ public final static HashSet<ClassKey> _untouchables = DEFAULT_UNTOUCHABLES;
- /**
- * Default constructor, usually used when provider is automatically
- * configured to be used with JAX-RS implementation.
+ public final static Class<?>[] _unreadableClasses = DEFAULT_UNREADABLES;
+
+ public final static Class<?>[] _unwritableClasses = DEFAULT_UNWRITABLES;
+
+ /*
+ /**********************************************************
+ /* Bit of caching
+ /**********************************************************
*/
- public JacksonJsonProvider()
- {
- this(null, BASIC_ANNOTATIONS);
- }
/**
- * @param annotationsToUse Annotation set(s) to use for configuring
- * data binding
+ * Cache for resolved endpoint configurations when reading JSON data
*/
- public JacksonJsonProvider(Annotations... annotationsToUse)
- {
- this(null, annotationsToUse);
- }
+ protected final LRUMap<AnnotationBundleKey, EP_CONFIG> _readers
+ = new LRUMap<AnnotationBundleKey, EP_CONFIG>(16, 120);
- public JacksonJsonProvider(ObjectMapper mapper)
- {
- this(mapper, BASIC_ANNOTATIONS);
- }
-
/**
- * Constructor to use when a custom mapper (usually components
- * like serializer/deserializer factories that have been configured)
- * is to be used.
- *
- * @param annotationsToUse Sets of annotations (Jackson, JAXB) that provider should
- * support
+ * Cache for resolved endpoint configurations when writing JSON data
*/
- public JacksonJsonProvider(ObjectMapper mapper, Annotations[] annotationsToUse)
- {
- _mapperConfig = new MapperConfigurator(mapper, annotationsToUse);
- }
-
+ protected final LRUMap<AnnotationBundleKey, EP_CONFIG> _writers
+ = new LRUMap<AnnotationBundleKey, EP_CONFIG>(16, 120);
+
+ /*
+ /**********************************************************
+ /* General configuration
+ /**********************************************************
+ */
+
/**
- * Method that will return version information stored in and read from jar
- * that contains this class.
+ * Helper object used for encapsulating configuration aspects
+ * of {@link ObjectMapper}
*/
- public Version version() {
- return PackageVersion.VERSION;
+ protected final MAPPER_CONFIG _mapperConfig;
+
+ protected ProviderBase(MAPPER_CONFIG mconfig) {
+ _mapperConfig = mconfig;
}
/*
@@ -269,6 +175,23 @@ public class JacksonJsonProvider
public void checkCanSerialize(boolean state) { _cfgCheckCanSerialize = state; }
/**
+ * Method for marking specified type as "untouchable", meaning that provider
+ * will not try to read or write values of this type (or its subtypes).
+ *
+ * @param type Type to consider untouchable; can be any kind of class,
+ * including abstract class or interface. No instance of this type
+ * (including subtypes, i.e. types assignable to this type) will
+ * be read or written by provider
+ */
+ public void addUntouchable(Class<?> type)
+ {
+ if (_cfgCustomUntouchables == null) {
+ _cfgCustomUntouchables = new HashSet<ClassKey>();
+ }
+ _cfgCustomUntouchables.add(new ClassKey(type));
+ }
+
+ /**
* Method for configuring which annotation sets to use (including none).
* Annotation sets are defined in order decreasing precedence; that is,
* first one has the priority over following ones.
@@ -276,7 +199,7 @@ public class JacksonJsonProvider
* @param annotationsToUse Ordered list of annotation sets to use; if null,
* default
*/
- public void setAnnotationsToUse(Annotations[] annotationsToUse) {
+ public void setAnnotationsToUse(ANN[] annotationsToUse) {
_mapperConfig.setAnnotationsToUse(annotationsToUse);
}
@@ -285,174 +208,96 @@ public class JacksonJsonProvider
* for serialization and deserialization; if null, will use the standard
* provider discovery from context instead. Default setting is null.
*/
- public void setMapper(ObjectMapper m) {
+ public void setMapper(MAPPER m) {
_mapperConfig.setMapper(m);
}
- public JacksonJsonProvider configure(DeserializationFeature f, boolean state) {
+ public THIS configure(DeserializationFeature f, boolean state) {
_mapperConfig.configure(f, state);
- return this;
+ return _this();
}
- public JacksonJsonProvider configure(SerializationFeature f, boolean state) {
+ public THIS configure(SerializationFeature f, boolean state) {
_mapperConfig.configure(f, state);
- return this;
+ return _this();
}
- public JacksonJsonProvider configure(JsonParser.Feature f, boolean state) {
+ public THIS configure(JsonParser.Feature f, boolean state) {
_mapperConfig.configure(f, state);
- return this;
+ return _this();
}
- public JacksonJsonProvider configure(JsonGenerator.Feature f, boolean state) {
+ public THIS configure(JsonGenerator.Feature f, boolean state) {
_mapperConfig.configure(f, state);
- return this;
+ return _this();
}
- public JacksonJsonProvider enable(DeserializationFeature f, boolean state) {
+ public THIS enable(DeserializationFeature f, boolean state) {
_mapperConfig.configure(f, true);
- return this;
+ return _this();
}
- public JacksonJsonProvider enable(SerializationFeature f, boolean state) {
+ public THIS enable(SerializationFeature f, boolean state) {
_mapperConfig.configure(f, true);
- return this;
+ return _this();
}
- public JacksonJsonProvider enable(JsonParser.Feature f, boolean state) {
+ public THIS enable(JsonParser.Feature f, boolean state) {
_mapperConfig.configure(f, true);
- return this;
+ return _this();
}
- public JacksonJsonProvider enable(JsonGenerator.Feature f, boolean state) {
+ public THIS enable(JsonGenerator.Feature f, boolean state) {
_mapperConfig.configure(f, true);
- return this;
+ return _this();
}
- public JacksonJsonProvider disable(DeserializationFeature f, boolean state) {
+ public THIS disable(DeserializationFeature f, boolean state) {
_mapperConfig.configure(f, false);
- return this;
+ return _this();
}
- public JacksonJsonProvider disable(SerializationFeature f, boolean state) {
+ public THIS disable(SerializationFeature f, boolean state) {
_mapperConfig.configure(f, false);
- return this;
+ return _this();
}
- public JacksonJsonProvider disable(JsonParser.Feature f, boolean state) {
+ public THIS disable(JsonParser.Feature f, boolean state) {
_mapperConfig.configure(f, false);
- return this;
+ return _this();
}
- public JacksonJsonProvider disable(JsonGenerator.Feature f, boolean state) {
+ public THIS disable(JsonGenerator.Feature f, boolean state) {
_mapperConfig.configure(f, false);
- return this;
+ return _this();
}
- /**
- * Method for marking specified type as "untouchable", meaning that provider
- * will not try to read or write values of this type (or its subtypes).
- *
- * @param type Type to consider untouchable; can be any kind of class,
- * including abstract class or interface. No instance of this type
- * (including subtypes, i.e. types assignable to this type) will
- * be read or written by provider
- */
- public void addUntouchable(Class<?> type)
- {
- if (_cfgCustomUntouchables == null) {
- _cfgCustomUntouchables = new HashSet<ClassKey>();
- }
- _cfgCustomUntouchables.add(new ClassKey(type));
- }
-
- public void setJSONPFunctionName(String fname) {
- _jsonpFunctionName = fname;
- }
-
/*
/**********************************************************
- /* MessageBodyReader impl
+ /* Abstract methods sub-classes need to implement
/**********************************************************
*/
-
- /**
- * Method that JAX-RS container calls to try to check whether
- * values of given type (and media type) can be deserialized by
- * this provider.
- * Implementation will first check that expected media type is
- * a JSON type (via call to {@link #isJsonType}; then verify
- * that type is not one of "untouchable" types (types we will never
- * automatically handle), and finally that there is a deserializer
- * for type (iff {@link #checkCanDeserialize} has been called with
- * true argument -- otherwise assumption is there will be a handler)
- */
- public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
- {
- if (!isJsonType(mediaType)) {
- return false;
- }
-
- /* Ok: looks like we must weed out some core types here; ones that
- * make no sense to try to bind from JSON:
- */
- if (_untouchables.contains(new ClassKey(type))) {
- return false;
- }
- // and there are some other abstract/interface types to exclude too:
- for (Class<?> cls : _unreadableClasses) {
- if (cls.isAssignableFrom(type)) {
- return false;
- }
- }
- // as well as possible custom exclusions
- if (_containedIn(type, _cfgCustomUntouchables)) {
- return false;
- }
-
- // Finally: if we really want to verify that we can serialize, we'll check:
- if (_cfgCheckCanSerialize) {
- ObjectMapper mapper = locateMapper(type, mediaType);
- if (!mapper.canDeserialize(mapper.constructType(type))) {
- return false;
- }
- }
- return true;
- }
/**
- * Method that JAX-RS container calls to deserialize given value.
+ * Helper method used to check whether given media type
+ * is JSON type or sub type.
+ * Current implementation essentially checks to see whether
+ * {@link MediaType#getSubtype} returns "json" or something
+ * ending with "+json".
+ *
+ * @since 2.2
*/
- public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,String> httpHeaders, InputStream entityStream)
- throws IOException
- {
+ protected abstract boolean hasMatchingMediaType(MediaType mediaType);
- AnnotationBundleKey key = new AnnotationBundleKey(annotations);
- EndpointConfig endpoint;
- synchronized (_readers) {
- endpoint = _readers.get(key);
- }
- // not yet resolved (or not cached any more)? Resolve!
- if (endpoint == null) {
- ObjectMapper mapper = locateMapper(type, mediaType);
- endpoint = EndpointConfig.forReading(mapper, annotations);
- // and cache for future reuse
- synchronized (_readers) {
- _readers.put(key.immutableKey(), endpoint);
- }
- }
- ObjectReader reader = endpoint.getReader();
-
- JsonParser jp = reader.getFactory().createParser(entityStream);
- if (jp.nextToken() == null) {
- return null;
- }
- return reader.withType(genericType).readValue(jp);
- }
+ protected abstract MAPPER _locateMapperViaProvider(Class<?> type, MediaType mediaType);
+ protected abstract EP_CONFIG _configForReading(MAPPER mapper, Annotation[] annotations);
+
+ protected abstract EP_CONFIG _configForWriting(MAPPER mapper, Annotation[] annotations);
+
/*
/**********************************************************
- /* MessageBodyWriter impl
+ /* Partial MessageBodyWriter impl
/**********************************************************
*/
@@ -471,7 +316,7 @@ public class JacksonJsonProvider
*/
return -1;
}
-
+
/**
* Method that JAX-RS container calls to try to check whether
* given value (of specified type) can be serialized by
@@ -485,7 +330,7 @@ public class JacksonJsonProvider
*/
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
{
- if (!isJsonType(mediaType)) {
+ if (!hasMatchingMediaType(mediaType)) {
return false;
}
@@ -523,15 +368,14 @@ public class JacksonJsonProvider
throws IOException
{
AnnotationBundleKey key = new AnnotationBundleKey(annotations);
- EndpointConfig endpoint;
+ EP_CONFIG endpoint;
synchronized (_writers) {
endpoint = _writers.get(key);
}
// not yet resolved (or not cached any more)? Resolve!
if (endpoint == null) {
- ObjectMapper mapper = locateMapper(type, mediaType);
- endpoint = EndpointConfig.forWriting(mapper, annotations,
- this._jsonpFunctionName);
+ MAPPER mapper = locateMapper(type, mediaType);
+ endpoint = _configForWriting(mapper, annotations);
// and cache for future reuse
synchronized (_writers) {
_writers.put(key.immutableKey(), endpoint);
@@ -579,9 +423,7 @@ public class JacksonJsonProvider
if (rootType != null) {
writer = writer.withType(rootType);
}
- // and finally, JSONP wrapping, if any:
- value = endpoint.applyJSONP(value);
-
+ value = endpoint.modifyBeforeWrite(value);
writer.writeValue(jg, value);
}
@@ -593,39 +435,92 @@ public class JacksonJsonProvider
{
return JsonEncoding.UTF8;
}
-
+
/*
/**********************************************************
- /* Public helper methods
+ /* MessageBodyReader impl
/**********************************************************
*/
-
+
/**
- * Helper method used to check whether given media type
- * is JSON type or sub type.
- * Current implementation essentially checks to see whether
- * {@link MediaType#getSubtype} returns "json" or something
- * ending with "+json".
+ * Method that JAX-RS container calls to try to check whether
+ * values of given type (and media type) can be deserialized by
+ * this provider.
+ * Implementation will first check that expected media type is
+ * a JSON type (via call to {@link #isJsonType}; then verify
+ * that type is not one of "untouchable" types (types we will never
+ * automatically handle), and finally that there is a deserializer
+ * for type (iff {@link #checkCanDeserialize} has been called with
+ * true argument -- otherwise assumption is there will be a handler)
*/
- protected boolean isJsonType(MediaType mediaType)
+ public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
{
- /* As suggested by Stephen D, there are 2 ways to check: either
- * being as inclusive as possible (if subtype is "json"), or
- * exclusive (major type "application", minor type "json").
- * Let's start with inclusive one, hard to know which major
- * types we should cover aside from "application".
- */
- if (mediaType != null) {
- // Ok: there are also "xxx+json" subtypes, which count as well
- String subtype = mediaType.getSubtype();
- return "json".equalsIgnoreCase(subtype) || subtype.endsWith("+json");
+ if (!hasMatchingMediaType(mediaType)) {
+ return false;
}
- /* Not sure if this can happen; but it seems reasonable
- * that we can at least produce json without media type?
+
+ /* Ok: looks like we must weed out some core types here; ones that
+ * make no sense to try to bind from JSON:
*/
+ if (_untouchables.contains(new ClassKey(type))) {
+ return false;
+ }
+ // and there are some other abstract/interface types to exclude too:
+ for (Class<?> cls : _unreadableClasses) {
+ if (cls.isAssignableFrom(type)) {
+ return false;
+ }
+ }
+ // as well as possible custom exclusions
+ if (_containedIn(type, _cfgCustomUntouchables)) {
+ return false;
+ }
+
+ // Finally: if we really want to verify that we can serialize, we'll check:
+ if (_cfgCheckCanSerialize) {
+ ObjectMapper mapper = locateMapper(type, mediaType);
+ if (!mapper.canDeserialize(mapper.constructType(type))) {
+ return false;
+ }
+ }
return true;
}
-
+
+ /**
+ * Method that JAX-RS container calls to deserialize given value.
+ */
+ public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,String> httpHeaders, InputStream entityStream)
+ throws IOException
+ {
+ AnnotationBundleKey key = new AnnotationBundleKey(annotations);
+ EP_CONFIG endpoint;
+ synchronized (_readers) {
+ endpoint = _readers.get(key);
+ }
+ // not yet resolved (or not cached any more)? Resolve!
+ if (endpoint == null) {
+ MAPPER mapper = locateMapper(type, mediaType);
+ endpoint = _configForReading(mapper, annotations);
+ // and cache for future reuse
+ synchronized (_readers) {
+ _readers.put(key.immutableKey(), endpoint);
+ }
+ }
+ ObjectReader reader = endpoint.getReader();
+
+ JsonParser jp = reader.getFactory().createParser(entityStream);
+ if (jp.nextToken() == null) {
+ return null;
+ }
+ return reader.withType(genericType).readValue(jp);
+ }
+
+ /*
+ /**********************************************************
+ /* Private/sub-class helper methods
+ /**********************************************************
+ */
+
/**
* Method called to locate {@link ObjectMapper} to use for serialization
* and deserialization. If an instance has been explicitly defined by
@@ -646,26 +541,13 @@ public class JacksonJsonProvider
* not used by this method,
* but will be passed to {@link ContextResolver} as is.
*/
- public ObjectMapper locateMapper(Class<?> type, MediaType mediaType)
+ public MAPPER locateMapper(Class<?> type, MediaType mediaType)
{
// First: were we configured with a specific instance?
- ObjectMapper m = _mapperConfig.getConfiguredMapper();
+ MAPPER m = _mapperConfig.getConfiguredMapper();
if (m == null) {
// If not, maybe we can get one configured via context?
- if (_providers != null) {
- ContextResolver<ObjectMapper> resolver = _providers.getContextResolver(ObjectMapper.class, mediaType);
- /* Above should work as is, but due to this bug
- * [https://jersey.dev.java.net/issues/show_bug.cgi?id=288]
- * in Jersey, it doesn't. But this works until resolution of
- * the issue:
- */
- if (resolver == null) {
- resolver = _providers.getContextResolver(ObjectMapper.class, null);
- }
- if (resolver != null) {
- m = resolver.getContext(type);
- }
- }
+ m = _locateMapperViaProvider(type, mediaType);
if (m == null) {
// If not, let's get the fallback default instance
m = _mapperConfig.getDefaultMapper();
@@ -673,13 +555,7 @@ public class JacksonJsonProvider
}
return m;
}
-
- /*
- /**********************************************************
- /* Private/sub-class helper methods
- /**********************************************************
- */
-
+
protected static boolean _containedIn(Class<?> mainType, HashSet<ClassKey> set)
{
if (set != null) {
@@ -695,18 +571,18 @@ public class JacksonJsonProvider
return false;
}
- private static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore)
+ protected static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore)
{
return findSuperTypes(cls, endBefore, new ArrayList<Class<?>>(8));
}
- private static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore, List<Class<?>> result)
+ protected static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore, List<Class<?>> result)
{
_addSuperTypes(cls, endBefore, result, false);
return result;
}
- private static void _addSuperTypes(Class<?> cls, Class<?> endBefore, Collection<Class<?>> result, boolean addClassItself)
+ protected static void _addSuperTypes(Class<?> cls, Class<?> endBefore, Collection<Class<?>> result, boolean addClassItself)
{
if (cls == endBefore || cls == null || cls == Object.class) {
return;
@@ -723,4 +599,8 @@ public class JacksonJsonProvider
_addSuperTypes(cls.getSuperclass(), endBefore, result, true);
}
+ @SuppressWarnings("unchecked")
+ private final THIS _this() {
+ return (THIS) this;
+ }
}
diff --git a/base/src/main/java/com/fasterxml/jackson/jaxrs/base/cfg/EndpointConfigBase.java b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/cfg/EndpointConfigBase.java
new file mode 100644
index 0000000..da7ec2a
--- /dev/null
+++ b/base/src/main/java/com/fasterxml/jackson/jaxrs/base/cfg/EndpointConfigBase.java
@@ -0,0 +1,165 @@
+package com.fasterxml.jackson.jaxrs.base.cfg;
+
+import java.lang.annotation.Annotation;
+
+import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
+import com.fasterxml.jackson.annotation.JsonRootName;
+import com.fasterxml.jackson.annotation.JsonView;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Container class for figuring out annotation-based configuration
+ * for JAX-RS end points.
+ */
+public abstract class EndpointConfigBase<THIS extends EndpointConfigBase<THIS>>
+{
+ // // General configuration
+
+ protected Class<?> _activeView;
+
+ protected String _rootName;
+
+ // // Deserialization-only config
+
+ protected DeserializationFeature[] _deserEnable;
+ protected DeserializationFeature[] _deserDisable;
+
+ protected ObjectReader _reader;
+
+ // // Serialization-only config
+
+ protected SerializationFeature[] _serEnable;
+ protected SerializationFeature[] _serDisable;
+
+ protected ObjectWriter _writer;
+
+ /*
+ /**********************************************************
+ /* Construction
+ /**********************************************************
+ */
+
+ protected EndpointConfigBase() { }
+
+ @SuppressWarnings("unchecked")
+ protected THIS add(Annotation[] annotations, boolean forWriting)
+ {
+ if (annotations != null) {
+ for (Annotation annotation : annotations) {
+ addAnnotation(annotation.annotationType(), annotation, forWriting);
+ }
+ }
+ return (THIS) this;
+ }
+
+ protected void addAnnotation(Class<? extends Annotation> type,
+ Annotation annotation, boolean forWriting)
+ {
+ if (type == JsonView.class) {
+ // Can only use one view; but if multiple defined, use first (no exception)
+ Class<?>[] views = ((JsonView) annotation).value();
+ _activeView = (views.length > 0) ? views[0] : null;
+ } else if (type == JsonRootName.class) {
+ _rootName = ((JsonRootName) annotation).value();
+ } else if (type == JacksonAnnotationsInside.class) {
+ // skip; processed below (in parent), so encountering here is of no use
+ } else {
+ // For all unrecognized types, check meta-annotation(s) to see if they are bundles
+ JacksonAnnotationsInside inside = type.getAnnotation(JacksonAnnotationsInside.class);
+ if (inside != null) {
+ add(type.getAnnotations(), forWriting);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected THIS initReader(ObjectMapper mapper)
+ {
+ // first common config
+ if (_activeView != null) {
+ _reader = mapper.readerWithView(_activeView);
+ } else {
+ _reader = mapper.reader();
+ }
+
+ if (_rootName != null) {
+ _reader = _reader.withRootName(_rootName);
+ }
+ // Then deser features
+ if (_deserEnable != null) {
+ _reader = _reader.withFeatures(_deserEnable);
+ }
+ if (_deserDisable != null) {
+ _reader = _reader.withoutFeatures(_deserDisable);
+ }
+ /* Important: we are NOT to close the underlying stream after
+ * mapping, so we need to instruct parser:
+ */
+ _reader.getFactory().disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
+
+ return (THIS) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected THIS initWriter(ObjectMapper mapper)
+ {
+ // first common config
+ if (_activeView != null) {
+ _writer = mapper.writerWithView(_activeView);
+ } else {
+ _writer = mapper.writer();
+ }
+ if (_rootName != null) {
+ _writer = _writer.withRootName(_rootName);
+ }
+ // Then features
+ if (_serEnable != null) {
+ _writer = _writer.withFeatures(_serEnable);
+ }
+ if (_serDisable != null) {
+ _writer = _writer.withoutFeatures(_serDisable);
+ }
+ // then others
+
+ // Finally: couple of features we always set
+
+ /* Important: we are NOT to close the underlying stream after
+ * mapping, so we need to instruct parser:
+ */
+ _writer.getFactory().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
+
+ return (THIS) this;
+ }
+
+ /*
+ /**********************************************************
+ /* Accessors
+ /**********************************************************
+ */
+
+ public final ObjectReader getReader() {
+ if (_reader == null) { // sanity check, should never happen
+ throw new IllegalStateException();
+ }
+ return _reader;
+ }
+
+ public final ObjectWriter getWriter() {
+ if (_writer == null) { // sanity check, should never happen
+ throw new IllegalStateException();
+ }
+ return _writer;
+ }
+
+ /*
+ /**********************************************************
+ /* Value modifications
+ /**********************************************************
+ */
+
+ public abstract Object modifyBeforeWrite(Object value);
+}
diff --git a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java b/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java
index 65ec884..9a211db 100644
--- a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java
+++ b/json/src/main/java/com/fasterxml/jackson/jaxrs/json/JacksonJsonProvider.java
@@ -1,24 +1,15 @@
package com.fasterxml.jackson.jaxrs.json;
-import java.io.*;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
-import java.util.*;
import javax.ws.rs.*;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.StreamingOutput;
+import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
-import com.fasterxml.jackson.databind.util.LRUMap;
-import com.fasterxml.jackson.jaxrs.base.util.AnnotationBundleKey;
-import com.fasterxml.jackson.jaxrs.base.util.ClassKey;
+import com.fasterxml.jackson.jaxrs.base.ProviderBase;
import com.fasterxml.jackson.jaxrs.json.annotation.EndpointConfig;
import com.fasterxml.jackson.jaxrs.json.cfg.MapperConfigurator;
@@ -57,10 +48,9 @@ import com.fasterxml.jackson.jaxrs.json.cfg.MapperConfigurator;
@Consumes(MediaType.WILDCARD) // NOTE: required to support "non-standard" JSON variants
@Produces(MediaType.WILDCARD)
public class JacksonJsonProvider
- implements
- MessageBodyReader<Object>,
- MessageBodyWriter<Object>,
- Versioned
+ extends ProviderBase<JacksonJsonProvider,
+ ObjectMapper, Annotations,
+ EndpointConfig, MapperConfigurator>
{
/**
* Default annotation sets to use, if not explicitly defined during
@@ -70,89 +60,12 @@ public class JacksonJsonProvider
public final static Annotations[] BASIC_ANNOTATIONS = {
Annotations.JACKSON
};
-
- /**
- * 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
- * blacklisting things we are not to handle.
- *<p>
- * (why ClassKey? since plain old Class has no hashCode() defined,
- * lookups are painfully slow)
- */
- public final static HashSet<ClassKey> _untouchables = new HashSet<ClassKey>();
- static {
- // First, I/O things (direct matches)
- _untouchables.add(new ClassKey(java.io.InputStream.class));
- _untouchables.add(new ClassKey(java.io.Reader.class));
- _untouchables.add(new ClassKey(java.io.OutputStream.class));
- _untouchables.add(new ClassKey(java.io.Writer.class));
-
- // then some primitive types
- _untouchables.add(new ClassKey(char[].class));
-
- /* 28-Jan-2012, tatu: 1.x excluded some additional types;
- * but let's relax these a bit:
- */
- /* 27-Apr-2012, tatu: Ugh. As per
- * [https://github.com/FasterXML/jackson-jaxrs-json-provider/issues/12]
- * better revert this back, to make them untouchable again.
- */
- _untouchables.add(new ClassKey(String.class));
- _untouchables.add(new ClassKey(byte[].class));
- }
-
- /**
- * These are classes that we never use for reading
- * (never try to deserialize instances of these types).
- */
- public final static Class<?>[] _unreadableClasses = new Class<?>[] {
- InputStream.class, Reader.class
- };
-
- /**
- * These are classes that we never use for writing
- * (never try to serialize instances of these types).
- */
- public final static Class<?>[] _unwritableClasses = new Class<?>[] {
- OutputStream.class, Writer.class,
- StreamingOutput.class, Response.class
- };
-
- /*
- /**********************************************************
- /* Bit of caching
- /**********************************************************
- */
-
- /**
- * Cache for resolved endpoint configurations when reading JSON data
- */
- protected final LRUMap<AnnotationBundleKey, EndpointConfig> _readers
- = new LRUMap<AnnotationBundleKey, EndpointConfig>(16, 120);
-
- /**
- * Cache for resolved endpoint configurations when writing JSON data
- */
- protected final LRUMap<AnnotationBundleKey, EndpointConfig> _writers
- = new LRUMap<AnnotationBundleKey, EndpointConfig>(16, 120);
/*
/**********************************************************
/* General configuration
/**********************************************************
*/
-
- /**
- * Helper object used for encapsulating configuration aspects
- * of {@link ObjectMapper}
- */
- protected final MapperConfigurator _mapperConfig;
-
- /**
- * Set of types (classes) that provider should ignore for data binding
- */
- protected HashSet<ClassKey> _cfgCustomUntouchables;
/**
* JSONP function name to use for automatic JSONP wrapping, if any;
@@ -178,28 +91,6 @@ public class JacksonJsonProvider
/*
/**********************************************************
- /* Configuration
- /**********************************************************
- */
-
- /**
- * Whether we want to actually check that Jackson has
- * a serializer for given type. Since this should generally
- * be the case (due to auto-discovery) and since the call
- * to check availability can be bit expensive, defaults to false.
- */
- protected boolean _cfgCheckCanSerialize = false;
-
- /**
- * Whether we want to actually check that Jackson has
- * a deserializer for given type. Since this should generally
- * be the case (due to auto-discovery) and since the call
- * to check availability can be bit expensive, defaults to false.
- */
- protected boolean _cfgCheckCanDeserialize = false;
-
- /*
- /**********************************************************
/* Construction
/**********************************************************
*/
@@ -208,8 +99,7 @@ public class JacksonJsonProvider
* Default constructor, usually used when provider is automatically
* configured to be used with JAX-RS implementation.
*/
- public JacksonJsonProvider()
- {
+ public JacksonJsonProvider() {
this(null, BASIC_ANNOTATIONS);
}
@@ -217,13 +107,11 @@ public class JacksonJsonProvider
* @param annotationsToUse Annotation set(s) to use for configuring
* data binding
*/
- public JacksonJsonProvider(Annotations... annotationsToUse)
- {
+ public JacksonJsonProvider(Annotations... annotationsToUse) {
this(null, annotationsToUse);
}
- public JacksonJsonProvider(ObjectMapper mapper)
- {
+ public JacksonJsonProvider(ObjectMapper mapper) {
this(mapper, BASIC_ANNOTATIONS);
}
@@ -235,9 +123,8 @@ public class JacksonJsonProvider
* @param annotationsToUse Sets of annotations (Jackson, JAXB) that provider should
* support
*/
- public JacksonJsonProvider(ObjectMapper mapper, Annotations[] annotationsToUse)
- {
- _mapperConfig = new MapperConfigurator(mapper, annotationsToUse);
+ public JacksonJsonProvider(ObjectMapper mapper, Annotations[] annotationsToUse) {
+ super(new MapperConfigurator(mapper, annotationsToUse));
}
/**
@@ -250,364 +137,30 @@ public class JacksonJsonProvider
/*
/**********************************************************
- /* Configuring
+ /* JSON-specific configuration
/**********************************************************
*/
- /**
- * Method for defining whether actual detection for existence of
- * a deserializer for type should be done when {@link #isReadable}
- * is called.
- */
- public void checkCanDeserialize(boolean state) { _cfgCheckCanDeserialize = state; }
-
- /**
- * Method for defining whether actual detection for existence of
- * a serializer for type should be done when {@link #isWriteable}
- * is called.
- */
- public void checkCanSerialize(boolean state) { _cfgCheckCanSerialize = state; }
-
- /**
- * Method for configuring which annotation sets to use (including none).
- * Annotation sets are defined in order decreasing precedence; that is,
- * first one has the priority over following ones.
- *
- * @param annotationsToUse Ordered list of annotation sets to use; if null,
- * default
- */
- public void setAnnotationsToUse(Annotations[] annotationsToUse) {
- _mapperConfig.setAnnotationsToUse(annotationsToUse);
- }
-
- /**
- * Method that can be used to directly define {@link ObjectMapper} to use
- * for serialization and deserialization; if null, will use the standard
- * provider discovery from context instead. Default setting is null.
- */
- public void setMapper(ObjectMapper m) {
- _mapperConfig.setMapper(m);
- }
-
- public JacksonJsonProvider configure(DeserializationFeature f, boolean state) {
- _mapperConfig.configure(f, state);
- return this;
- }
-
- public JacksonJsonProvider configure(SerializationFeature f, boolean state) {
- _mapperConfig.configure(f, state);
- return this;
- }
-
- public JacksonJsonProvider configure(JsonParser.Feature f, boolean state) {
- _mapperConfig.configure(f, state);
- return this;
- }
-
- public JacksonJsonProvider configure(JsonGenerator.Feature f, boolean state) {
- _mapperConfig.configure(f, state);
- return this;
- }
-
- public JacksonJsonProvider enable(DeserializationFeature f, boolean state) {
- _mapperConfig.configure(f, true);
- return this;
- }
-
- public JacksonJsonProvider enable(SerializationFeature f, boolean state) {
- _mapperConfig.configure(f, true);
- return this;
- }
-
- public JacksonJsonProvider enable(JsonParser.Feature f, boolean state) {
- _mapperConfig.configure(f, true);
- return this;
- }
-
- public JacksonJsonProvider enable(JsonGenerator.Feature f, boolean state) {
- _mapperConfig.configure(f, true);
- return this;
- }
-
- public JacksonJsonProvider disable(DeserializationFeature f, boolean state) {
- _mapperConfig.configure(f, false);
- return this;
- }
-
- public JacksonJsonProvider disable(SerializationFeature f, boolean state) {
- _mapperConfig.configure(f, false);
- return this;
- }
-
- public JacksonJsonProvider disable(JsonParser.Feature f, boolean state) {
- _mapperConfig.configure(f, false);
- return this;
- }
-
- public JacksonJsonProvider disable(JsonGenerator.Feature f, boolean state) {
- _mapperConfig.configure(f, false);
- return this;
- }
-
- /**
- * Method for marking specified type as "untouchable", meaning that provider
- * will not try to read or write values of this type (or its subtypes).
- *
- * @param type Type to consider untouchable; can be any kind of class,
- * including abstract class or interface. No instance of this type
- * (including subtypes, i.e. types assignable to this type) will
- * be read or written by provider
- */
- public void addUntouchable(Class<?> type)
- {
- if (_cfgCustomUntouchables == null) {
- _cfgCustomUntouchables = new HashSet<ClassKey>();
- }
- _cfgCustomUntouchables.add(new ClassKey(type));
- }
-
public void setJSONPFunctionName(String fname) {
_jsonpFunctionName = fname;
}
-
- /*
- /**********************************************************
- /* MessageBodyReader impl
- /**********************************************************
- */
-
- /**
- * Method that JAX-RS container calls to try to check whether
- * values of given type (and media type) can be deserialized by
- * this provider.
- * Implementation will first check that expected media type is
- * a JSON type (via call to {@link #isJsonType}; then verify
- * that type is not one of "untouchable" types (types we will never
- * automatically handle), and finally that there is a deserializer
- * for type (iff {@link #checkCanDeserialize} has been called with
- * true argument -- otherwise assumption is there will be a handler)
- */
- public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
- {
- if (!isJsonType(mediaType)) {
- return false;
- }
-
- /* Ok: looks like we must weed out some core types here; ones that
- * make no sense to try to bind from JSON:
- */
- if (_untouchables.contains(new ClassKey(type))) {
- return false;
- }
- // and there are some other abstract/interface types to exclude too:
- for (Class<?> cls : _unreadableClasses) {
- if (cls.isAssignableFrom(type)) {
- return false;
- }
- }
- // as well as possible custom exclusions
- if (_containedIn(type, _cfgCustomUntouchables)) {
- return false;
- }
-
- // Finally: if we really want to verify that we can serialize, we'll check:
- if (_cfgCheckCanSerialize) {
- ObjectMapper mapper = locateMapper(type, mediaType);
- if (!mapper.canDeserialize(mapper.constructType(type))) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Method that JAX-RS container calls to deserialize given value.
- */
- public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String,String> httpHeaders, InputStream entityStream)
- throws IOException
- {
-
- AnnotationBundleKey key = new AnnotationBundleKey(annotations);
- EndpointConfig endpoint;
- synchronized (_readers) {
- endpoint = _readers.get(key);
- }
- // not yet resolved (or not cached any more)? Resolve!
- if (endpoint == null) {
- ObjectMapper mapper = locateMapper(type, mediaType);
- endpoint = EndpointConfig.forReading(mapper, annotations);
- // and cache for future reuse
- synchronized (_readers) {
- _readers.put(key.immutableKey(), endpoint);
- }
- }
- ObjectReader reader = endpoint.getReader();
-
- JsonParser jp = reader.getFactory().createParser(entityStream);
- if (jp.nextToken() == null) {
- return null;
- }
- return reader.withType(genericType).readValue(jp);
- }
/*
/**********************************************************
- /* MessageBodyWriter impl
+ /* Abstract method impl
/**********************************************************
*/
/**
- * Method that JAX-RS container calls to try to figure out
- * serialized length of given value. Since computation of
- * this length is about as expensive as serialization itself,
- * implementation will return -1 to denote "not known", so
- * that container will determine length from actual serialized
- * output (if needed).
- */
- public long getSize(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
- {
- /* In general figuring output size requires actual writing; usually not
- * worth it to write everything twice.
- */
- return -1;
- }
-
- /**
- * Method that JAX-RS container calls to try to check whether
- * given value (of specified type) can be serialized by
- * this provider.
- * Implementation will first check that expected media type is
- * a JSON type (via call to {@link #isJsonType}; then verify
- * that type is not one of "untouchable" types (types we will never
- * automatically handle), and finally that there is a serializer
- * for type (iff {@link #checkCanSerialize} has been called with
- * true argument -- otherwise assumption is there will be a handler)
+ * @deprecated Since 2.2 use {@link #hasMatchingMediaType(MediaType)} instead
*/
- public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
- {
- if (!isJsonType(mediaType)) {
- return false;
- }
-
- /* Ok: looks like we must weed out some core types here; ones that
- * make no sense to try to bind from JSON:
- */
- if (_untouchables.contains(new ClassKey(type))) {
- return false;
- }
- // but some are interface/abstract classes, so
- for (Class<?> cls : _unwritableClasses) {
- if (cls.isAssignableFrom(type)) {
- return false;
- }
- }
- // and finally, may have additional custom types to exclude
- if (_containedIn(type, _cfgCustomUntouchables)) {
- return false;
- }
-
- // Also: if we really want to verify that we can deserialize, we'll check:
- if (_cfgCheckCanSerialize) {
- if (!locateMapper(type, mediaType).canSerialize(type)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Method that JAX-RS container calls to serialize given value.
- */
- public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
- MultivaluedMap<String,Object> httpHeaders, OutputStream entityStream)
- throws IOException
- {
- AnnotationBundleKey key = new AnnotationBundleKey(annotations);
- EndpointConfig endpoint;
- synchronized (_writers) {
- endpoint = _writers.get(key);
- }
- // not yet resolved (or not cached any more)? Resolve!
- if (endpoint == null) {
- ObjectMapper mapper = locateMapper(type, mediaType);
- endpoint = EndpointConfig.forWriting(mapper, annotations,
- this._jsonpFunctionName);
- // and cache for future reuse
- synchronized (_writers) {
- _writers.put(key.immutableKey(), endpoint);
- }
- }
-
- ObjectWriter writer = endpoint.getWriter();
-
- /* 27-Feb-2009, tatu: Where can we find desired encoding? Within
- * HTTP headers?
- */
- JsonEncoding enc = findEncoding(mediaType, httpHeaders);
- JsonGenerator jg = writer.getFactory().createGenerator(entityStream, enc);
-
- // Want indentation?
- if (writer.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
- jg.useDefaultPrettyPrinter();
- }
- // 04-Mar-2010, tatu: How about type we were given? (if any)
- JavaType rootType = null;
-
- if (genericType != null && value != null) {
- /* 10-Jan-2011, tatu: as per [JACKSON-456], it's not safe to just force root
- * type since it prevents polymorphic type serialization. Since we really
- * just need this for generics, let's only use generic type if it's truly
- * generic.
- */
- if (genericType.getClass() != Class.class) { // generic types are other impls of 'java.lang.reflect.Type'
- /* This is still not exactly right; should root type be further
- * specialized with 'value.getClass()'? Let's see how well this works before
- * trying to come up with more complete solution.
- */
- rootType = writer.getTypeFactory().constructType(genericType);
- /* 26-Feb-2011, tatu: To help with [JACKSON-518], we better recognize cases where
- * type degenerates back into "Object.class" (as is the case with plain TypeVariable,
- * for example), and not use that.
- */
- if (rootType.getRawClass() == Object.class) {
- rootType = null;
- }
- }
- }
- // Most of the configuration now handled through EndpointConfig, ObjectWriter
- // but we may need to force root type:
- if (rootType != null) {
- writer = writer.withType(rootType);
- }
- // and finally, JSONP wrapping, if any:
- value = endpoint.applyJSONP(value);
-
- writer.writeValue(jg, value);
- }
-
- /**
- * Helper method to use for determining desired output encoding.
- * For now, will always just use UTF-8...
- */
- protected JsonEncoding findEncoding(MediaType mediaType, MultivaluedMap<String,Object> httpHeaders)
- {
- return JsonEncoding.UTF8;
+ @Deprecated
+ protected boolean isJsonType(MediaType mediaType) {
+ return hasMatchingMediaType(mediaType);
}
- /*
- /**********************************************************
- /* Public helper methods
- /**********************************************************
- */
-
- /**
- * Helper method used to check whether given media type
- * is JSON type or sub type.
- * Current implementation essentially checks to see whether
- * {@link MediaType#getSubtype} returns "json" or something
- * ending with "+json".
- */
- protected boolean isJsonType(MediaType mediaType)
+ @Override
+ protected boolean hasMatchingMediaType(MediaType mediaType)
{
/* As suggested by Stephen D, there are 2 ways to check: either
* being as inclusive as possible (if subtype is "json"), or
@@ -626,101 +179,33 @@ public class JacksonJsonProvider
return true;
}
- /**
- * Method called to locate {@link ObjectMapper} to use for serialization
- * and deserialization. If an instance has been explicitly defined by
- * {@link #setMapper} (or non-null instance passed in constructor), that
- * will be used.
- * If not, will try to locate it using standard JAX-RS
- * {@link ContextResolver} mechanism, if it has been properly configured
- * to access it (by JAX-RS runtime).
- * Finally, if no mapper is found, will return a default unconfigured
- * {@link ObjectMapper} instance (one constructed with default constructor
- * and not modified in any way)
- *
- * @param type Class of object being serialized or deserialized;
- * not checked at this point, since it is assumed that unprocessable
- * classes have been already weeded out,
- * but will be passed to {@link ContextResolver} as is.
- * @param mediaType Declared media type for the instance to process:
- * not used by this method,
- * but will be passed to {@link ContextResolver} as is.
- */
- public ObjectMapper locateMapper(Class<?> type, MediaType mediaType)
+ @Override
+ protected ObjectMapper _locateMapperViaProvider(Class<?> type, MediaType mediaType)
{
- // First: were we configured with a specific instance?
- ObjectMapper m = _mapperConfig.getConfiguredMapper();
- if (m == null) {
- // If not, maybe we can get one configured via context?
- if (_providers != null) {
- ContextResolver<ObjectMapper> resolver = _providers.getContextResolver(ObjectMapper.class, mediaType);
- /* Above should work as is, but due to this bug
- * [https://jersey.dev.java.net/issues/show_bug.cgi?id=288]
- * in Jersey, it doesn't. But this works until resolution of
- * the issue:
- */
- if (resolver == null) {
- resolver = _providers.getContextResolver(ObjectMapper.class, null);
- }
- if (resolver != null) {
- m = resolver.getContext(type);
- }
- }
- if (m == null) {
- // If not, let's get the fallback default instance
- m = _mapperConfig.getDefaultMapper();
+ if (_providers != null) {
+ ContextResolver<ObjectMapper> resolver = _providers.getContextResolver(ObjectMapper.class, mediaType);
+ /* Above should work as is, but due to this bug
+ * [https://jersey.dev.java.net/issues/show_bug.cgi?id=288]
+ * in Jersey, it doesn't. But this works until resolution of
+ * the issue:
+ */
+ if (resolver == null) {
+ resolver = _providers.getContextResolver(ObjectMapper.class, null);
}
- }
- return m;
- }
-
- /*
- /**********************************************************
- /* Private/sub-class helper methods
- /**********************************************************
- */
-
- protected static boolean _containedIn(Class<?> mainType, HashSet<ClassKey> set)
- {
- if (set != null) {
- ClassKey key = new ClassKey(mainType);
- // First: type itself?
- if (set.contains(key)) return true;
- // Then supertypes (note: will not contain Object.class)
- for (Class<?> cls : findSuperTypes(mainType, null)) {
- key.reset(cls);
- if (set.contains(key)) return true;
+ if (resolver != null) {
+ return resolver.getContext(type);
}
}
- return false;
+ return null;
}
- private static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore)
- {
- return findSuperTypes(cls, endBefore, new ArrayList<Class<?>>(8));
+ @Override
+ protected EndpointConfig _configForReading(ObjectMapper mapper, Annotation[] annotations) {
+ return EndpointConfig.forReading(mapper, annotations);
}
- private static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore, List<Class<?>> result)
- {
- _addSuperTypes(cls, endBefore, result, false);
- return result;
- }
-
- private static void _addSuperTypes(Class<?> cls, Class<?> endBefore, Collection<Class<?>> result, boolean addClassItself)
- {
- if (cls == endBefore || cls == null || cls == Object.class) {
- return;
- }
- if (addClassItself) {
- if (result.contains(cls)) { // already added, no need to check supers
- return;
- }
- result.add(cls);
- }
- for (Class<?> intCls : cls.getInterfaces()) {
- _addSuperTypes(intCls, endBefore, result, true);
- }
- _addSuperTypes(cls.getSuperclass(), endBefore, result, true);
+ @Override
+ protected EndpointConfig _configForWriting(ObjectMapper mapper, Annotation[] annotations) {
+ return EndpointConfig.forWriting(mapper, annotations, _jsonpFunctionName);
}
-
}
diff --git a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/annotation/EndpointConfig.java b/json/src/main/java/com/fasterxml/jackson/jaxrs/json/annotation/EndpointConfig.java
index ea7d099..ee10375 100644
--- a/json/src/main/java/com/fasterxml/jackson/jaxrs/json/annotation/EndpointConfig.java
+++ b/json/src/main/java/com/fasterxml/jackson/jaxrs/json/annotation/EndpointConfig.java
@@ -2,42 +2,23 @@ package com.fasterxml.jackson.jaxrs.json.annotation;
import java.lang.annotation.Annotation;
-import com.fasterxml.jackson.annotation.*;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.fasterxml.jackson.databind.util.JSONWrappedObject;
+import com.fasterxml.jackson.jaxrs.base.cfg.EndpointConfigBase;
+
/**
* Container class for figuring out annotation-based configuration
* for JAX-RS end points.
*/
public class EndpointConfig
+ extends EndpointConfigBase<EndpointConfig>
{
- // // General configuration
-
- protected Class<?> _activeView;
-
- protected String _rootName;
-
- // // Deserialization-only config
-
- protected DeserializationFeature[] _deserEnable;
- protected DeserializationFeature[] _deserDisable;
-
- protected ObjectReader _reader;
-
// // Serialization-only config
protected JSONP.Def _jsonp;
- protected SerializationFeature[] _serEnable;
- protected SerializationFeature[] _serDisable;
-
- protected ObjectWriter _writer;
-
/*
/**********************************************************
/* Construction
@@ -45,7 +26,7 @@ public class EndpointConfig
*/
protected EndpointConfig() { }
-
+
public static EndpointConfig forReading(ObjectMapper mapper, Annotation[] annotations)
{
return new EndpointConfig()
@@ -65,122 +46,46 @@ public class EndpointConfig
.initWriter(mapper)
;
}
+
+ /*
+ /**********************************************************
+ /* Abstract method impls, overrides
+ /**********************************************************
+ */
- protected EndpointConfig add(Annotation[] annotations, boolean forWriting)
+ @Override
+ protected void addAnnotation(Class<? extends Annotation> type,
+ Annotation annotation, boolean forWriting)
{
- if (annotations != null) {
- for (Annotation annotation : annotations) {
- Class<?> type = annotation.annotationType();
- if (type == JSONP.class) {
- if (forWriting) {
- _jsonp = new JSONP.Def((JSONP) annotation);
- }
- } else if (type == JsonView.class) {
- // Can only use one view; but if multiple defined, use first (no exception)
- Class<?>[] views = ((JsonView) annotation).value();
- _activeView = (views.length > 0) ? views[0] : null;
- } else if (type == JsonRootName.class) {
- _rootName = ((JsonRootName) annotation).value();
- } else if (type == JacksonFeatures.class) {
- JacksonFeatures feats = (JacksonFeatures) annotation;
- if (forWriting) {
- _serEnable = nullIfEmpty(feats.serializationEnable());
- _serDisable = nullIfEmpty(feats.serializationDisable());
- } else {
- _deserEnable = nullIfEmpty(feats.deserializationEnable());
- _deserDisable = nullIfEmpty(feats.deserializationDisable());
- }
- } else if (type == JacksonAnnotationsInside.class) {
- // skip; processed below (in parent), so encountering here is of no use
+ if (type == JSONP.class) {
+ if (forWriting) {
+ _jsonp = new JSONP.Def((JSONP) annotation);
+ }
+ } else if (type == JacksonFeatures.class) {
+ JacksonFeatures feats = (JacksonFeatures) annotation;
+ if (forWriting) {
+ _serEnable = nullIfEmpty(feats.serializationEnable());
+ _serDisable = nullIfEmpty(feats.serializationDisable());
} else {
- // For all unrecognized types, check meta-annotation(s) to see if they are bundles
- JacksonAnnotationsInside inside = type.getAnnotation(JacksonAnnotationsInside.class);
- if (inside != null) {
- add(type.getAnnotations(), forWriting);
- }
+ _deserEnable = nullIfEmpty(feats.deserializationEnable());
+ _deserDisable = nullIfEmpty(feats.deserializationDisable());
}
- }
- }
- return this;
- }
-
- protected EndpointConfig initReader(ObjectMapper mapper)
- {
- // first common config
- if (_activeView != null) {
- _reader = mapper.readerWithView(_activeView);
} else {
- _reader = mapper.reader();
+ super.addAnnotation(type, annotation, forWriting);
}
-
- if (_rootName != null) {
- _reader = _reader.withRootName(_rootName);
- }
- // Then deser features
- if (_deserEnable != null) {
- _reader = _reader.withFeatures(_deserEnable);
- }
- if (_deserDisable != null) {
- _reader = _reader.withoutFeatures(_deserDisable);
- }
- /* Important: we are NOT to close the underlying stream after
- * mapping, so we need to instruct parser:
- */
- _reader.getFactory().disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
-
- return this;
}
-
- protected EndpointConfig initWriter(ObjectMapper mapper)
- {
- // first common config
- if (_activeView != null) {
- _writer = mapper.writerWithView(_activeView);
- } else {
- _writer = mapper.writer();
- }
- if (_rootName != null) {
- _writer = _writer.withRootName(_rootName);
- }
- // Then features
- if (_serEnable != null) {
- _writer = _writer.withFeatures(_serEnable);
- }
- if (_serDisable != null) {
- _writer = _writer.withoutFeatures(_serDisable);
- }
- // then others
-
- // Finally: couple of features we always set
- /* Important: we are NOT to close the underlying stream after
- * mapping, so we need to instruct parser:
- */
- _writer.getFactory().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
-
- return this;
+ @Override
+ public Object modifyBeforeWrite(Object value) {
+ return applyJSONP(value);
}
-
+
/*
/**********************************************************
/* Accessors
/**********************************************************
*/
- public ObjectReader getReader() {
- if (_reader == null) { // sanity check, should never happen
- throw new IllegalStateException();
- }
- return _reader;
- }
-
- public ObjectWriter getWriter() {
- if (_writer == null) { // sanity check, should never happen
- throw new IllegalStateException();
- }
- return _writer;
- }
-
/**
* Method that will add JSONP wrapper object, if and as
* configured by collected annotations.
--
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