org.glassfish.jersey.message.internal.MessageBodyFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2014 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.jersey.message.internal;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.WriterInterceptor;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.xml.transform.Source;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.PropertiesDelegate;
import org.glassfish.jersey.internal.inject.Providers;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.ReflectionHelper.DeclaringClassInterfacePair;
import org.glassfish.jersey.internal.util.collection.DataStructures;
import org.glassfish.jersey.internal.util.collection.KeyComparator;
import org.glassfish.jersey.internal.util.collection.KeyComparatorHashMap;
import org.glassfish.jersey.internal.util.collection.KeyComparatorLinkedHashMap;
import org.glassfish.jersey.message.MessageBodyWorkers;
import org.glassfish.jersey.message.MessageProperties;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.jvnet.hk2.annotations.Optional;
import jersey.repackaged.com.google.common.collect.Lists;
import jersey.repackaged.com.google.common.collect.Sets;
import jersey.repackaged.com.google.common.primitives.Primitives;
/**
* A factory for managing {@link MessageBodyReader}, {@link MessageBodyWriter} instances.
*
* @author Paul Sandoz
* @author Marek Potociar (marek.potociar at oracle.com)
* @author Jakub Podlesak (jakub.podlesak at oracle.com)
*/
public class MessageBodyFactory implements MessageBodyWorkers {
private static final Logger LOGGER = Logger.getLogger(MessageBodyFactory.class.getName());
/**
* Message body factory injection binder.
*/
public static class Binder extends AbstractBinder {
@Override
protected void configure() {
bindAsContract(MessageBodyFactory.class).to(MessageBodyWorkers.class).in(Singleton.class);
}
}
/**
* Media type comparator.
*/
public static final KeyComparator MEDIA_TYPE_COMPARATOR =
new KeyComparator() {
private static final long serialVersionUID = 2727819828630827763L;
@Override
public boolean equals(MediaType x, MediaType y) {
return x.getType().equalsIgnoreCase(y.getType())
&& x.getSubtype().equalsIgnoreCase(y.getSubtype());
}
@Override
public int hash(MediaType k) {
return k.getType().toLowerCase().hashCode()
+ k.getSubtype().toLowerCase().hashCode();
}
// move to separate comparator?
@Override
public int compare(MediaType o1, MediaType o2) {
if (equals(o1, o2)) {
return 0;
} else if (o1.isWildcardType() ^ o2.isWildcardType()) {
return (o1.isWildcardType()) ? 1 : -1;
} else if (o1.isWildcardSubtype() ^ o2.isWildcardSubtype()) {
return (o1.isWildcardSubtype()) ? 1 : -1;
}
return 0;
}
};
/**
* Compares message body workers by providing class (most specific first) and assigned media types if provider classes are
* the same.
*/
private static final Comparator> WORKER_BY_TYPE_COMPARATOR =
new Comparator>() {
@Override
public int compare(final WorkerModel> o1, final WorkerModel> o2) {
final Class> o1ProviderClassParam = o1.providerClassParam;
final Class> o2ProviderClassParam = o2.providerClassParam;
if (o1ProviderClassParam == o2ProviderClassParam) {
// Compare producible media types.
return compare(o2.types, o1.types);
} else if (o1ProviderClassParam.isAssignableFrom(o2ProviderClassParam)) {
return 1;
} else if (o2ProviderClassParam.isAssignableFrom(o1ProviderClassParam)) {
return -1;
}
return 0;
}
private int compare(List mediaTypeList1, List mediaTypeList2) {
mediaTypeList1 = mediaTypeList1.isEmpty() ? MediaTypes.GENERAL_MEDIA_TYPE_LIST : mediaTypeList1;
mediaTypeList2 = mediaTypeList2.isEmpty() ? MediaTypes.GENERAL_MEDIA_TYPE_LIST : mediaTypeList2;
return MediaTypes.MEDIA_TYPE_LIST_COMPARATOR.compare(mediaTypeList2, mediaTypeList1);
}
};
private final ServiceLocator serviceLocator;
private final Boolean legacyProviderOrdering;
private final List readers;
private final List writers;
private final Map> readersCache =
new KeyComparatorHashMap>(MEDIA_TYPE_COMPARATOR);
private final Map> writersCache =
new KeyComparatorHashMap>(MEDIA_TYPE_COMPARATOR);
private static final int LOOKUP_CACHE_INITIAL_CAPACITY = 32;
private static final float LOOKUP_CACHE_LOAD_FACTOR = 0.75f;
private final Map, List> mbrTypeLookupCache = DataStructures.createConcurrentMap(
LOOKUP_CACHE_INITIAL_CAPACITY, LOOKUP_CACHE_LOAD_FACTOR, DataStructures.DEFAULT_CONCURENCY_LEVEL);
private final Map, List> mbwTypeLookupCache = DataStructures.createConcurrentMap(
LOOKUP_CACHE_INITIAL_CAPACITY, LOOKUP_CACHE_LOAD_FACTOR, DataStructures.DEFAULT_CONCURENCY_LEVEL);
private final Map, List> typeToMediaTypeReadersCache = DataStructures.createConcurrentMap(
LOOKUP_CACHE_INITIAL_CAPACITY, LOOKUP_CACHE_LOAD_FACTOR, DataStructures.DEFAULT_CONCURENCY_LEVEL);
private final Map, List> typeToMediaTypeWritersCache = DataStructures.createConcurrentMap(
LOOKUP_CACHE_INITIAL_CAPACITY, LOOKUP_CACHE_LOAD_FACTOR, DataStructures.DEFAULT_CONCURENCY_LEVEL);
private final Map> mbrLookupCache = DataStructures.createConcurrentMap(
LOOKUP_CACHE_INITIAL_CAPACITY, LOOKUP_CACHE_LOAD_FACTOR, DataStructures.DEFAULT_CONCURENCY_LEVEL);
private final Map> mbwLookupCache = DataStructures.createConcurrentMap(
LOOKUP_CACHE_INITIAL_CAPACITY, LOOKUP_CACHE_LOAD_FACTOR, DataStructures.DEFAULT_CONCURENCY_LEVEL);
private static class WorkerModel {
public final T provider;
public final List types;
public final Boolean custom;
public final Class> providerClassParam;
protected WorkerModel(
final T provider, final List types, final Boolean custom, Class providerType) {
this.provider = provider;
this.types = types;
this.custom = custom;
this.providerClassParam = getProviderClassParam(provider, providerType);
}
private static Class> getProviderClassParam(Object provider, Class> providerType) {
final ReflectionHelper.DeclaringClassInterfacePair pair =
ReflectionHelper.getClass(provider.getClass(), providerType);
final Class[] classArgs = ReflectionHelper.getParameterizedClassArguments(pair);
return classArgs != null ? classArgs[0] : Object.class;
}
}
private static class MbrModel extends WorkerModel {
public MbrModel(MessageBodyReader provider, List types, Boolean custom) {
super(provider, types, custom, MessageBodyReader.class);
}
public boolean isReadable(Class> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return MbrModel.isReadable(provider, type, genericType, annotations, mediaType);
}
@SuppressWarnings("unchecked")
public static boolean isReadable(MessageBodyReader provider,
Class> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
try {
return provider.isReadable(type, genericType, annotations, mediaType);
} catch (Exception ex) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, LocalizationMessages.ERROR_MBR_ISREADABLE(provider.getClass().getName()), ex);
}
}
return false;
}
}
private static class MbwModel extends WorkerModel {
public MbwModel(MessageBodyWriter provider, List types, Boolean custom) {
super(provider, types, custom, MessageBodyWriter.class);
}
public boolean isWriteable(Class> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return MbwModel.isWriteable(provider, type, genericType, annotations, mediaType);
}
@SuppressWarnings("unchecked")
public static boolean isWriteable(MessageBodyWriter provider,
Class> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
try {
return provider.isWriteable(type, genericType, annotations, mediaType);
} catch (Exception ex) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, LocalizationMessages.ERROR_MBW_ISWRITABLE(provider.getClass().getName()), ex);
}
}
return false;
}
}
/**
* Create new message body workers factory.
*
* @param locator service locator.
* @param configuration configuration. Optional - can be null.
*/
@Inject
public MessageBodyFactory(ServiceLocator locator, @Optional Configuration configuration) {
this.serviceLocator = locator;
this.legacyProviderOrdering = configuration != null
&& PropertiesHelper.isProperty(configuration.getProperty(MessageProperties.LEGACY_WORKERS_ORDERING));
// Initialize readers
this.readers = new ArrayList();
final Set customMbrs = Providers.getCustomProviders(locator, MessageBodyReader.class);
final Set mbrs = Providers.getProviders(locator, MessageBodyReader.class);
addReaders(readers, customMbrs, true);
mbrs.removeAll(customMbrs);
addReaders(readers, mbrs, false);
if (legacyProviderOrdering) {
Collections.sort(readers, new LegacyWorkerComparator(MessageBodyReader.class));
for (MbrModel model : readers) {
for (MediaType mt : model.types) {
List readerList = readersCache.get(mt);
if (readerList == null) {
readerList = new ArrayList();
readersCache.put(mt, readerList);
}
readerList.add(model.provider);
}
}
}
// Initialize writers
this.writers = new ArrayList();
final Set customMbws = Providers.getCustomProviders(locator, MessageBodyWriter.class);
final Set mbws = Providers.getProviders(locator, MessageBodyWriter.class);
addWriters(writers, customMbws, true);
mbws.removeAll(customMbws);
addWriters(writers, mbws, false);
if (legacyProviderOrdering) {
Collections.sort(writers, new LegacyWorkerComparator(MessageBodyWriter.class));
for (WorkerModel model : writers) {
for (MediaType mt : model.types) {
List writerList = writersCache.get(mt);
if (writerList == null) {
writerList = new ArrayList();
writersCache.put(mt, writerList);
}
writerList.add(model.provider);
}
}
}
}
/**
* Compares 2 instances implementing/inheriting the same super-type and returns
* which of the two instances has the super-type declaration closer in it's
* inheritance hierarchy tree.
*
* The comparator is optimized to cache results of the previous distance declaration
* computations.
*
* @param common super-type used for computing the declaration distance and
* comparing instances.
*/
private static class DeclarationDistanceComparator implements Comparator {
private final Class declared;
private final Map distanceMap = new HashMap();
DeclarationDistanceComparator(Class declared) {
this.declared = declared;
}
@Override
public int compare(T o1, T o2) {
int d1 = getDistance(o1);
int d2 = getDistance(o2);
return d2 - d1;
}
private int getDistance(T t) {
Integer distance = distanceMap.get(t.getClass());
if (distance != null) {
return distance;
}
DeclaringClassInterfacePair p = ReflectionHelper.getClass(
t.getClass(), declared);
Class[] as = ReflectionHelper.getParameterizedClassArguments(p);
Class a = (as != null) ? as[0] : null;
distance = 0;
while (a != null && a != Object.class) {
distance++;
a = a.getSuperclass();
}
distanceMap.put(t.getClass(), distance);
return distance;
}
}
/**
* {@link org.glassfish.jersey.message.internal.MessageBodyFactory.WorkerModel} comparator
* which works as it is described in JAX-RS 2.x specification.
*
* Pairs are sorted by distance from required type, media type and custom/provided (provided goes first).
*
* @param MessageBodyReader or MessageBodyWriter.
* @see DeclarationDistanceComparator
* @see #MEDIA_TYPE_COMPARATOR
*/
private static class WorkerComparator implements Comparator> {
final Class wantedType;
final MediaType wantedMediaType;
private WorkerComparator(Class wantedType, MediaType wantedMediaType) {
this.wantedType = wantedType;
this.wantedMediaType = wantedMediaType;
}
@Override
public int compare(WorkerModel modelA, WorkerModel modelB) {
final int distance = compareTypeDistances(modelA.providerClassParam, modelB.providerClassParam);
if (distance != 0) {
return distance;
}
final int mediaTypeComparison =
getMediaTypeDistance(wantedMediaType, modelA.types) - getMediaTypeDistance(wantedMediaType, modelB.types);
if (mediaTypeComparison != 0) {
return mediaTypeComparison;
}
if (modelA.custom ^ modelB.custom) {
return (modelA.custom) ? -1 : 1;
}
return 0;
}
private int getMediaTypeDistance(MediaType wanted, List mtl) {
if (wanted == null) {
return 0;
}
int distance = 2;
for (MediaType mt : mtl) {
if (MediaTypes.typeEqual(wanted, mt)) {
return 0;
}
if (distance > 1 && MediaTypes.typeEqual(MediaTypes.getTypeWildCart(wanted), mt)) {
distance = 1;
}
}
return distance;
}
private int compareTypeDistances(Class> providerClassParam1, Class> providerClassParam2) {
return getTypeDistance(providerClassParam1) - getTypeDistance(providerClassParam2);
}
private int getTypeDistance(Class> classParam) {
// cache?
Class> tmp1 = wantedType;
Class> tmp2 = classParam;
final Iterator> it1 = getClassHierarchyIterator(tmp1);
final Iterator> it2 = getClassHierarchyIterator(tmp2);
int distance = 0;
while (!wantedType.equals(tmp2) && !classParam.equals(tmp1)) {
distance++;
if (!wantedType.equals(tmp2)) {
tmp2 = it2.hasNext() ? it2.next() : null;
}
if (!classParam.equals(tmp1)) {
tmp1 = it1.hasNext() ? it1.next() : null;
}
if (tmp2 == null && tmp1 == null) {
return Integer.MAX_VALUE;
}
}
return distance;
}
private Iterator> getClassHierarchyIterator(final Class> classParam) {
if (classParam == null) {
return Collections.>emptyList().iterator();
}
final ArrayList> classes = new ArrayList>();
final LinkedList> unprocessed = new LinkedList>();
unprocessed.add(classParam);
while (!unprocessed.isEmpty()) {
final Class> clazz = unprocessed.removeFirst();
classes.add(clazz);
unprocessed.addAll(Arrays.asList(clazz.getInterfaces()));
final Class> superclazz = clazz.getSuperclass();
if (superclazz != null) {
unprocessed.add(superclazz);
}
}
return classes.iterator();
}
}
/**
* {@link org.glassfish.jersey.message.internal.MessageBodyFactory.WorkerModel} comparator which
* works as it is described in JAX-RS 1.x specification.
*
* Pairs are sorted by custom/provided (custom goes first), media type and declaration distance.
*
* @param MessageBodyReader or MessageBodyWriter.
* @see DeclarationDistanceComparator
* @see #MEDIA_TYPE_COMPARATOR
*/
private static class LegacyWorkerComparator implements Comparator> {
final DeclarationDistanceComparator distanceComparator;
private LegacyWorkerComparator(Class type) {
distanceComparator = new DeclarationDistanceComparator(type);
}
@Override
public int compare(WorkerModel modelA, WorkerModel modelB) {
if (modelA.custom ^ modelB.custom) {
return (modelA.custom) ? -1 : 1;
}
final int mediaTypeComparison = MEDIA_TYPE_COMPARATOR.compare(modelA.types.get(0), modelB.types.get(0));
if (mediaTypeComparison != 0) {
return mediaTypeComparison;
}
return distanceComparator.compare(modelA.provider, modelB.provider);
}
}
private static class ModelLookupKey {
final Class> clazz;
final MediaType mediaType;
private ModelLookupKey(Class> clazz, MediaType mediaType) {
this.clazz = clazz;
this.mediaType = mediaType;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ModelLookupKey that = (ModelLookupKey) o;
return !(clazz != null ? !clazz.equals(that.clazz) : that.clazz != null) &&
!(mediaType != null ? !mediaType.equals(that.mediaType) : that.mediaType != null);
}
@Override
public int hashCode() {
int result = clazz != null ? clazz.hashCode() : 0;
result = 31 * result + (mediaType != null ? mediaType.hashCode() : 0);
return result;
}
}
private static void addReaders(List models, Set readers, boolean custom) {
for (MessageBodyReader provider : readers) {
List values = MediaTypes.createFrom(provider.getClass().getAnnotation(Consumes.class));
models.add(new MbrModel(provider, values, custom));
}
}
private static void addWriters(List models, Set writers, boolean custom) {
for (MessageBodyWriter provider : writers) {
List values = MediaTypes.createFrom(provider.getClass().getAnnotation(Produces.class));
models.add(new MbwModel(provider, values, custom));
}
}
// MessageBodyWorkers
@Override
public Map> getReaders(MediaType mediaType) {
Map> subSet =
new KeyComparatorLinkedHashMap>(
MEDIA_TYPE_COMPARATOR);
getCompatibleProvidersMap(mediaType, readers, subSet);
return subSet;
}
@Override
public Map> getWriters(MediaType mediaType) {
Map> subSet =
new KeyComparatorLinkedHashMap>(
MEDIA_TYPE_COMPARATOR);
getCompatibleProvidersMap(mediaType, writers, subSet);
return subSet;
}
@Override
public String readersToString(Map> readers) {
return toString(readers);
}
@Override
public String writersToString(Map> writers) {
return toString(writers);
}
private String toString(Map> set) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
for (Map.Entry> e : set.entrySet()) {
pw.append(e.getKey().toString()).println(" ->");
for (T t : e.getValue()) {
pw.append(" ").println(t.getClass().getName());
}
}
pw.flush();
return sw.toString();
}
@Override
public MessageBodyReader getMessageBodyReader(Class c, Type t,
Annotation[] as,
MediaType mediaType) {
return getMessageBodyReader(c, t, as, mediaType, null);
}
@Override
public MessageBodyReader getMessageBodyReader(Class c, Type t,
Annotation[] as,
MediaType mediaType,
PropertiesDelegate propertiesDelegate) {
MessageBodyReader p = null;
if (legacyProviderOrdering) {
if (mediaType != null) {
p = _getMessageBodyReader(c, t, as, mediaType, mediaType, propertiesDelegate);
if (p == null) {
p = _getMessageBodyReader(c, t, as, mediaType, MediaTypes.getTypeWildCart(mediaType), propertiesDelegate);
}
}
if (p == null) {
p = _getMessageBodyReader(c, t, as, mediaType, MediaTypes.GENERAL_MEDIA_TYPE, propertiesDelegate);
}
} else {
p = _getMessageBodyReader(c, t, as, mediaType, readers, propertiesDelegate);
}
return p;
}
@Override
@SuppressWarnings("unchecked")
public List getMessageBodyReaderMediaTypes(Class> type, Type genericType, Annotation[] annotations) {
final Set readableMediaTypes = Sets.newLinkedHashSet();
for (MbrModel model : readers) {
boolean readableWorker = false;
for (MediaType mt : model.types) {
if (model.isReadable(type, genericType, annotations, mt)) {
readableMediaTypes.add(mt);
readableWorker = true;
}
if (!readableMediaTypes.contains(MediaType.WILDCARD_TYPE)
&& readableWorker
&& model.types.contains(MediaType.WILDCARD_TYPE)) {
readableMediaTypes.add(MediaType.WILDCARD_TYPE);
}
}
}
final List mtl = Lists.newArrayList(readableMediaTypes);
Collections.sort(mtl, MediaTypes.MEDIA_TYPE_COMPARATOR);
return mtl;
}
@SuppressWarnings("unchecked")
private boolean isCompatible(WorkerModel model, Class c, MediaType mediaType) {
if (model.providerClassParam.equals(Object.class) ||
// looks weird. Could/(should?) be separated to Writer/Reader check
model.providerClassParam.isAssignableFrom(c) ||
c.isAssignableFrom(model.providerClassParam)
) {
for (MediaType mt : model.types) {
if (mediaType == null) {
return true;
}
if (mediaType.isCompatible(mt)) {
return true;
}
}
}
return false;
}
@SuppressWarnings("unchecked")
private MessageBodyReader _getMessageBodyReader(Class c, Type t,
Annotation[] as,
MediaType mediaType,
List models,
PropertiesDelegate propertiesDelegate) {
// Ensure a parameter-less lookup type to prevent excessive memory consumption
// reported in JERSEY-2297
final MediaType lookupType = mediaType == null || mediaType.getParameters().isEmpty() ?
mediaType :
new MediaType(mediaType.getType(), mediaType.getSubtype());
final ModelLookupKey lookupKey = new ModelLookupKey(c, lookupType);
List readers = mbrLookupCache.get(lookupKey);
if (readers == null) {
readers = new ArrayList();
for (MbrModel model : models) {
if (isCompatible(model, c, mediaType)) {
readers.add(model);
}
}
Collections.sort(readers, new WorkerComparator(c, mediaType));
mbrLookupCache.put(lookupKey, readers);
}
if (readers.isEmpty()) {
return null;
}
final TracingLogger tracingLogger = TracingLogger.getInstance(propertiesDelegate);
MessageBodyReader selected = null;
final Iterator iterator = readers.iterator();
while (iterator.hasNext()) {
final MbrModel model = iterator.next();
if (model.isReadable(c, t, as, mediaType)) {
selected = (MessageBodyReader) model.provider;
tracingLogger.log(MsgTraceEvent.MBR_SELECTED, selected);
break;
}
tracingLogger.log(MsgTraceEvent.MBR_NOT_READABLE, model.provider);
}
if (tracingLogger.isLogEnabled(MsgTraceEvent.MBR_SKIPPED)) {
while (iterator.hasNext()) {
final MbrModel model = iterator.next();
tracingLogger.log(MsgTraceEvent.MBR_SKIPPED, model.provider);
}
}
return selected;
}
@SuppressWarnings("unchecked")
private MessageBodyReader _getMessageBodyReader(Class c, Type t,
Annotation[] as,
MediaType mediaType, MediaType lookup,
PropertiesDelegate propertiesDelegate) {
List readers = readersCache.get(lookup);
if (readers == null) {
return null;
}
final TracingLogger tracingLogger = TracingLogger.getInstance(propertiesDelegate);
MessageBodyReader selected = null;
final Iterator iterator = readers.iterator();
while (iterator.hasNext()) {
final MessageBodyReader p = iterator.next();
if (MbrModel.isReadable(p, c, t, as, mediaType)) {
selected = (MessageBodyReader) p;
tracingLogger.log(MsgTraceEvent.MBR_SELECTED, selected);
break;
}
tracingLogger.log(MsgTraceEvent.MBR_NOT_READABLE, p);
}
if (tracingLogger.isLogEnabled(MsgTraceEvent.MBR_SKIPPED)) {
while (iterator.hasNext()) {
final MessageBodyReader p = iterator.next();
tracingLogger.log(MsgTraceEvent.MBR_SKIPPED, p);
}
}
return selected;
}
@Override
public MessageBodyWriter getMessageBodyWriter(Class c, Type t,
Annotation[] as,
MediaType mediaType) {
return getMessageBodyWriter(c, t, as, mediaType, null);
}
@Override
public MessageBodyWriter getMessageBodyWriter(Class c, Type t,
Annotation[] as,
MediaType mediaType,
PropertiesDelegate propertiesDelegate) {
MessageBodyWriter p = null;
if (legacyProviderOrdering) {
if (mediaType != null) {
p = _getMessageBodyWriter(c, t, as, mediaType, mediaType, propertiesDelegate);
if (p == null) {
p = _getMessageBodyWriter(c, t, as, mediaType, MediaTypes.getTypeWildCart(mediaType), propertiesDelegate);
}
}
if (p == null) {
p = _getMessageBodyWriter(c, t, as, mediaType, MediaTypes.GENERAL_MEDIA_TYPE, propertiesDelegate);
}
} else {
p = _getMessageBodyWriter(c, t, as, mediaType, writers, propertiesDelegate);
}
return p;
}
@SuppressWarnings("unchecked")
private MessageBodyWriter _getMessageBodyWriter(Class c, Type t,
Annotation[] as,
MediaType mediaType,
List models,
PropertiesDelegate propertiesDelegate) {
// Ensure a parameter-less lookup type to prevent excessive memory consumption
// reported in JERSEY-2297
final MediaType lookupType = mediaType == null || mediaType.getParameters().isEmpty() ?
mediaType :
new MediaType(mediaType.getType(), mediaType.getSubtype());
final ModelLookupKey lookupKey = new ModelLookupKey(c, lookupType);
List writers = mbwLookupCache.get(lookupKey);
if (writers == null) {
writers = new ArrayList();
for (MbwModel model : models) {
if (isCompatible(model, c, mediaType)) {
writers.add(model);
}
}
Collections.sort(writers, new WorkerComparator(c, mediaType));
mbwLookupCache.put(lookupKey, writers);
}
if (writers.isEmpty()) {
return null;
}
final TracingLogger tracingLogger = TracingLogger.getInstance(propertiesDelegate);
MessageBodyWriter selected = null;
final Iterator iterator = writers.iterator();
while (iterator.hasNext()) {
final MbwModel model = iterator.next();
if (model.isWriteable(c, t, as, mediaType)) {
selected = (MessageBodyWriter) model.provider;
tracingLogger.log(MsgTraceEvent.MBW_SELECTED, selected);
break;
}
tracingLogger.log(MsgTraceEvent.MBW_NOT_WRITEABLE, model.provider);
}
if (tracingLogger.isLogEnabled(MsgTraceEvent.MBW_SKIPPED)) {
while (iterator.hasNext()) {
final MbwModel model = iterator.next();
tracingLogger.log(MsgTraceEvent.MBW_SKIPPED, model.provider);
}
}
return selected;
}
@SuppressWarnings("unchecked")
private MessageBodyWriter _getMessageBodyWriter(Class c, Type t,
Annotation[] as,
MediaType mediaType, MediaType lookup,
PropertiesDelegate propertiesDelegate) {
List writers = writersCache.get(lookup);
if (writers == null) {
return null;
}
final TracingLogger tracingLogger = TracingLogger.getInstance(propertiesDelegate);
MessageBodyWriter selected = null;
final Iterator iterator = writers.iterator();
while (iterator.hasNext()) {
final MessageBodyWriter p = iterator.next();
if (MbwModel.isWriteable(p, c, t, as, mediaType)) {
selected = (MessageBodyWriter) p;
tracingLogger.log(MsgTraceEvent.MBW_SELECTED, selected);
break;
}
tracingLogger.log(MsgTraceEvent.MBW_NOT_WRITEABLE, p);
}
if (tracingLogger.isLogEnabled(MsgTraceEvent.MBW_SKIPPED)) {
while (iterator.hasNext()) {
final MessageBodyWriter p = iterator.next();
tracingLogger.log(MsgTraceEvent.MBW_SKIPPED, p);
}
}
return selected;
}
private void getCompatibleProvidersMap(MediaType mediaType,
List extends WorkerModel> set,
Map> subSet) {
if (mediaType.isWildcardType()) {
getCompatibleProvidersList(mediaType, set, subSet);
} else if (mediaType.isWildcardSubtype()) {
getCompatibleProvidersList(mediaType, set, subSet);
getCompatibleProvidersList(MediaTypes.GENERAL_MEDIA_TYPE, set, subSet);
} else {
getCompatibleProvidersList(mediaType, set, subSet);
getCompatibleProvidersList(
MediaTypes.getTypeWildCart(mediaType),
set, subSet);
getCompatibleProvidersList(MediaTypes.GENERAL_MEDIA_TYPE, set, subSet);
}
}
private void getCompatibleProvidersList(MediaType mediaType,
List extends WorkerModel> set,
Map> subSet) {
List providers = new ArrayList();
for (WorkerModel model : set) {
if (model.types.contains(mediaType)) {
providers.add(model.provider);
}
}
if (!providers.isEmpty()) {
subSet.put(mediaType, Collections.unmodifiableList(providers));
}
}
@Override
@SuppressWarnings("unchecked")
public List getMessageBodyWriterMediaTypes(Class> c, Type t, Annotation[] as) {
final Set writeableMediaTypes = Sets.newLinkedHashSet();
for (MbwModel model : writers) {
boolean writeableWorker = false;
for (MediaType mt : model.types) {
if (model.isWriteable(c, t, as, mt)) {
writeableMediaTypes.add(mt);
writeableWorker = true;
}
if (!writeableMediaTypes.contains(MediaType.WILDCARD_TYPE)
&& writeableWorker
&& model.types.contains(MediaType.WILDCARD_TYPE)) {
writeableMediaTypes.add(MediaType.WILDCARD_TYPE);
}
}
}
final List mtl = Lists.newArrayList(writeableMediaTypes);
Collections.sort(mtl, MediaTypes.MEDIA_TYPE_COMPARATOR);
return mtl;
}
@Override
@SuppressWarnings("unchecked")
public List getMessageBodyWritersForType(final Class> clazz) {
if (!mbwTypeLookupCache.containsKey(clazz)) {
processMessageBodyWritersForType(clazz);
}
return mbwTypeLookupCache.get(clazz);
}
private void processMessageBodyWritersForType(final Class> clazz) {
final List> suitableWriters = Lists.newArrayList();
if (Response.class.isAssignableFrom(clazz)) {
suitableWriters.addAll(writers);
} else {
for (final WorkerModel workerPair : writers) {
final Class> wrapped = Primitives.wrap(clazz);
if (workerPair.providerClassParam == null
|| workerPair.providerClassParam.isAssignableFrom(wrapped)
|| workerPair.providerClassParam == clazz) {
suitableWriters.add(workerPair);
}
}
}
// Type -> MediaType.
typeToMediaTypeWritersCache.put(clazz, getMessageBodyWorkersMediaTypesByType(suitableWriters));
// Type -> Writer.
Collections.sort(suitableWriters, WORKER_BY_TYPE_COMPARATOR);
final List writers = Lists.newArrayList();
for (final WorkerModel workerPair : suitableWriters) {
writers.add(workerPair.provider);
}
mbwTypeLookupCache.put(clazz, writers);
}
@Override
public List getMessageBodyWriterMediaTypesByType(final Class> type) {
if (!typeToMediaTypeWritersCache.containsKey(type)) {
processMessageBodyWritersForType(type);
}
return typeToMediaTypeWritersCache.get(type);
}
@Override
public List getMessageBodyReaderMediaTypesByType(final Class> type) {
if (!typeToMediaTypeReadersCache.containsKey(type)) {
processMessageBodyReadersForType(type);
}
return typeToMediaTypeReadersCache.get(type);
}
@SuppressWarnings("unchecked")
private List getMessageBodyWorkersMediaTypesByType(final List extends WorkerModel> workerModels) {
final Set mediaTypeSet = Sets.newHashSet();
for (final WorkerModel model : workerModels) {
mediaTypeSet.addAll(model.types);
}
final List mediaTypes = Lists.newArrayList(mediaTypeSet);
Collections.sort(mediaTypes, MediaTypes.MEDIA_TYPE_COMPARATOR);
return mediaTypes;
}
@Override
@SuppressWarnings("unchecked")
public List getMessageBodyReadersForType(final Class> clazz) {
if (!mbrTypeLookupCache.containsKey(clazz)) {
processMessageBodyReadersForType(clazz);
}
return mbrTypeLookupCache.get(clazz);
}
private void processMessageBodyReadersForType(final Class> clazz) {
final List suitableReaders = Lists.newArrayList();
for (MbrModel reader : readers) {
final Class> wrapped = Primitives.wrap(clazz);
if (reader.providerClassParam == null
|| reader.providerClassParam.isAssignableFrom(wrapped)
|| reader.providerClassParam == clazz) {
suitableReaders.add(reader);
}
}
// Type -> MediaType.
typeToMediaTypeReadersCache.put(clazz, getMessageBodyWorkersMediaTypesByType(suitableReaders));
// Type -> Writer.
Collections.sort(suitableReaders, WORKER_BY_TYPE_COMPARATOR);
final List readers = Lists.newArrayList();
for (final MbrModel reader : suitableReaders) {
readers.add(reader.provider);
}
mbrTypeLookupCache.put(clazz, readers);
}
@Override
@SuppressWarnings("unchecked")
public MediaType getMessageBodyWriterMediaType(Class> c, Type t,
Annotation[] as, List acceptableMediaTypes) {
for (MediaType acceptable : acceptableMediaTypes) {
for (MbwModel model : writers) {
for (MediaType mt : model.types) {
if (mt.isCompatible(acceptable)
&& model.isWriteable(c, t, as, acceptable)) {
return MediaTypes.mostSpecific(mt, acceptable);
}
}
}
}
return null;
}
@Override
public Object readFrom(Class> rawType,
Type type,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap httpHeaders,
PropertiesDelegate propertiesDelegate,
InputStream entityStream,
Iterable readerInterceptors,
boolean translateNce) throws WebApplicationException, IOException {
ReaderInterceptorExecutor executor = new ReaderInterceptorExecutor(rawType, type, annotations, mediaType,
httpHeaders, propertiesDelegate, entityStream, this, readerInterceptors, translateNce, serviceLocator);
final TracingLogger tracingLogger = TracingLogger.getInstance(propertiesDelegate);
final long timestamp = tracingLogger.timestamp(MsgTraceEvent.RI_SUMMARY);
try {
Object instance = executor.proceed();
if (!(instance instanceof Closeable) && !(instance instanceof Source)) {
final InputStream stream = executor.getInputStream();
if (stream != null) {
stream.close();
}
}
return instance;
} finally {
tracingLogger.logDuration(MsgTraceEvent.RI_SUMMARY, timestamp, executor.getProcessedCount());
}
}
@Override
public OutputStream writeTo(Object t,
Class> rawType,
Type type,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap httpHeaders,
PropertiesDelegate propertiesDelegate,
OutputStream entityStream,
Iterable writerInterceptors)
throws IOException, WebApplicationException {
WriterInterceptorExecutor executor = new WriterInterceptorExecutor(t, rawType, type, annotations, mediaType,
httpHeaders, propertiesDelegate, entityStream, this, writerInterceptors, serviceLocator);
final TracingLogger tracingLogger = TracingLogger.getInstance(propertiesDelegate);
final long timestamp = tracingLogger.timestamp(MsgTraceEvent.WI_SUMMARY);
try {
executor.proceed();
} finally {
tracingLogger.logDuration(MsgTraceEvent.WI_SUMMARY, timestamp, executor.getProcessedCount());
}
return executor.getOutputStream();
}
}