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 jersey-all Show documentation
Show all versions of jersey-all Show documentation
jersey-all is a rebundled verison of Jersey as one OSGi bundle.
/*
* 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();
}
}