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: 4.0.0-M1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2012 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.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.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
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 org.glassfish.jersey.internal.PropertiesDelegate;
import org.glassfish.jersey.internal.ServiceProviders;
import org.glassfish.jersey.internal.inject.AbstractModule;
import org.glassfish.jersey.internal.inject.ReferencingFactory;
import org.glassfish.jersey.internal.util.KeyComparator;
import org.glassfish.jersey.internal.util.KeyComparatorHashMap;
import org.glassfish.jersey.internal.util.KeyComparatorLinkedHashMap;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.ReflectionHelper.DeclaringClassInterfacePair;
import org.glassfish.jersey.internal.util.collection.Ref;
import org.glassfish.jersey.message.MessageBodyWorkers;
import org.glassfish.jersey.process.internal.RequestScope;

import org.glassfish.hk2.Factory;
import org.glassfish.hk2.Scope;
import org.glassfish.hk2.TypeLiteral;

import org.jvnet.hk2.annotations.Inject;

/**
 * A factory for managing {@link MessageBodyReader} and {@link MessageBodyWriter}
 * instances.
 * 

* Note: {@link MessageBodyReader} and {@link MessageBodyWriter} implementation * must not inject the instance of this type directly, e.g. {@code @Inject MessageBodyWorkers w;}. * Instead a {@link Factory}-based injection should be used to prevent * cycles in the injection framework caused by the eager initialization of the * providers in the current factory implementation: * {@code @Inject Factory w;} * * * @author Paul Sandoz * @author Marek Potociar (marek.potociar at oracle.com) * @author Jakub Podlesak (jakub.podlesak at oracle.com) */ // FIXME: Remove the note from the javadoc once the issue is fixed. public class MessageBodyFactory implements MessageBodyWorkers { public static class Module extends AbstractModule { private static class InjectionFactory extends ReferencingFactory { public InjectionFactory(@Inject Factory> referenceFactory) { super(referenceFactory); } } // private final Class refScope; public Module(Class refScope) { this.refScope = refScope; } @Override protected void configure() { bind(MessageBodyWorkers.class) .toFactory(InjectionFactory.class) .in(RequestScope.class); bind(new TypeLiteral>() {}) .toFactory(ReferencingFactory.referenceFactory()) .in(refScope); } } // 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(); } @Override public int compare(MediaType o1, MediaType o2) { throw new UnsupportedOperationException("Not supported yet."); } }; private final ServiceProviders serviceProviders; private Map> readerProviders; private Map> writerProviders; private List readerListProviders; private List writerListProviders; private Map> customReaderProviders; private Map> customWriterProviders; private List customReaderListProviders; private List customWriterListProviders; private Set readerInterceptors; private Set writerInterceptors; @Override public Set getReaderInterceptors() { return readerInterceptors; } @Override public Set getWriterInterceptors() { return writerInterceptors; } private static class MessageBodyWriterPair { final MessageBodyWriter mbw; final List types; MessageBodyWriterPair(MessageBodyWriter mbw, List types) { this.mbw = mbw; this.types = types; } } private static class MessageBodyReaderPair { final MessageBodyReader mbr; final List types; MessageBodyReaderPair(MessageBodyReader mbr, List types) { this.mbr = mbr; this.types = types; } } public MessageBodyFactory(ServiceProviders serviceProviders) { this.serviceProviders = serviceProviders; initReaders(); initWriters(); initInterceptors(); } /** * 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; } } private void initInterceptors() { this.readerInterceptors = serviceProviders.getAll(ReaderInterceptor.class); this.writerInterceptors = serviceProviders.getAll(WriterInterceptor.class); } private void initReaders() { this.customReaderProviders = new KeyComparatorHashMap>( MEDIA_TYPE_COMPARATOR); this.customReaderListProviders = new ArrayList(); this.readerProviders = new KeyComparatorHashMap>( MEDIA_TYPE_COMPARATOR); this.readerListProviders = new ArrayList(); initReaders(customReaderProviders, customReaderListProviders, serviceProviders.getCustom(MessageBodyReader.class)); initReaders(readerProviders, readerListProviders, serviceProviders.getDefault(MessageBodyReader.class)); } private void initReaders(Map> mediaToProvidersMap, List listProviders, Set providersSet) { for (MessageBodyReader provider : providersSet) { List values = MediaTypes.createFrom( provider.getClass().getAnnotation(Consumes.class)); for (MediaType type : values) { registerProviderForMediaType(mediaToProvidersMap, provider, type); } listProviders.add(new MessageBodyReaderPair(provider, values)); } final DeclarationDistanceComparator dc = new DeclarationDistanceComparator(MessageBodyReader.class); for (Map.Entry> e : mediaToProvidersMap.entrySet()) { Collections.sort(e.getValue(), dc); } Collections.sort(listProviders, new Comparator() { @Override public int compare(MessageBodyReaderPair p1, MessageBodyReaderPair p2) { return dc.compare(p1.mbr, p2.mbr); } }); } private void initWriters() { this.customWriterProviders = new KeyComparatorHashMap>( MEDIA_TYPE_COMPARATOR); this.customWriterListProviders = new ArrayList(); this.writerProviders = new KeyComparatorHashMap>( MEDIA_TYPE_COMPARATOR); this.writerListProviders = new ArrayList(); initWriters(customWriterProviders, customWriterListProviders, serviceProviders.getCustom(MessageBodyWriter.class)); initWriters(writerProviders, writerListProviders, serviceProviders.getDefault(MessageBodyWriter.class)); } private void initWriters(Map> mediaToProvidersMap, List listProviders, Set providersSet) { for (MessageBodyWriter provider : providersSet) { List values = MediaTypes.createFrom( provider.getClass().getAnnotation(Produces.class)); for (MediaType type : values) { registerProviderForMediaType(mediaToProvidersMap, provider, type); } listProviders.add(new MessageBodyWriterPair(provider, values)); } final DeclarationDistanceComparator dc = new DeclarationDistanceComparator(MessageBodyWriter.class); for (Map.Entry> e : mediaToProvidersMap.entrySet()) { Collections.sort(e.getValue(), dc); } Collections.sort(listProviders, new Comparator() { @Override public int compare(MessageBodyWriterPair p1, MessageBodyWriterPair p2) { return dc.compare(p1.mbw, p2.mbw); } }); } private void registerProviderForMediaType(Map> mediaToProviderMap, T provider, MediaType mediaType) { if (!mediaToProviderMap.containsKey(mediaType)) { mediaToProviderMap.put(mediaType, new ArrayList()); } List providers = mediaToProviderMap.get(mediaType); providers.add(provider); } // MessageBodyWorkers @Override public Map> getReaders(MediaType mediaType) { Map> subSet = new KeyComparatorLinkedHashMap>( MEDIA_TYPE_COMPARATOR); if (!customReaderProviders.isEmpty()) { getCompatibleProvidersMap(mediaType, customReaderProviders, subSet); } getCompatibleProvidersMap(mediaType, readerProviders, subSet); return subSet; } @Override public Map> getWriters(MediaType mediaType) { Map> subSet = new KeyComparatorLinkedHashMap>( MEDIA_TYPE_COMPARATOR); if (!customWriterProviders.isEmpty()) { getCompatibleProvidersMap(mediaType, customWriterProviders, subSet); } getCompatibleProvidersMap(mediaType, writerProviders, 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) { MessageBodyReader reader; if (!customReaderProviders.isEmpty()) { reader = _getMessageBodyReader(c, t, as, mediaType, customReaderProviders); if (reader != null) { return reader; } } reader = _getMessageBodyReader(c, t, as, mediaType, readerProviders); return reader; } @Override public List getMessageBodyReaderMediaTypes(Class type, Type genericType, Annotation[] annotations) { List mtl = new ArrayList(); for (MessageBodyReaderPair mbrp : customReaderListProviders) { for (MediaType mt : mbrp.types) { if (mbrp.mbr.isReadable(type, genericType, annotations, mt)) { mtl.add(mt); } } } for (MessageBodyReaderPair mbrp : readerListProviders) { for (MediaType mt : mbrp.types) { if (mbrp.mbr.isReadable(type, genericType, annotations, mt)) { mtl.addAll(mbrp.types); } } } Collections.sort(mtl, MediaTypes.MEDIA_TYPE_COMPARATOR); return mtl; } private MessageBodyReader _getMessageBodyReader(Class c, Type t, Annotation[] as, MediaType mediaType, Map> providers) { MessageBodyReader p = null; if (mediaType != null) { p = _getMessageBodyReader(c, t, as, mediaType, mediaType, providers); if (p == null) { p = _getMessageBodyReader(c, t, as, mediaType, MediaTypes.getTypeWildCart(mediaType), providers); } } if (p == null) { p = _getMessageBodyReader(c, t, as, mediaType, MediaTypes.GENERAL_MEDIA_TYPE, providers); } return p; } @SuppressWarnings("unchecked") private MessageBodyReader _getMessageBodyReader(Class c, Type t, Annotation[] as, MediaType mediaType, MediaType lookup, Map> providers) { List readers = providers.get(lookup); if (readers == null) { return null; } for (MessageBodyReader p : readers) { if (p.isReadable(c, t, as, mediaType)) { return (MessageBodyReader) p; } } return null; } @Override public MessageBodyWriter getMessageBodyWriter(Class c, Type t, Annotation[] as, MediaType mediaType) { MessageBodyWriter p; if (!customWriterProviders.isEmpty()) { p = _getMessageBodyWriter(c, t, as, mediaType, customWriterProviders); if (p != null) { return p; } } p = _getMessageBodyWriter(c, t, as, mediaType, writerProviders); return p; } private MessageBodyWriter _getMessageBodyWriter(Class c, Type t, Annotation[] as, MediaType mediaType, Map> providers) { MessageBodyWriter p = null; if (mediaType != null) { p = _getMessageBodyWriter(c, t, as, mediaType, mediaType, providers); if (p == null) { p = _getMessageBodyWriter(c, t, as, mediaType, MediaTypes.getTypeWildCart(mediaType), providers); } } if (p == null) { p = _getMessageBodyWriter(c, t, as, mediaType, MediaTypes.GENERAL_MEDIA_TYPE, providers); } return p; } @SuppressWarnings("unchecked") private MessageBodyWriter _getMessageBodyWriter(Class c, Type t, Annotation[] as, MediaType mediaType, MediaType lookup, Map> providers) { List writers = providers.get(lookup); if (writers == null) { return null; } for (MessageBodyWriter p : writers) { if (p.isWriteable(c, t, as, mediaType)) { return (MessageBodyWriter) p; } } return null; } private void getCompatibleProvidersMap(MediaType mediaType, Map> 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, Map> set, Map> subSet) { List readers = set.get(mediaType); if (readers != null) { subSet.put(mediaType, Collections.unmodifiableList(readers)); } } @Override public List getMessageBodyWriterMediaTypes(Class c, Type t, Annotation[] as) { List mtl = new ArrayList(); for (MessageBodyWriterPair mbwp : customWriterListProviders) { for (MediaType mt : mbwp.types) { if (mbwp.mbw.isWriteable(c, t, as, mt)) { mtl.add(mt); } } } for (MessageBodyWriterPair mbwp : writerListProviders) { for (MediaType mt : mbwp.types) { if (mbwp.mbw.isWriteable(c, t, as, mt)) { mtl.addAll(mbwp.types); } } } Collections.sort(mtl, MediaTypes.MEDIA_TYPE_COMPARATOR); return mtl; } @Override public MediaType getMessageBodyWriterMediaType(Class c, Type t, Annotation[] as, List acceptableMediaTypes) { for (MediaType acceptable : acceptableMediaTypes) { for (MessageBodyWriterPair mbwp : customWriterListProviders) { for (MediaType mt : mbwp.types) { if (mt.isCompatible(acceptable) && mbwp.mbw.isWriteable(c, t, as, acceptable)) { return MediaTypes.mostSpecific(mt, acceptable); } } } for (MessageBodyWriterPair mbwp : writerListProviders) { for (MediaType mt : mbwp.types) { if (mt.isCompatible(acceptable) && mbwp.mbw.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, boolean intercept) throws WebApplicationException, IOException { ReaderInterceptorExecutor executor = new ReaderInterceptorExecutor(rawType, type, annotations, mediaType, httpHeaders, propertiesDelegate, entityStream, this, intercept); return executor.proceed(); } @Override public void writeTo(Object t, Class rawType, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, PropertiesDelegate propertiesDelegate, OutputStream entityStream, MessageBodySizeCallback sizeCallback, boolean intercept) throws IOException, WebApplicationException { writeTo(t, rawType, type, annotations, mediaType, httpHeaders, propertiesDelegate, entityStream, sizeCallback, intercept, true); } @Override public void writeTo(Object t, Class rawType, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, PropertiesDelegate propertiesDelegate, OutputStream entityStream, MessageBodySizeCallback sizeCallback, boolean intercept, boolean writeEntity) throws IOException, WebApplicationException { WriterInterceptorExecutor executor = new WriterInterceptorExecutor(t, rawType, type, annotations, mediaType, httpHeaders, propertiesDelegate, entityStream, this, sizeCallback, intercept, writeEntity); executor.proceed(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy