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-common Show documentation
Show all versions of jersey-common Show documentation
Jersey core common packages
/*
* 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();
}
}