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

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

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.8
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2013 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 com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import 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 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 final Map, List> mbrTypeLookupCache =
            DataStructures.createConcurrentMap(32, 0.75f, DataStructures.DEFAULT_CONCURENCY_LEVEL);
    private final Map, List> mbwTypeLookupCache =
            DataStructures.createConcurrentMap(32, 0.75f, DataStructures.DEFAULT_CONCURENCY_LEVEL);

    private final Map, List> typeToMediaTypeReadersCache =
            DataStructures.createConcurrentMap(32, 0.75f, DataStructures.DEFAULT_CONCURENCY_LEVEL);
    private final Map, List> typeToMediaTypeWritersCache =
            DataStructures.createConcurrentMap(32, 0.75f, DataStructures.DEFAULT_CONCURENCY_LEVEL);

    private final Map> mbrLookupCache =
            DataStructures.createConcurrentMap(32, 0.75f, DataStructures.DEFAULT_CONCURENCY_LEVEL);
    private final Map> mbwLookupCache =
            DataStructures.createConcurrentMap(32, 0.75f, 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.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 (MediaTypes.typeEqual(mediaType, mt) || MediaTypes.typeEqual(MediaTypes.getTypeWildCart(mediaType), mt) || MediaTypes.typeEqual(MediaTypes.GENERAL_MEDIA_TYPE, mt)) { return true; } } } return false; } @SuppressWarnings("unchecked") private MessageBodyReader _getMessageBodyReader(Class c, Type t, Annotation[] as, MediaType mediaType, List models, PropertiesDelegate propertiesDelegate) { List readers = mbrLookupCache.get(new ModelLookupKey(c, mediaType)); 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(new ModelLookupKey(c, mediaType), 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) { List writers = mbwLookupCache.get(new ModelLookupKey(c, mediaType)); 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(new ModelLookupKey(c, mediaType), 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); 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); 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