All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.jersey.message.internal.MessageBodyFactory Maven / Gradle / Ivy

There is a newer version: 2.22.2
Show newest version
/*
 * 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> 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> 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> 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(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy