/******************************************************************************* * Copyright (c) 2012-2016 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.everrest.core.impl; import com.google.common.collect.Iterables; import org.everrest.core.ApplicationContext; import org.everrest.core.Filter; import org.everrest.core.FilterDescriptor; import org.everrest.core.ObjectFactory; import org.everrest.core.ObjectModel; import org.everrest.core.PerRequestObjectFactory; import org.everrest.core.RequestFilter; import org.everrest.core.ResponseFilter; import org.everrest.core.SingletonObjectFactory; import org.everrest.core.impl.header.MediaTypeHelper; import org.everrest.core.impl.provider.ByteEntityProvider; import org.everrest.core.impl.provider.DOMSourceEntityProvider; import org.everrest.core.impl.provider.DataSourceEntityProvider; import org.everrest.core.impl.provider.DefaultExceptionMapper; import org.everrest.core.impl.provider.DuplicateProviderException; import org.everrest.core.impl.provider.FileEntityProvider; import org.everrest.core.impl.provider.InputStreamEntityProvider; import org.everrest.core.impl.provider.JAXBContextResolver; import org.everrest.core.impl.provider.JAXBElementEntityProvider; import org.everrest.core.impl.provider.JAXBObjectEntityProvider; import org.everrest.core.impl.provider.JsonEntityProvider; import org.everrest.core.impl.provider.MultipartFormDataEntityProvider; import org.everrest.core.impl.provider.MultivaluedMapEntityProvider; import org.everrest.core.impl.provider.ProviderDescriptorImpl; import org.everrest.core.impl.provider.ReaderEntityProvider; import org.everrest.core.impl.provider.SAXSourceEntityProvider; import org.everrest.core.impl.provider.StreamOutputEntityProvider; import org.everrest.core.impl.provider.StreamSourceEntityProvider; import org.everrest.core.impl.provider.StringEntityProvider; import org.everrest.core.impl.provider.multipart.CollectionMultipartFormDataMessageBodyWriter; import org.everrest.core.impl.provider.multipart.ListMultipartFormDataMessageBodyReader; import org.everrest.core.impl.provider.multipart.MapMultipartFormDataMessageBodyReader; import org.everrest.core.method.MethodInvokerFilter; import org.everrest.core.provider.ProviderDescriptor; import org.everrest.core.uri.UriPattern; import org.everrest.core.util.MediaTypeComparator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.Providers; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicReference; import static com.google.common.collect.Lists.newArrayList; import static java.util.stream.Collectors.toList; import static javax.ws.rs.core.MediaType.WILDCARD_TYPE; /** * Gives access to common predefined provider. Users of EverRest are not expected to use this class or any of its subclasses. * * @author andrew00x * @see Providers * @see Provider * @see MessageBodyReader * @see MessageBodyWriter * @see ContextResolver * @see ExceptionMapper * @see Filter * @see RequestFilter * @see ResponseFilter * @see MethodInvokerFilter */ public class ProviderBinder implements Providers { /** Logger. */ private static final Logger LOG = LoggerFactory.getLogger(ProviderBinder.class); /** Need have possibility to disable replacing default providers. */ private static final RuntimePermission PROVIDERS_PERMISSIONS = new RuntimePermission("providersManagePermission"); /** Providers binder instance. */ private static final AtomicReference<ProviderBinder> INSTANCE = new AtomicReference<>(); /** @return instance of {@link ProviderBinder} */ public static ProviderBinder getInstance() { ProviderBinder providerBinder = INSTANCE.get(); if (providerBinder == null) { ProviderBinder newProviderBinder = new ProviderBinder(); if (INSTANCE.compareAndSet(null, newProviderBinder)) { providerBinder = newProviderBinder; providerBinder.init(); } else { providerBinder = INSTANCE.get(); } } return providerBinder; } /** * Replace default set of providers by new one. This must not be used by regular users of EverRest framework. * * @param providerBinder * instance of ProviderBinder * @throws SecurityException * if caller is not permitted to call this method because to current security policy */ public static void setInstance(ProviderBinder providerBinder) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(PROVIDERS_PERMISSIONS); } INSTANCE.set(providerBinder); } protected final MediaTypeComparator mediaTypeComparator = new MediaTypeComparator(); /** Read message body providers. */ protected final ConcurrentNavigableMap<MediaType, List<ObjectFactory<ProviderDescriptor>>> writeProviders = new ConcurrentSkipListMap<>(mediaTypeComparator); /** Read message body providers. */ protected final ConcurrentNavigableMap<MediaType, List<ObjectFactory<ProviderDescriptor>>> readProviders = new ConcurrentSkipListMap<>(mediaTypeComparator); /** Exception mappers, see {@link ExceptionMapper}. */ protected final ConcurrentMap<Class<? extends Throwable>, ObjectFactory<ProviderDescriptor>> exceptionMappers = new ConcurrentHashMap<>(); /** Context resolvers. */ protected final ConcurrentMap<Class<?>, NavigableMap<MediaType, ObjectFactory<ProviderDescriptor>>> contextResolvers = new ConcurrentHashMap<>(); /** Request filters, see {@link RequestFilter}. */ protected final ConcurrentMap<UriPattern, List<ObjectFactory<FilterDescriptor>>> requestFilters = new ConcurrentHashMap<>(); /** Response filters, see {@link ResponseFilter}. */ protected final ConcurrentMap<UriPattern, List<ObjectFactory<FilterDescriptor>>> responseFilters = new ConcurrentHashMap<>(); /** Method invoking filters. */ protected final ConcurrentMap<UriPattern, List<ObjectFactory<FilterDescriptor>>> invokerFilters = new ConcurrentHashMap<>(); protected ProviderBinder() { } /** * Add per-request ContextResolver. * * @param contextResolverClass * class of implementation ContextResolver */ public void addContextResolver(Class<? extends ContextResolver> contextResolverClass) { try { ProviderDescriptor descriptor = new ProviderDescriptorImpl(contextResolverClass); addContextResolver(new PerRequestObjectFactory<>(descriptor)); } catch (Exception e) { LOG.error(String.format("Failed add ContextResolver %s. %s", contextResolverClass.getName(), e.getMessage()), e); } } /** * Add singleton ContextResolver. * * @param contextResolver * ContextResolver instance */ public void addContextResolver(ContextResolver contextResolver) { try { ProviderDescriptor descriptor = new ProviderDescriptorImpl(contextResolver); addContextResolver(new SingletonObjectFactory<>(descriptor, contextResolver)); } catch (Exception e) { LOG.error(String.format("Failed add ContextResolver %s. %s", contextResolver.getClass().getName(), e.getMessage()), e); } } /** * Add per-request ExceptionMapper. * * @param exceptionMapperClass * class of implementation ExceptionMapper */ @SuppressWarnings({"unchecked"}) public void addExceptionMapper(Class<? extends ExceptionMapper> exceptionMapperClass) { try { addExceptionMapper(new PerRequestObjectFactory(new ProviderDescriptorImpl(exceptionMapperClass))); } catch (Exception e) { LOG.error(String.format("Failed add ExceptionMapper %s. %s", exceptionMapperClass.getName(), e.getMessage()), e); } } /** * Add singleton ExceptionMapper. * * @param exceptionMapper * ExceptionMapper instance */ @SuppressWarnings({"unchecked"}) public void addExceptionMapper(ExceptionMapper exceptionMapper) { try { addExceptionMapper(new SingletonObjectFactory(new ProviderDescriptorImpl(exceptionMapper), exceptionMapper)); } catch (Exception e) { LOG.error(String.format("Failed add ExceptionMapper %s. %s", exceptionMapper.getClass().getName(), e.getMessage()), e); } } /** * Add per-request MessageBodyReader. * * @param messageBodyReaderClass * class of implementation MessageBodyReader */ public void addMessageBodyReader(Class<? extends MessageBodyReader> messageBodyReaderClass) { try { ProviderDescriptor descriptor = new ProviderDescriptorImpl(messageBodyReaderClass); addMessageBodyReader(new PerRequestObjectFactory<>(descriptor)); } catch (Exception e) { LOG.error(String.format("Failed add MessageBodyReader %s. %s", messageBodyReaderClass.getName(), e.getMessage()), e); } } /** * Add singleton MessageBodyReader. * * @param messageBodyReader * MessageBodyReader instance */ @SuppressWarnings({"unchecked"}) public void addMessageBodyReader(MessageBodyReader messageBodyReader) { try { ProviderDescriptor descriptor = new ProviderDescriptorImpl(messageBodyReader); addMessageBodyReader(new SingletonObjectFactory(descriptor, messageBodyReader)); } catch (Exception e) { LOG.error(String.format("Failed add MessageBodyReader %s. %s", messageBodyReader.getClass().getName(), e.getMessage()), e); } } /** * Add per-request MessageBodyWriter. * * @param messageBodyWriter * class of implementation MessageBodyWriter */ public void addMessageBodyWriter(Class<? extends MessageBodyWriter> messageBodyWriter) { try { ProviderDescriptor descriptor = new ProviderDescriptorImpl(messageBodyWriter); addMessageBodyWriter(new PerRequestObjectFactory<>(descriptor)); } catch (Exception e) { LOG.error(String.format("Failed add MessageBodyWriter %s. %s", messageBodyWriter.getName(), e.getMessage()), e); } } /** * Add singleton MessageBodyWriter. * * @param messageBodyWriter * MessageBodyWriter instance */ public void addMessageBodyWriter(MessageBodyWriter messageBodyWriter) { try { ProviderDescriptor descriptor = new ProviderDescriptorImpl(messageBodyWriter); addMessageBodyWriter(new SingletonObjectFactory<>(descriptor, messageBodyWriter)); } catch (Exception e) { LOG.error(String.format("Failed add MessageBodyWriter %s. %s", messageBodyWriter.getClass().getName(), e.getMessage()), e); } } /** * Add per-request MethodInvokerFilter. * * @param methodInvokerFilterClass * class of implementation MethodInvokerFilter */ public void addMethodInvokerFilter(Class<? extends MethodInvokerFilter> methodInvokerFilterClass) { try { FilterDescriptor descriptor = new FilterDescriptorImpl(methodInvokerFilterClass); addMethodInvokerFilter(new PerRequestObjectFactory<>(descriptor)); } catch (Exception e) { LOG.error(String.format("Failed add MethodInvokerFilter %s. %s", methodInvokerFilterClass.getName(), e.getMessage()), e); } } /** * Add singleton MethodInvokerFilter. * * @param methodInvokerFilter * MethodInvokerFilter instance */ public void addMethodInvokerFilter(MethodInvokerFilter methodInvokerFilter) { try { FilterDescriptor descriptor = new FilterDescriptorImpl(methodInvokerFilter); addMethodInvokerFilter(new SingletonObjectFactory<>(descriptor, methodInvokerFilter)); } catch (Exception e) { LOG.error(String.format("Failed add MethodInvokerFilter %s. %s", methodInvokerFilter.getClass().getName(), e.getMessage()), e); } } /** * Add per-request RequestFilter. * * @param requestFilterClass * class of implementation RequestFilter */ public void addRequestFilter(Class<? extends RequestFilter> requestFilterClass) { try { FilterDescriptor descriptor = new FilterDescriptorImpl(requestFilterClass); addRequestFilter(new PerRequestObjectFactory<>(descriptor)); } catch (Exception e) { LOG.error(String.format("Failed add RequestFilter %s. %s", requestFilterClass.getName(), e.getMessage()), e); } } /** * Add singleton RequestFilter. * * @param requestFilter * RequestFilter instance */ public void addRequestFilter(RequestFilter requestFilter) { try { FilterDescriptor descriptor = new FilterDescriptorImpl(requestFilter); addRequestFilter(new SingletonObjectFactory<>(descriptor, requestFilter)); } catch (Exception e) { LOG.error(String.format("Failed add RequestFilter %s. %s", requestFilter.getClass().getName(), e.getMessage()), e); } } /** * Add per-request ResponseFilter. * * @param responseFilterClass * class of implementation ResponseFilter */ public void addResponseFilter(Class<? extends ResponseFilter> responseFilterClass) { try { FilterDescriptor descriptor = new FilterDescriptorImpl(responseFilterClass); addResponseFilter(new PerRequestObjectFactory<>(descriptor)); } catch (Exception e) { LOG.error(String.format("Failed add ResponseFilter %s. %s", responseFilterClass.getName(), e.getMessage()), e); } } /** * Add singleton ResponseFilter. * * @param responseFilter * ResponseFilter instance */ public void addResponseFilter(ResponseFilter responseFilter) { try { FilterDescriptor descriptor = new FilterDescriptorImpl(responseFilter); addResponseFilter(new SingletonObjectFactory<>(descriptor, responseFilter)); } catch (Exception e) { LOG.error(String.format("Failed add ResponseFilter %s. %s", responseFilter.getClass().getName(), e.getMessage()), e); } } /** * Get list of most acceptable writer's media type for specified type. * * @param type * type * @param genericType * generic type * @param annotations * annotations * @return sorted acceptable media type collection */ public List<MediaType> getAcceptableWriterMediaTypes(Class<?> type, Type genericType, Annotation[] annotations) { return doGetAcceptableWriterMediaTypes(type, genericType, annotations); } @Override public <T> ContextResolver<T> getContextResolver(Class<T> contextType, MediaType mediaType) { return doGetContextResolver(contextType, mediaType); } @Override public <T extends Throwable> ExceptionMapper<T> getExceptionMapper(Class<T> errorType) { return doGetExceptionMapper(errorType); } @Override public <T> MessageBodyReader<T> getMessageBodyReader(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return doGetMessageBodyReader(type, genericType, annotations, mediaType); } @Override public <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return doGetMessageBodyWriter(type, genericType, annotations, mediaType); } /** * @param path * request path * @return acceptable method invocation filters */ public List<MethodInvokerFilter> getMethodInvokerFilters(String path) { ApplicationContext context = ApplicationContext.getCurrent(); return doGetMatchedFilters(path, invokerFilters).stream() .map(factory -> (MethodInvokerFilter)factory.getInstance(context)) .collect(toList()); } /** * @param path * request path * @return acceptable request filters */ public List<RequestFilter> getRequestFilters(String path) { ApplicationContext context = ApplicationContext.getCurrent(); return doGetMatchedFilters(path, requestFilters).stream() .map(factory -> (RequestFilter)factory.getInstance(context)) .collect(toList()); } /** * @param path * request path * @return acceptable response filters */ public List<ResponseFilter> getResponseFilters(String path) { ApplicationContext context = ApplicationContext.getCurrent(); return doGetMatchedFilters(path, responseFilters).stream() .map(factory -> (ResponseFilter)factory.getInstance(context)) .collect(toList()); } public void addContextResolver(ObjectFactory<ProviderDescriptor> contextResolverFactory) { for (Type type : contextResolverFactory.getObjectModel().getObjectClass().getGenericInterfaces()) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType)type; if (ContextResolver.class == parameterizedType.getRawType()) { Type[] typeArguments = parameterizedType.getActualTypeArguments(); if (typeArguments.length != 1) { throw new IllegalArgumentException("Unable strong determine actual type argument"); } Class<?> aClass = (Class<?>)typeArguments[0]; NavigableMap<MediaType, ObjectFactory<ProviderDescriptor>> contextResolversForType = contextResolvers.get(aClass); if (contextResolversForType == null) { NavigableMap<MediaType, ObjectFactory<ProviderDescriptor>> newContextResolversForType = new ConcurrentSkipListMap<>(mediaTypeComparator); contextResolversForType = contextResolvers.putIfAbsent(aClass, newContextResolversForType); if (contextResolversForType == null) { contextResolversForType = newContextResolversForType; } } for (MediaType mediaType : contextResolverFactory.getObjectModel().produces()) { if (contextResolversForType.putIfAbsent(mediaType, contextResolverFactory) != null) { throw new DuplicateProviderException( String.format("ContextResolver for %s and media type %s already registered", aClass.getName(), mediaType)); } } } } } } @SuppressWarnings("unchecked") public void addExceptionMapper(ObjectFactory<ProviderDescriptor> exceptionMapperFactory) { for (Type type : exceptionMapperFactory.getObjectModel().getObjectClass().getGenericInterfaces()) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType)type; if (ExceptionMapper.class == parameterizedType.getRawType()) { Type[] typeArguments = parameterizedType.getActualTypeArguments(); if (typeArguments.length != 1) { throw new RuntimeException("Unable strong determine actual type argument"); } Class<? extends Throwable> errorType = (Class<? extends Throwable>)typeArguments[0]; if (exceptionMappers.putIfAbsent(errorType, exceptionMapperFactory) != null) { throw new DuplicateProviderException(String.format("ExceptionMapper for exception %s already registered", errorType)); } } } } } public void addMessageBodyReader(ObjectFactory<ProviderDescriptor> readerFactory) { for (MediaType mediaType : readerFactory.getObjectModel().consumes()) { addProviderFactory(readProviders, mediaType, readerFactory); } } public void addMessageBodyWriter(ObjectFactory<ProviderDescriptor> writerFactory) { for (MediaType mediaType : writerFactory.getObjectModel().produces()) { addProviderFactory(writeProviders, mediaType, writerFactory); } } public void addMethodInvokerFilter(ObjectFactory<FilterDescriptor> filterFactory) { addProviderFactory(invokerFilters, filterFactory.getObjectModel().getUriPattern(), filterFactory); } public void addRequestFilter(ObjectFactory<FilterDescriptor> filterFactory) { addProviderFactory(requestFilters, filterFactory.getObjectModel().getUriPattern(), filterFactory); } public void addResponseFilter(ObjectFactory<FilterDescriptor> filterFactory) { addProviderFactory(responseFilters, filterFactory.getObjectModel().getUriPattern(), filterFactory); } private <K, PF extends ObjectModel> void addProviderFactory(ConcurrentMap<K, List<ObjectFactory<PF>>> providersFactoryMap, K key, ObjectFactory<PF> providerFactory) { List<ObjectFactory<PF>> providersFactoryList = providersFactoryMap.get(key); if (providersFactoryList == null) { List<ObjectFactory<PF>> newList = new CopyOnWriteArrayList<>(); providersFactoryList = providersFactoryMap.putIfAbsent(key, newList); if (providersFactoryList == null) { providersFactoryList = newList; } } providersFactoryList.add(providerFactory); } @SuppressWarnings({"unchecked"}) protected List<MediaType> doGetAcceptableWriterMediaTypes(Class<?> type, Type genericType, Annotation[] annotations) { List<MediaType> result = new ArrayList<>(); Map<Class, MessageBodyWriter> instanceCache = new HashMap<>(); for (Map.Entry<MediaType, List<ObjectFactory<ProviderDescriptor>>> e : writeProviders.entrySet()) { MediaType mediaType = e.getKey(); for (ObjectFactory messageBodyWriterFactory : e.getValue()) { Class messageBodyWriterClass = messageBodyWriterFactory.getObjectModel().getObjectClass(); MessageBodyWriter messageBodyWriter = instanceCache.get(messageBodyWriterClass); if (messageBodyWriter == null) { messageBodyWriter = (MessageBodyWriter)messageBodyWriterFactory.getInstance(ApplicationContext.getCurrent()); instanceCache.put(messageBodyWriterClass, messageBodyWriter); } if (messageBodyWriter.isWriteable(type, genericType, annotations, WILDCARD_TYPE)) { result.add(mediaType); } } } if (result.size() > 1) { Collections.sort(result, mediaTypeComparator); } return result; } protected <T> ContextResolver<T> doGetContextResolver(Class<T> contextType, MediaType mediaType) { NavigableMap<MediaType, ObjectFactory<ProviderDescriptor>> mediaTypeToContextResolverMap = contextResolvers.get(contextType); ContextResolver<T> contextResolver = null; if (mediaTypeToContextResolverMap != null) { Iterator<MediaType> mediaTypeRange = MediaTypeHelper.createDescendingMediaTypeIterator(mediaType); while (mediaTypeRange.hasNext() && contextResolver == null) { MediaType actual = mediaTypeRange.next(); contextResolver = doGetContextResolver(mediaTypeToContextResolverMap, actual); } } return contextResolver; } /** * @param mediaTypeToContextResolverMap * map that contains ProviderFactories that may produce objects that are instance of T * @param mediaType * media type that can be used to restrict context resolver choose * @return ContextResolver or null if nothing was found */ @SuppressWarnings("unchecked") private <T> ContextResolver<T> doGetContextResolver(NavigableMap<MediaType, ObjectFactory<ProviderDescriptor>> mediaTypeToContextResolverMap, MediaType mediaType) { for (Map.Entry<MediaType, ObjectFactory<ProviderDescriptor>> e : mediaTypeToContextResolverMap.entrySet()) { if (mediaType.isCompatible(e.getKey())) { return (ContextResolver<T>)e.getValue().getInstance(ApplicationContext.getCurrent()); } } return null; } @SuppressWarnings("unchecked") protected <T extends Throwable> ExceptionMapper<T> doGetExceptionMapper(Class<T> errorType) { ObjectFactory objectFactory = exceptionMappers.get(errorType); if (objectFactory == null) { Class superclassOfErrorType = errorType.getSuperclass(); while (objectFactory == null && superclassOfErrorType != Object.class) { objectFactory = exceptionMappers.get(superclassOfErrorType); superclassOfErrorType = superclassOfErrorType.getSuperclass(); } } if (objectFactory == null) { return null; } return (ExceptionMapper<T>)objectFactory.getInstance(ApplicationContext.getCurrent()); } /** * Looking for message body reader according to supplied entity class, entity generic type, annotations and content * type. * * @param <T> * message body reader actual type argument * @param type * entity type * @param genericType * entity generic type * @param annotations * annotations * @param mediaType * entity content type * @return message body reader or null if no one was found. */ @SuppressWarnings({"unchecked"}) protected <T> MessageBodyReader<T> doGetMessageBodyReader(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType) { Iterator<MediaType> mediaTypeRange = MediaTypeHelper.createDescendingMediaTypeIterator(mediaType); Map<Class, MessageBodyReader> instanceCache = new HashMap<>(); List<MessageBodyReader> matchedReaders = newArrayList(); while (mediaTypeRange.hasNext()) { MediaType actual = mediaTypeRange.next(); List<ObjectFactory<ProviderDescriptor>> messageBodyReaderFactories = readProviders.get(actual); if (messageBodyReaderFactories != null) { for (ObjectFactory messageBodyReaderFactory : messageBodyReaderFactories) { Class<?> messageBodyReaderClass = messageBodyReaderFactory.getObjectModel().getObjectClass(); MessageBodyReader messageBodyReader = instanceCache.get(messageBodyReaderClass); if (messageBodyReader == null) { messageBodyReader = (MessageBodyReader)messageBodyReaderFactory.getInstance(ApplicationContext.getCurrent()); instanceCache.put(messageBodyReaderClass, messageBodyReader); } if (messageBodyReader.isReadable(type, genericType, annotations, actual)) { matchedReaders.add(messageBodyReader); } } } } if (matchedReaders.isEmpty()) { return null; } if (matchedReaders.size() > 1) { Collections.sort(matchedReaders, (readerOne, readerTwo) -> { Type typeOne = getTypeSupportedByReader(readerOne); Type typeTwo = getTypeSupportedByReader(readerTwo); if (!(typeOne instanceof Class) || !(typeTwo instanceof Class)) { return 0; } int inheritanceDepthOne = calculateInheritanceDepth((Class<?>)typeOne, type); int inheritanceDepthTwo = calculateInheritanceDepth((Class<?>)typeTwo, type); if (inheritanceDepthOne < 0 && inheritanceDepthTwo >= 0) { return 1; } else if (inheritanceDepthOne >= 0 && inheritanceDepthTwo < 0) { return -1; } else if (inheritanceDepthOne > inheritanceDepthTwo) { return 1; } else if (inheritanceDepthOne < inheritanceDepthTwo) { return -1; } return 0; }); } return matchedReaders.get(0); } private static Type getTypeSupportedByReader(MessageBodyReader<?> reader) { Class readerSuperClass = reader.getClass(); while (readerSuperClass != null) { for (Type anInterface : readerSuperClass.getGenericInterfaces()) { if (anInterface instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType)anInterface; if (parameterizedType.getRawType() == MessageBodyReader.class) { return parameterizedType.getActualTypeArguments()[0]; } } } readerSuperClass = readerSuperClass.getSuperclass(); } return null; } /** * Looking for message body writer according to supplied entity class, entity generic type, annotations and content * type. * * @param <T> * message body writer actual type argument * @param type * entity type * @param genericType * entity generic type * @param annotations * annotations * @param mediaType * content type in which entity should be represented * @return message body writer or null if no one was found. */ @SuppressWarnings({"unchecked"}) protected <T> MessageBodyWriter<T> doGetMessageBodyWriter(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType) { Iterator<MediaType> mediaTypeRange = MediaTypeHelper.createDescendingMediaTypeIterator(mediaType); Map<Class, MessageBodyWriter> instanceCache = new HashMap<>(); List<MessageBodyWriter> matchedWriters = newArrayList(); while (mediaTypeRange.hasNext()) { MediaType actual = mediaTypeRange.next(); List<ObjectFactory<ProviderDescriptor>> messageBodyWriterFactories = writeProviders.get(actual); if (messageBodyWriterFactories != null) { for (ObjectFactory messageBodyWriterFactory : messageBodyWriterFactories) { Class<?> messageBodyWriterClass = messageBodyWriterFactory.getObjectModel().getObjectClass(); MessageBodyWriter writer = instanceCache.get(messageBodyWriterClass); if (writer == null) { writer = (MessageBodyWriter)messageBodyWriterFactory.getInstance(ApplicationContext.getCurrent()); instanceCache.put(messageBodyWriterClass, writer); } if (writer.isWriteable(type, genericType, annotations, actual)) { matchedWriters.add(writer); } } } } if (matchedWriters.isEmpty()) { return null; } if (matchedWriters.size() > 1) { Collections.sort(matchedWriters, (writerOne, writerTwo) -> { Type typeOne = getTypeSupportedByWriter(writerOne); Type typeTwo = getTypeSupportedByWriter(writerTwo); if (!(typeOne instanceof Class) || !(typeTwo instanceof Class)) { return 0; } int inheritanceDepthOne = calculateInheritanceDepth((Class<?>)typeOne, type); int inheritanceDepthTwo = calculateInheritanceDepth((Class<?>)typeTwo, type); if (inheritanceDepthOne < 0 && inheritanceDepthTwo >= 0) { return 1; } else if (inheritanceDepthOne >= 0 && inheritanceDepthTwo < 0) { return -1; } else if (inheritanceDepthOne > inheritanceDepthTwo) { return 1; } else if (inheritanceDepthOne < inheritanceDepthTwo) { return -1; } return 0; }); } return matchedWriters.get(0); } private static Type getTypeSupportedByWriter(MessageBodyWriter writer) { Class writerSuperClass = writer.getClass(); while (writerSuperClass != null) { for (Type anInterface : writerSuperClass.getGenericInterfaces()) { if (anInterface instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType)anInterface; if (parameterizedType.getRawType() == MessageBodyWriter.class) { return parameterizedType.getActualTypeArguments()[0]; } } } writerSuperClass = writerSuperClass.getSuperclass(); } return null; } private static int calculateInheritanceDepth(Class<?> inherited, Class<?> inheritor) { if (!inherited.isAssignableFrom(inheritor)) { return -1; } Class superClass = inheritor; int depth = 0; while (superClass != null && superClass != inherited) { superClass = superClass.getSuperclass(); depth++; } return depth; } /** * @param path * request path * @param filtersMap * filter map * @return acceptable filter * @see #getMethodInvokerFilters(String) * @see #getRequestFilters(String) * @see #getResponseFilters(String) */ protected List<ObjectFactory<FilterDescriptor>> doGetMatchedFilters(String path, Map<UriPattern, List<ObjectFactory<FilterDescriptor>>> filtersMap) { if (path == null) { path = FilterDescriptorImpl.DEFAULT_PATH; } List<ObjectFactory<FilterDescriptor>> result = new ArrayList<>(); List<String> capturingValues = new ArrayList<>(); for (Map.Entry<UriPattern, List<ObjectFactory<FilterDescriptor>>> e : filtersMap.entrySet()) { UriPattern uriPattern = e.getKey(); if (uriPattern != null) { if (uriPattern.match(path, capturingValues)) { String last = Iterables.getLast(capturingValues); if (last != null && !"/".equals(last)) { continue; } } else { continue; } } result.addAll(e.getValue()); } return result; } /** Add prepared providers. */ protected void init() { ByteEntityProvider byteArrayEntityProvider = new ByteEntityProvider(); addMessageBodyReader(byteArrayEntityProvider); addMessageBodyWriter(byteArrayEntityProvider); DataSourceEntityProvider dataSourceEntityProvider = new DataSourceEntityProvider(); addMessageBodyReader(dataSourceEntityProvider); addMessageBodyWriter(dataSourceEntityProvider); DOMSourceEntityProvider domSourceEntityProvider = new DOMSourceEntityProvider(); addMessageBodyReader(domSourceEntityProvider); addMessageBodyWriter(domSourceEntityProvider); FileEntityProvider fileEntityProvider = new FileEntityProvider(); addMessageBodyReader(fileEntityProvider); addMessageBodyWriter(fileEntityProvider); addMessageBodyReader(MultivaluedMapEntityProvider.class); addMessageBodyWriter(MultivaluedMapEntityProvider.class); InputStreamEntityProvider inputStreamEntityProvider = new InputStreamEntityProvider(); addMessageBodyReader(inputStreamEntityProvider); addMessageBodyWriter(inputStreamEntityProvider); ReaderEntityProvider readerEntityProvider = new ReaderEntityProvider(); addMessageBodyReader(readerEntityProvider); addMessageBodyWriter(readerEntityProvider); SAXSourceEntityProvider saxSourceEntityProvider = new SAXSourceEntityProvider(); addMessageBodyReader(saxSourceEntityProvider); addMessageBodyWriter(saxSourceEntityProvider); StreamSourceEntityProvider streamSourceEntityProvider = new StreamSourceEntityProvider(); addMessageBodyReader(streamSourceEntityProvider); addMessageBodyWriter(streamSourceEntityProvider); StringEntityProvider stringEntityProvider = new StringEntityProvider(); addMessageBodyReader(stringEntityProvider); addMessageBodyWriter(stringEntityProvider); StreamOutputEntityProvider streamOutputEntityProvider = new StreamOutputEntityProvider(); addMessageBodyReader(streamOutputEntityProvider); addMessageBodyWriter(streamOutputEntityProvider); JsonEntityProvider<Object> jsonEntityProvider = new JsonEntityProvider<>(); addMessageBodyReader(jsonEntityProvider); addMessageBodyWriter(jsonEntityProvider); addMessageBodyReader(JAXBElementEntityProvider.class); addMessageBodyWriter(JAXBElementEntityProvider.class); addMessageBodyReader(JAXBObjectEntityProvider.class); addMessageBodyWriter(JAXBObjectEntityProvider.class); addMessageBodyReader(MultipartFormDataEntityProvider.class); addMessageBodyReader(ListMultipartFormDataMessageBodyReader.class); addMessageBodyReader(MapMultipartFormDataMessageBodyReader.class); addMessageBodyWriter(CollectionMultipartFormDataMessageBodyWriter.class); addContextResolver(new JAXBContextResolver()); addExceptionMapper(new DefaultExceptionMapper()); } }