Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.fasterxml.jackson.jaxrs.base.ProviderBase Maven / Gradle / Ivy
Go to download
Pile of code that is shared by all Jackson-based JAX-RS
providers.
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.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
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.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.databind.util.LookupCache;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.jaxrs.base.nocontent.JaxRS1NoContentExceptionSupplier;
import com.fasterxml.jackson.jaxrs.base.nocontent.JaxRS2NoContentExceptionSupplier;
import com.fasterxml.jackson.jaxrs.cfg.*;
import com.fasterxml.jackson.jaxrs.util.ClassKey;
public abstract class ProviderBase<
THIS extends ProviderBase,
MAPPER extends ObjectMapper,
EP_CONFIG extends EndpointConfigBase,
MAPPER_CONFIG extends MapperConfiguratorBase
>
implements
MessageBodyReader,
MessageBodyWriter,
Versioned
{
/**
* This header is useful on Windows, trying to deal with potential XSS attacks.
*/
public final static String HEADER_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options";
protected final static String CLASS_NAME_NO_CONTENT_EXCEPTION = "javax.ws.rs.core.NoContentException";
private final NoContentExceptionSupplier noContentExceptionSupplier = _createNoContentExceptionSupplier();
/**
* 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.
*
* (why ClassKey? since plain old Class has no hashCode() defined,
* lookups are painfully slow)
*/
protected final static HashSet DEFAULT_UNTOUCHABLES = new HashSet();
static {
// First, I/O things (direct matches)
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
DEFAULT_UNTOUCHABLES.add(new ClassKey(char[].class));
// 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.
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>[] DEFAULT_UNREADABLES = 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>[] DEFAULT_UNWRITABLES = new Class>[] {
InputStream.class, // as per [Issue#19]
OutputStream.class, Writer.class,
StreamingOutput.class, Response.class
};
protected final static int JAXRS_FEATURE_DEFAULTS = JaxRSFeature.collectDefaults();
/*
/**********************************************************
/* General configuration
/**********************************************************
*/
/**
* 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: true
meaning that entry is untouchable,
* false
that is is not.
*/
protected HashMap _cfgCustomUntouchables;
/**
* 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;
/**
* Feature flags set.
*
* @since 2.3
*/
protected int _jaxRSFeatures;
/**
* View to use for reading if none defined for the end point.
*/
protected Class> _defaultReadView;
/**
* View to use for writing if none defined for the end point.
*/
protected Class> _defaultWriteView;
/*
/**********************************************************
/* Excluded types
/**********************************************************
*/
public final static HashSet _untouchables = DEFAULT_UNTOUCHABLES;
public final static Class>[] _unreadableClasses = DEFAULT_UNREADABLES;
public final static Class>[] _unwritableClasses = DEFAULT_UNWRITABLES;
/*
/**********************************************************
/* Bit of caching
/**********************************************************
*/
/**
* Cache for resolved endpoint configurations when reading JSON data
*/
protected final LookupCache _readers;
/**
* Cache for resolved endpoint configurations when writing JSON data
*/
protected final LookupCache _writers;
/*
/**********************************************************
/* Life-cycle
/**********************************************************
*/
protected ProviderBase(MAPPER_CONFIG mconfig) {
this(mconfig,
new LRUMap<>(16, 120),
new LRUMap<>(16, 120));
}
/**
* Constructor that is only added to resolve [jaxrs-providers#10]; problems
* with combination of RESTeasy and CDI.
* 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
protected ProviderBase() {
this(null);
}
/**
* @since 2.17
*/
protected ProviderBase(MAPPER_CONFIG mconfig,
LookupCache readerCache,
LookupCache writerCache)
{
_mapperConfig = mconfig;
_jaxRSFeatures = JAXRS_FEATURE_DEFAULTS;
_readers = readerCache;
_writers = writerCache;
}
/*
/**********************************************************
/* Configuring
/**********************************************************
*/
/**
* 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 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 HashMap();
}
_cfgCustomUntouchables.put(new ClassKey(type), Boolean.TRUE);
}
/**
* Method for removing definition of specified type as untouchable:
* usually only
*
* @since 2.2
*/
public void removeUntouchable(Class> type)
{
if (_cfgCustomUntouchables == null) {
_cfgCustomUntouchables = new HashMap();
}
_cfgCustomUntouchables.put(new ClassKey(type), Boolean.FALSE);
}
/**
* 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(MAPPER m) {
_mapperConfig.setMapper(m);
}
/**
* Method for specifying JSON View to use for reading content
* when end point does not have explicit View annotations.
*
* @since 2.3
*/
public THIS setDefaultReadView(Class> view) {
_defaultReadView = view;
return _this();
}
/**
* Method for specifying JSON View to use for reading content
* when end point does not have explicit View annotations.
*
* @since 2.3
*/
public THIS setDefaultWriteView(Class> view) {
_defaultWriteView = view;
return _this();
}
/**
* Method for specifying JSON View to use for reading and writing content
* when end point does not have explicit View annotations.
* Functionally equivalent to:
*
* setDefaultReadView(view);
* setDefaultWriteView(view);
*
*
* @since 2.3
*/
public THIS setDefaultView(Class> view) {
_defaultReadView = _defaultWriteView = view;
return _this();
}
// // // JaxRSFeature config
public THIS configure(JaxRSFeature feature, boolean state) {
return state ? enable(feature) : disable(feature);
}
public THIS enable(JaxRSFeature feature) {
_jaxRSFeatures |= feature.getMask();
return _this();
}
public THIS enable(JaxRSFeature first, JaxRSFeature... f2) {
_jaxRSFeatures |= first.getMask();
for (JaxRSFeature f : f2) {
_jaxRSFeatures |= f.getMask();
}
return _this();
}
public THIS disable(JaxRSFeature feature) {
_jaxRSFeatures &= ~feature.getMask();
return _this();
}
public THIS disable(JaxRSFeature first, JaxRSFeature... f2) {
_jaxRSFeatures &= ~first.getMask();
for (JaxRSFeature f : f2) {
_jaxRSFeatures &= ~f.getMask();
}
return _this();
}
public boolean isEnabled(JaxRSFeature f) {
return (_jaxRSFeatures & f.getMask()) != 0;
}
// // // DeserializationFeature
public THIS configure(DeserializationFeature f, boolean state) {
_mapperConfig.configure(f, state);
return _this();
}
public THIS enable(DeserializationFeature f) {
_mapperConfig.configure(f, true);
return _this();
}
public THIS disable(DeserializationFeature f) {
_mapperConfig.configure(f, false);
return _this();
}
// // // SerializationFeature
public THIS configure(SerializationFeature f, boolean state) {
_mapperConfig.configure(f, state);
return _this();
}
public THIS enable(SerializationFeature f) {
_mapperConfig.configure(f, true);
return _this();
}
public THIS disable(SerializationFeature f) {
_mapperConfig.configure(f, false);
return _this();
}
// // // JsonParser/JsonGenerator
public THIS enable(JsonParser.Feature f) {
_mapperConfig.configure(f, true);
return _this();
}
public THIS enable(JsonGenerator.Feature f) {
_mapperConfig.configure(f, true);
return _this();
}
public THIS disable(JsonParser.Feature f) {
_mapperConfig.configure(f, false);
return _this();
}
public THIS disable(JsonGenerator.Feature f) {
_mapperConfig.configure(f, false);
return _this();
}
public THIS configure(JsonParser.Feature f, boolean state) {
_mapperConfig.configure(f, state);
return _this();
}
public THIS configure(JsonGenerator.Feature f, boolean state) {
_mapperConfig.configure(f, state);
return _this();
}
/*
/**********************************************************
/* Abstract methods sub-classes need to implement
/**********************************************************
*/
/**
* Helper method used to check whether given media type
* is supported by this provider for read operations
* (when binding input data such as POST body).
*
* Default implementation simply calls {@link #hasMatchingMediaType}.
*
* @since 2.3
*/
protected boolean hasMatchingMediaTypeForReading(MediaType mediaType) {
return hasMatchingMediaType(mediaType);
}
/**
* Helper method used to check whether given media type
* is supported by this provider for writing operations,
* such as when converting response object to response
* body of request (like GET or POST).
*
* Default implementation simply calls {@link #hasMatchingMediaType}.
*
* @since 2.3
*/
protected boolean hasMatchingMediaTypeForWriting(MediaType mediaType) {
return hasMatchingMediaType(mediaType);
}
/**
* Helper method used to check whether given media type
* is supported by this provider.
*
* @since 2.2
*/
protected abstract boolean hasMatchingMediaType(MediaType mediaType);
/**
* Helper method that is called if no mapper has been explicitly configured.
*/
protected abstract MAPPER _locateMapperViaProvider(Class> type, MediaType mediaType);
protected EP_CONFIG _configForReading(MAPPER mapper,
Annotation[] annotations, Class> defaultView)
{
// ObjectReader r = _readerInjector.getAndClear();
ObjectReader r;
if (defaultView != null) {
r = mapper.readerWithView(defaultView);
} else {
r = mapper.reader();
}
if (JaxRSFeature.READ_FULL_STREAM.enabledIn(_jaxRSFeatures)) {
r = r.withFeatures(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
}
return _configForReading(r, annotations);
}
protected EP_CONFIG _configForWriting(MAPPER mapper,
Annotation[] annotations, Class> defaultView)
{
// ObjectWriter w = _writerInjector.getAndClear();
ObjectWriter w;
if (defaultView != null) {
w = mapper.writerWithView(defaultView);
} else {
w = mapper.writer();
}
return _configForWriting(w, annotations);
}
protected abstract EP_CONFIG _configForReading(ObjectReader reader,
Annotation[] annotations);
protected abstract EP_CONFIG _configForWriting(ObjectWriter writer,
Annotation[] annotations);
/*
/**********************************************************
/* Partial MessageBodyWriter 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).
*/
@Override
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
* expected one (by call to {@link #hasMatchingMediaType}); 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)
*/
@Override
public boolean isWriteable(Class> type, Type genericType, Annotation[] annotations, MediaType mediaType)
{
if (!hasMatchingMediaType(mediaType)) {
return false;
}
Boolean customUntouchable = _findCustomUntouchable(type);
if (customUntouchable != null) {
// negation: Boolean.TRUE means untouchable -> can not write
return !customUntouchable.booleanValue();
}
// Ok: looks like we must weed out some core types here; ones that
// make no sense to try to bind from JSON:
if (_isIgnorableForWriting(new ClassKey(type))) {
return false;
}
// but some are interface/abstract classes, so
for (Class> cls : _unwritableClasses) {
if (cls.isAssignableFrom(type)) {
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.
*/
@Override
public void writeTo(Object value, Class> type, Type genericType, Annotation[] annotations,
MediaType mediaType,
MultivaluedMap httpHeaders, OutputStream entityStream)
throws IOException
{
EP_CONFIG endpoint = _endpointForWriting(value, type, genericType, annotations,
mediaType, httpHeaders);
// Any headers we should write?
_modifyHeaders(value, type, genericType, annotations, httpHeaders, endpoint);
ObjectWriter writer = endpoint.getWriter();
// Where can we find desired encoding? Within HTTP headers?
JsonEncoding enc = findEncoding(mediaType, httpHeaders);
JsonGenerator g = _createGenerator(writer, entityStream, enc);
boolean ok = false;
try {
// Want indentation?
if (writer.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
g.useDefaultPrettyPrinter();
}
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 instanceof 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.
// 18-Mar-2015, tatu: As per [#60], there is now a problem with non-polymorphic lists,
// since forcing of type will then force use of content serializer, which is
// generally not the intent. Fix may require addition of functionality in databind
TypeFactory typeFactory = writer.getTypeFactory();
JavaType baseType = typeFactory.constructType(genericType);
rootType = typeFactory.constructSpecializedType(baseType, type);
/* 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.forType(rootType);
}
value = endpoint.modifyBeforeWrite(value);
// [Issue#32]: allow modification by filter-injectible thing
ObjectWriterModifier mod = ObjectWriterInjector.getAndClear();
if (mod != null) {
writer = mod.modify(endpoint, httpHeaders, value, writer, g);
}
writer.writeValue(g, value);
ok = true;
} finally {
if (ok) {
g.close();
} else {
try {
g.close();
} catch (Exception e) { }
}
}
}
/**
* Helper method to use for determining desired output encoding.
* For now, will always just use UTF-8...
*/
protected JsonEncoding findEncoding(MediaType mediaType, MultivaluedMap httpHeaders)
{
return JsonEncoding.UTF8;
}
/**
* Overridable method used for adding optional response headers before
* serializing response object.
*/
protected void _modifyHeaders(Object value, Class> type, Type genericType, Annotation[] annotations,
MultivaluedMap httpHeaders,
EP_CONFIG endpoint)
throws IOException
{
// Add "nosniff" header?
if (isEnabled(JaxRSFeature.ADD_NO_SNIFF_HEADER)) {
httpHeaders.add(HEADER_CONTENT_TYPE_OPTIONS, "nosniff");
}
}
/**
* Overridable helper method called to create a {@link JsonGenerator} for writing
* contents into given raw {@link OutputStream}.
*
* @since 2.3
*/
protected JsonGenerator _createGenerator(ObjectWriter writer, OutputStream rawStream, JsonEncoding enc)
throws IOException
{
JsonGenerator g = writer.getFactory().createGenerator(rawStream, enc);
// Important: we are NOT to close the underlying stream after
// mapping, so we need to instruct generator
g.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
return g;
}
protected EP_CONFIG _endpointForWriting(Object value, Class> type, Type genericType,
Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders)
{
// 29-Jun-2016, tatu: As per [jaxrs-providers#86] allow skipping caching
if (!isEnabled(JaxRSFeature.CACHE_ENDPOINT_WRITERS)) {
return _configForWriting(locateMapper(type, mediaType), annotations, _defaultWriteView);
}
AnnotationBundleKey key = new AnnotationBundleKey(annotations, type);
EP_CONFIG endpoint = _writers.get(key);
// not yet resolved (or not cached any more)? Resolve!
if (endpoint == null) {
MAPPER mapper = locateMapper(type, mediaType);
endpoint = _configForWriting(mapper, annotations, _defaultWriteView);
// and cache for future reuse
_writers.put(key.immutableKey(), endpoint);
}
return endpoint;
}
/*
/**********************************************************
/* 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 #hasMatchingMediaType});
* 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)
*/
@Override
public boolean isReadable(Class> type, Type genericType, Annotation[] annotations, MediaType mediaType)
{
if (!hasMatchingMediaType(mediaType)) {
return false;
}
Boolean customUntouchable = _findCustomUntouchable(type);
if (customUntouchable != null) {
// negation: Boolean.TRUE means untouchable -> can not write
return !customUntouchable.booleanValue();
}
// Ok: looks like we must weed out some core types here; ones that
// make no sense to try to bind from JSON:
if (_isIgnorableForReading(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;
}
}
// Finally: if we really want to verify that we can serialize, we'll check:
if (_cfgCheckCanDeserialize) {
if (_isSpecialReadable(type)) {
return true;
}
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.
*/
@Override
public Object readFrom(Class type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap httpHeaders,
InputStream entityStream)
throws IOException
{
EP_CONFIG endpoint = _endpointForReading(type, genericType, annotations,
mediaType, httpHeaders);
ObjectReader reader = endpoint.getReader();
JsonParser p = _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 (p == null || p.nextToken() == 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.
// 20-Jan-2021, tatu: as per [jaxrs-providers#134], simplify
throw _createNoContentException();
}
Class> rawType = type;
if (rawType == JsonParser.class) {
return p;
}
final TypeFactory tf = reader.getTypeFactory();
final JavaType resolvedType = tf.constructType(genericType);
// 09-Jul-2015, tatu: As per [jaxrs-providers#69], handle MappingIterator too
boolean multiValued = (rawType == MappingIterator.class);
if (multiValued) {
JavaType[] contents = tf.findTypeParameters(resolvedType, MappingIterator.class);
JavaType valueType = (contents == null || contents.length == 0)
? tf.constructType(Object.class) : contents[0];
reader = reader.forType(valueType);
} else {
reader = reader.forType(resolvedType);
}
// [Issue#32]: allow modification by filter-injectable thing
ObjectReaderModifier mod = ObjectReaderInjector.getAndClear();
if (mod != null) {
reader = mod.modify(endpoint, httpHeaders, resolvedType, reader, p);
}
if (multiValued) {
return reader.readValues(p);
}
return reader.readValue(p);
}
/**
* Overridable helper method called to create a {@link JsonParser} for reading
* contents of given raw {@link InputStream}.
* May return null to indicate that Stream is empty; that is, contains no
* content.
*
* @since 2.2
*/
protected JsonParser _createParser(ObjectReader reader, InputStream rawStream)
throws IOException
{
JsonParser p = reader.getFactory().createParser(rawStream);
// Important: we are NOT to close the underlying stream after
// mapping, so we need to instruct parser:
p.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
return p;
}
/**
* Overridable helper method that will basically fetch representation of the
* endpoint that can be used to get {@link ObjectReader} to use for deserializing
* content
*
* @since 2.8
*/
protected EP_CONFIG _endpointForReading(Class type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap httpHeaders)
{
// 29-Jun-2016, tatu: As per [jaxrs-providers#86] allow skipping caching
if (!isEnabled(JaxRSFeature.CACHE_ENDPOINT_READERS)) {
return _configForReading(locateMapper(type, mediaType), annotations, _defaultReadView);
}
AnnotationBundleKey key = new AnnotationBundleKey(annotations, type);
EP_CONFIG 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, _defaultReadView);
// and cache for future reuse
_readers.put(key.immutableKey(), endpoint);
}
return endpoint;
}
/*
/**********************************************************
/* Overridable helper methods
/**********************************************************
*/
/**
* Method called to locate {@link ObjectMapper} to use for serialization
* and deserialization. Exact logic depends on setting of
* {@link JaxRSFeature#DYNAMIC_OBJECT_MAPPER_LOOKUP}.
*
*
* If {@link JaxRSFeature#DYNAMIC_OBJECT_MAPPER_LOOKUP} is disabled (default
* setting unless changed), behavior is as follows:
*
* 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
* 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)
*
*
*
* If {@link JaxRSFeature#DYNAMIC_OBJECT_MAPPER_LOOKUP} is enabled, steps
* 1 and 2 are reversed, such that JAX-RS ContextResolver
* is first used, and only if none is defined will configured mapper be used.
*
* @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 ContextResolver
as is.
* @param mediaType Declared media type for the instance to process:
* not used by this method,
* but will be passed to ContextResolver
as is.
*/
public MAPPER locateMapper(Class> type, MediaType mediaType)
{
// 29-Jun-2016, tatu: As per [jaxrs-providers#86] may want to do provider lookup first
if (isEnabled(JaxRSFeature.DYNAMIC_OBJECT_MAPPER_LOOKUP)) {
MAPPER m = _locateMapperViaProvider(type, mediaType);
if (m == null) {
m = _mapperConfig.getConfiguredMapper();
if (m == null) {
m = _mapperConfig.getDefaultMapper();
}
}
return m;
}
// Otherwise start with (pre-)configured Mapper and only check provider
// if not found
MAPPER m = _mapperConfig.getConfiguredMapper();
if (m == null) {
// If not, maybe we can get one configured via context?
m = _locateMapperViaProvider(type, mediaType);
if (m == null) {
// If not, let's get the fallback default instance
m = _mapperConfig.getDefaultMapper();
}
}
return m;
}
/**
* Overridable helper method used to allow handling of somewhat special
* types for reading
*
* @since 2.2
*/
protected boolean _isSpecialReadable(Class> type) {
return JsonParser.class == type;
}
/**
* Overridable helper method called to check whether given type is a known
* "ignorable type" (in context of reading), values of which are not bound
* from content.
*
* @since 2.6
*/
protected boolean _isIgnorableForReading(ClassKey typeKey)
{
return _untouchables.contains(typeKey);
}
/**
* Overridable helper method called to check whether given type is a known
* "ignorable type" (in context of reading), values of which
* can not be written out.
*
* @since 2.6
*/
protected boolean _isIgnorableForWriting(ClassKey typeKey)
{
return _untouchables.contains(typeKey);
}
protected IOException _createNoContentException() {
return noContentExceptionSupplier.createNoContentException();
}
/*
/**********************************************************
/* Private/sub-class helper methods
/**********************************************************
*/
protected static boolean _containedIn(Class> mainType, HashSet 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;
}
}
return false;
}
protected Boolean _findCustomUntouchable(Class> mainType)
{
if (_cfgCustomUntouchables != null) {
ClassKey key = new ClassKey(mainType);
// First: type itself?
Boolean b = _cfgCustomUntouchables.get(key);
if (b != null) {
return b;
}
// Then supertypes (note: will not contain Object.class)
for (Class> cls : findSuperTypes(mainType, null)) {
key.reset(cls);
b = _cfgCustomUntouchables.get(key);
if (b != null) {
return b;
}
}
}
return null;
}
protected static List> findSuperTypes(Class> cls, Class> endBefore)
{
return findSuperTypes(cls, endBefore, new ArrayList>(8));
}
protected static List> findSuperTypes(Class> cls, Class> endBefore, List> result)
{
_addSuperTypes(cls, endBefore, result, false);
return result;
}
protected static void _addSuperTypes(Class> cls, Class> endBefore, Collection> 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);
}
@SuppressWarnings("unchecked")
private final THIS _this() {
return (THIS) this;
}
/**
* Since class javax.ws.rs.core.NoContentException
only exists in
* JAX-RS 2.0, but we want to have 1.x compatibility, need to dynamically select exception supplier
*/
private static NoContentExceptionSupplier _createNoContentExceptionSupplier() {
try {
final Class> cls = Class.forName(CLASS_NAME_NO_CONTENT_EXCEPTION, false, getClassLoader());
Constructor> ctor;
if (System.getSecurityManager() == null) {
ctor = cls.getDeclaredConstructor(String.class);
} else {
ctor = AccessController.doPrivileged(new PrivilegedAction>() {
@Override
public Constructor> run() {
try {
return cls.getDeclaredConstructor(String.class);
} catch (NoSuchMethodException ignore) {
return null;
}
}
});
}
if (ctor != null) {
return new JaxRS2NoContentExceptionSupplier();
}
} catch (ClassNotFoundException | NoSuchMethodException ex) { }
return new JaxRS1NoContentExceptionSupplier();
}
private static ClassLoader getClassLoader() {
if (System.getSecurityManager() == null) {
return ProviderBase.class.getClassLoader();
}
return AccessController.doPrivileged(new PrivilegedAction() {
@Override
public ClassLoader run() {
return ProviderBase.class.getClassLoader();
}
});
}
}