,
Versioned
{
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();
protected final static HashSet DEFAULT_UNTOUCHABLES = new HashSet();
static {
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));
DEFAULT_UNTOUCHABLES.add(new ClassKey(char [].class));
DEFAULT_UNTOUCHABLES.add(new ClassKey(String.class));
DEFAULT_UNTOUCHABLES.add(new ClassKey(byte [].class));
}
public final static Class[] DEFAULT_UNREADABLES = new Class[] {
InputStream.class, Reader.class
};
public final static Class[] DEFAULT_UNWRITABLES = new Class[] {
InputStream.class,
OutputStream.class, Writer.class,
StreamingOutput.class, Response.class
};
protected final static int JAXRS_FEATURE_DEFAULTS = JaxRSFeature.collectDefaults();
protected final MAPPER_CONFIG _mapperConfig;
protected HashMap _cfgCustomUntouchables;
protected boolean _cfgCheckCanSerialize = false ;
protected boolean _cfgCheckCanDeserialize = false ;
protected int _jaxRSFeatures;
protected Class _defaultReadView;
protected Class _defaultWriteView;
public final static HashSet _untouchables = DEFAULT_UNTOUCHABLES;
public final static Class[] _unreadableClasses = DEFAULT_UNREADABLES;
public final static Class[] _unwritableClasses = DEFAULT_UNWRITABLES;
protected final LookupCache _readers;
protected final LookupCache _writers;
protected ProviderBase (MAPPER_CONFIG mconfig) {
this (mconfig,
new LRUMap<>(16 , 120 ),
new LRUMap<>(16 , 120 ));
}
@Deprecated
protected ProviderBase () {
this (null );
}
protected ProviderBase (MAPPER_CONFIG mconfig,
LookupCache readerCache,
LookupCache writerCache)
{
_mapperConfig = mconfig;
_jaxRSFeatures = JAXRS_FEATURE_DEFAULTS;
_readers = readerCache;
_writers = writerCache;
}
public void checkCanDeserialize (boolean state) { _cfgCheckCanDeserialize = state; }
public void checkCanSerialize (boolean state) { _cfgCheckCanSerialize = state; }
public void addUntouchable (Class type)
{
if (_cfgCustomUntouchables == null ) {
_cfgCustomUntouchables = new HashMap();
}
_cfgCustomUntouchables.put(new ClassKey(type), Boolean.TRUE);
}
public void removeUntouchable (Class type)
{
if (_cfgCustomUntouchables == null ) {
_cfgCustomUntouchables = new HashMap();
}
_cfgCustomUntouchables.put(new ClassKey(type), Boolean.FALSE);
}
public void setAnnotationsToUse (Annotations[] annotationsToUse) {
_mapperConfig.setAnnotationsToUse(annotationsToUse);
}
public void setMapper (MAPPER m) {
_mapperConfig.setMapper(m);
}
public THIS setDefaultReadView (Class view) {
_defaultReadView = view;
return _this();
}
public THIS setDefaultWriteView (Class view) {
_defaultWriteView = view;
return _this();
}
public THIS setDefaultView (Class view) {
_defaultReadView = _defaultWriteView = view;
return _this();
}
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 ;
}
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();
}
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();
}
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();
}
protected boolean hasMatchingMediaTypeForReading (MediaType mediaType) {
return hasMatchingMediaType(mediaType);
}
protected boolean hasMatchingMediaTypeForWriting (MediaType mediaType) {
return hasMatchingMediaType(mediaType);
}
protected abstract boolean hasMatchingMediaType (MediaType mediaType) ;
protected abstract MAPPER _locateMapperViaProvider (Class type, MediaType mediaType) ;
protected EP_CONFIG _configForReading (MAPPER mapper,
Annotation[] annotations, Class defaultView)
{
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;
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) ;
@Override
public long getSize (Object value, Class type, Type genericType, Annotation[] annotations, MediaType mediaType)
{
return -1 ;
}
@Override
public boolean isWriteable (Class type, Type genericType, Annotation[] annotations, MediaType mediaType)
{
if (!hasMatchingMediaType(mediaType)) {
return false ;
}
Boolean customUntouchable = _findCustomUntouchable(type);
if (customUntouchable != null ) {
return !customUntouchable.booleanValue();
}
if (_isIgnorableForWriting(new ClassKey(type))) {
return false ;
}
for (Class cls : _unwritableClasses) {
if (cls.isAssignableFrom(type)) {
return false ;
}
}
if (_cfgCheckCanSerialize) {
if (!locateMapper(type, mediaType).canSerialize(type)) {
return false ;
}
}
return true ;
}
@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);
_modifyHeaders(value, type, genericType, annotations, httpHeaders, endpoint);
ObjectWriter writer = endpoint.getWriter();
JsonEncoding enc = findEncoding(mediaType, httpHeaders);
JsonGenerator g = _createGenerator(writer, entityStream, enc);
boolean ok = false ;
try {
if (writer.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
g.useDefaultPrettyPrinter();
}
JavaType rootType = null ;
if ((genericType != null ) && (value != null )) {
if (!(genericType instanceof Class)) {
TypeFactory typeFactory = writer.getTypeFactory();
JavaType baseType = typeFactory.constructType(genericType);
rootType = typeFactory.constructSpecializedType(baseType, type);
if (rootType.getRawClass() == Object.class) {
rootType = null ;
}
}
}
if (rootType != null ) {
writer = writer.forType(rootType);
}
value = endpoint.modifyBeforeWrite(value);
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) { }
}
}
}
protected JsonEncoding findEncoding (MediaType mediaType, MultivaluedMap httpHeaders)
{
return JsonEncoding.UTF8;
}
protected void _modifyHeaders (Object value, Class type, Type genericType, Annotation[] annotations,
MultivaluedMap httpHeaders,
EP_CONFIG endpoint)
throws IOException
{
if (isEnabled(JaxRSFeature.ADD_NO_SNIFF_HEADER)) {
httpHeaders.add(HEADER_CONTENT_TYPE_OPTIONS, "nosniff" );
}
}
protected JsonGenerator _createGenerator (ObjectWriter writer, OutputStream rawStream, JsonEncoding enc)
throws IOException
{
JsonGenerator g = writer.getFactory().createGenerator(rawStream, enc);
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)
{
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);
if (endpoint == null ) {
MAPPER mapper = locateMapper(type, mediaType);
endpoint = _configForWriting(mapper, annotations, _defaultWriteView);
_writers.put(key.immutableKey(), endpoint);
}
return endpoint;
}
@Override
public boolean isReadable (Class type, Type genericType, Annotation[] annotations, MediaType mediaType)
{
if (!hasMatchingMediaType(mediaType)) {
return false ;
}
Boolean customUntouchable = _findCustomUntouchable(type);
if (customUntouchable != null ) {
return !customUntouchable.booleanValue();
}
if (_isIgnorableForReading(new ClassKey(type))) {
return false ;
}
for (Class cls : _unreadableClasses) {
if (cls.isAssignableFrom(type)) {
return false ;
}
}
if (_cfgCheckCanDeserialize) {
if (_isSpecialReadable(type)) {
return true ;
}
ObjectMapper mapper = locateMapper(type, mediaType);
if (!mapper.canDeserialize(mapper.constructType(type))) {
return false ;
}
}
return true ;
}
@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 (p == null || p.nextToken() == null ) {
if (JaxRSFeature.ALLOW_EMPTY_INPUT.enabledIn(_jaxRSFeatures)) {
return null ;
}
throw _createNoContentException();
}
Class rawType = type;
if (rawType == JsonParser.class) {
return p;
}
final TypeFactory tf = reader.getTypeFactory();
final JavaType resolvedType = tf.constructType(genericType);
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);
}
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);
}
protected JsonParser _createParser (ObjectReader reader, InputStream rawStream)
throws IOException
{
JsonParser p = reader.getFactory().createParser(rawStream);
p.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
return p;
}
protected EP_CONFIG _endpointForReading (Class type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap httpHeaders)
{
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);
if (endpoint == null ) {
MAPPER mapper = locateMapper(type, mediaType);
endpoint = _configForReading(mapper, annotations, _defaultReadView);
_readers.put(key.immutableKey(), endpoint);
}
return endpoint;
}
public MAPPER locateMapper (Class type, MediaType mediaType)
{
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;
}
MAPPER m = _mapperConfig.getConfiguredMapper();
if (m == null ) {
m = _locateMapperViaProvider(type, mediaType);
if (m == null ) {
m = _mapperConfig.getDefaultMapper();
}
}
return m;
}
protected boolean _isSpecialReadable (Class type) {
return JsonParser.class == type;
}
protected boolean _isIgnorableForReading (ClassKey typeKey)
{
return _untouchables.contains(typeKey);
}
protected boolean _isIgnorableForWriting (ClassKey typeKey)
{
return _untouchables.contains(typeKey);
}
protected IOException _createNoContentException () {
return noContentExceptionSupplier.createNoContentException();
}
protected static boolean _containedIn (Class mainType, HashSet set)
{
if (set != null ) {
ClassKey key = new ClassKey(mainType);
if (set.contains(key)) return true ;
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);
Boolean b = _cfgCustomUntouchables.get(key);
if (b != null ) {
return b;
}
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)) {
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 ;
}
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();
}
});
}
}