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

org.apache.wink.common.internal.registry.ProvidersRegistry Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *  
 *   http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *  
 *******************************************************************************/
package org.apache.wink.common.internal.registry;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
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 org.apache.wink.common.RuntimeContext;
import org.apache.wink.common.WinkApplication;
import org.apache.wink.common.internal.application.ApplicationValidator;
import org.apache.wink.common.internal.i18n.Messages;
import org.apache.wink.common.internal.lifecycle.LifecycleManagersRegistry;
import org.apache.wink.common.internal.lifecycle.ObjectFactory;
import org.apache.wink.common.internal.log.Providers;
import org.apache.wink.common.internal.utils.AnnotationUtils;
import org.apache.wink.common.internal.utils.GenericsUtils;
import org.apache.wink.common.internal.utils.MediaTypeUtils;
import org.apache.wink.common.internal.utils.SoftConcurrentMap;
import org.apache.wink.common.utils.ProviderUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Keeps the registry of providers.
 * 

* The order of the providers is important. The later provider was added, the * higher priority it has. Thus, the default providers should be always added * before the custom or with lower priority. */ public class ProvidersRegistry { private static final Logger logger = LoggerFactory .getLogger(ProvidersRegistry.class); private final ProducesMediaTypeMap> contextResolvers = new ProducesMediaTypeMap>( ContextResolver.class); /* * need exception mappers to be volatile for publication purposes */ private volatile TreeSet>> exceptionMappers = new TreeSet>>( Collections .reverseOrder()); private final ConsumesMediaTypeMap> messageBodyReaders = new ConsumesMediaTypeMap>( MessageBodyReader.class); private final ProducesMediaTypeMap> messageBodyWriters = new ProducesMediaTypeMap>( MessageBodyWriter.class); private final ApplicationValidator applicationValidator; private final LifecycleManagersRegistry factoryFactoryRegistry; public ProvidersRegistry(LifecycleManagersRegistry factoryRegistry, ApplicationValidator applicationValidator) { this.factoryFactoryRegistry = factoryRegistry; this.applicationValidator = applicationValidator; } public boolean addProvider(Class cls, double priority) { return addProvider(cls, priority, false); } @SuppressWarnings("unchecked") public boolean addProvider(Class cls, double priority, boolean isSystemProvider) { if (cls == null) { throw new NullPointerException(Messages.getMessage("variableIsNull", "cls")); //$NON-NLS-1$ //$NON-NLS-2$ } ObjectFactory objectFactory = factoryFactoryRegistry.getObjectFactory(cls); return addProvider(new PriorityObjectFactory(objectFactory, priority, isSystemProvider)); } public boolean addProvider(Object provider, double priority) { return addProvider(provider, priority, false); } @SuppressWarnings("unchecked") public boolean addProvider(Object provider, double priority, boolean isSystemProvider) { if (provider == null) { throw new NullPointerException(Messages.getMessage("variableIsNull", "provider")); //$NON-NLS-1$ //$NON-NLS-2$ } ObjectFactory objectFactory = factoryFactoryRegistry.getObjectFactory(provider); return addProvider(new PriorityObjectFactory(objectFactory, priority, isSystemProvider)); } @SuppressWarnings("unchecked") private synchronized boolean addProvider(PriorityObjectFactory objectFactory) { Class cls = objectFactory.getInstanceClass(); logger.trace("Processing provider of type {}", cls); //$NON-NLS-1$ boolean retValue = false; if (!applicationValidator.isValidProvider(cls)) { return retValue; } if (ContextResolver.class.isAssignableFrom(cls)) { contextResolvers.putProvider((PriorityObjectFactory>)objectFactory); retValue = true; } if (ExceptionMapper.class.isAssignableFrom(cls)) { logger.trace("Adding type {} to ExceptionMappers list", cls); //$NON-NLS-1$ TreeSet>> exceptionMappersCopy = new TreeSet>>(Collections.reverseOrder()); exceptionMappersCopy.addAll(exceptionMappers); exceptionMappersCopy.add((PriorityObjectFactory>)objectFactory); exceptionMappers = exceptionMappersCopy; retValue = true; } if (MessageBodyReader.class.isAssignableFrom(cls)) { messageBodyReaders .putProvider((PriorityObjectFactory>)objectFactory); retValue = true; } if (MessageBodyWriter.class.isAssignableFrom(cls)) { messageBodyWriters .putProvider((PriorityObjectFactory>)objectFactory); retValue = true; } if (retValue == false) { if (logger.isWarnEnabled()) { logger.warn(Messages.getMessage("classIsUnknownProvider", cls)); //$NON-NLS-1$ } } return retValue; } public boolean addProvider(Class cls) { return addProvider(cls, WinkApplication.DEFAULT_PRIORITY); } public boolean addProvider(Object provider) { return addProvider(provider, WinkApplication.DEFAULT_PRIORITY); } public List> getMessageBodyWriterRecords() { return new ArrayList>(messageBodyWriters.getProviderRecords()); } public List> getMessageBodyReaderRecords() { return new ArrayList>(messageBodyReaders.getProviderRecords()); } public List> getExceptionMapperRecords() { ArrayList> recordList = new ArrayList>(); for (PriorityObjectFactory> factory : exceptionMappers) { ProviderRecord record = new ProviderRecord>(factory.getInstanceClass(), null, ExceptionMapper.class, factory.isSystemProvider); recordList.add(record); } return recordList; } public List> getContextResolverRecords() { return new ArrayList>(contextResolvers.getProviderRecords()); } /** * Removes all providers in the registry. */ public void removeAllProviders() { contextResolvers.removeAll(); messageBodyReaders.removeAll(); messageBodyWriters.removeAll(); for (ObjectFactory of : exceptionMappers) { of.releaseAll(null); } } @SuppressWarnings("unchecked") public ContextResolver getContextResolver(final Class contextType, MediaType mediaType, RuntimeContext runtimeContext) { if (contextType == null) { throw new NullPointerException(Messages.getMessage("variableIsNull", "contextType")); //$NON-NLS-1$ //$NON-NLS-2$ } logger.trace("Getting ContextResolver for {} which has @Produces compatible with {}", //$NON-NLS-1$ contextType, mediaType); if (mediaType == null) { // see https://issues.apache.org/jira/browse/WINK-153 mediaType = MediaType.WILDCARD_TYPE; } /* * performance improvement */ if (contextResolvers.isMapEmpty()) { logger.trace("ContextResolvers MediaTypeMap was empty so returning null"); //$NON-NLS-1$ return null; } final List>.OFHolder>> factories = contextResolvers.getProvidersByMediaType(mediaType, contextType); if (factories.isEmpty()) { logger .trace("Did not find a ContextResolver for {} which has @Produces compatible with {}", //$NON-NLS-1$ contextType, mediaType); return null; } if (factories.size() == 1) { ObjectFactory> factory = factories.get(0); logger .trace("Found ContextResolver ObjectFactory {} for {} which has @Produces compatible with {}", //$NON-NLS-1$ new Object[] {factory, contextType, mediaType}); return (ContextResolver)factory.getInstance(runtimeContext); } // creates list of providers that is used by the proxy // this solution can be improved by creating providers inside the // proxy // one-by-one and keeping them on the proxy // so a new provider will be created only when all the old providers // will return null final List> providers = new ArrayList>(factories.size()); for (ObjectFactory> factory : factories) { providers.add(factory.getInstance(runtimeContext)); } logger .trace("Found multiple ContextResolver ObjectFactories {} for {} which has @Produces compatible with {} . Using Proxy object which will call all matching ContextResolvers to find correct context.", //$NON-NLS-1$ new Object[] {providers, contextType, mediaType}); final MediaType mt = mediaType; return (ContextResolver)Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {ContextResolver.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName() .equals("getContext") && args != null //$NON-NLS-1$ && args.length == 1 && (args[0] == null || args[0] .getClass() .equals(Class.class))) { for (ContextResolver resolver : providers) { Class arg0 = (Class)args[0]; if (logger .isTraceEnabled()) { logger .trace("Calling {}.getContext({}) to find context for {} with @Produces media type compatible with {}", //$NON-NLS-1$ new Object[] { resolver, arg0, contextType, mt}); } Object context = resolver .getContext(arg0); if (context != null) { if (logger .isTraceEnabled()) { logger .trace("Returning {} from calling {}.getContext({}) to find context for {} with @Produces media type compatible with {}", //$NON-NLS-1$ new Object[] { context, resolver, arg0, contextType, mt}); } return context; } } if (logger.isTraceEnabled()) { logger .trace("Did not find context for {} with @Produces media type compatible with {}", //$NON-NLS-1$ new Object[] { contextType, mt}); } return null; } else { return method.invoke(proxy, args); } } }); } @SuppressWarnings("unchecked") public ExceptionMapper getExceptionMapper(Class type, RuntimeContext runtimeContext) { if (type == null) { throw new NullPointerException(Messages.getMessage("variableIsNull", "type")); //$NON-NLS-1$ //$NON-NLS-2$ } logger.trace("Getting ExceptionMapper for {} ", type); //$NON-NLS-1$ List> matchingMappers = new ArrayList>(); for (ObjectFactory> factory : exceptionMappers) { ExceptionMapper exceptionMapper = factory.getInstance(runtimeContext); Type genericType = GenericsUtils.getGenericInterfaceParamType(exceptionMapper.getClass(), ExceptionMapper.class); Class classType = GenericsUtils.getClassType(genericType, null); if (classType.isAssignableFrom(type)) { matchingMappers.add(exceptionMapper); } } if (matchingMappers.isEmpty()) { logger.trace("Did not find an ExceptionMapper for {} ", type); //$NON-NLS-1$ return null; } logger.trace("Found matching ExceptionMappers {} for type {} ", matchingMappers, type); //$NON-NLS-1$ while (matchingMappers.size() > 1) { Type first = GenericsUtils.getGenericInterfaceParamType(matchingMappers.get(0).getClass(), ExceptionMapper.class); Type second = GenericsUtils.getGenericInterfaceParamType(matchingMappers.get(1).getClass(), ExceptionMapper.class); Class firstClass = GenericsUtils.getClassType(first, null); Class secondClass = GenericsUtils.getClassType(second, null); if (firstClass == secondClass) { // the first one has higher priority, so remove the second // one for the same classes! matchingMappers.remove(1); } else if (firstClass.isAssignableFrom(secondClass)) { matchingMappers.remove(0); } else { matchingMappers.remove(1); } } ExceptionMapper mapper = (ExceptionMapper)matchingMappers.get(0); logger.trace("Found best matching ExceptionMapper {} for type {} ", mapper, type); //$NON-NLS-1$ return mapper; } @SuppressWarnings("unchecked") public MessageBodyReader getMessageBodyReader(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, RuntimeContext runtimeContext) { if (type == null) { throw new NullPointerException(Messages.getMessage("variableIsNull", "type")); //$NON-NLS-1$ //$NON-NLS-2$ } if (mediaType == null) { throw new NullPointerException(Messages.getMessage("variableIsNull", "mediaType")); //$NON-NLS-1$ //$NON-NLS-2$ } if (logger.isTraceEnabled()) { List anns = (annotations == null) ? null : Arrays.asList(annotations); logger .trace("Getting MessageBodyReader for class type {}, genericType {}, annotations {}, and media type {}", //$NON-NLS-1$ new Object[] {type, genericType, anns, mediaType}); } List>.OFHolder>> factories = messageBodyReaders.getProvidersByMediaType(mediaType, type); logger.trace("Found possible MessageBodyReader ObjectFactories {}", factories); //$NON-NLS-1$ Providers providersLogger = new Providers(); MessageBodyReader ret = null; for (MediaTypeMap>.OFHolder> factory : factories) { MessageBodyReader reader = factory.getInstance(runtimeContext); if (isReadable(reader, type, genericType, annotations, mediaType, runtimeContext, factory.isSystemProvider)) { ret = (MessageBodyReader)reader; providersLogger.addMessageBodyReader(reader, true); break; } else { providersLogger.addMessageBodyReader(reader, false); } } providersLogger.log(); return ret; } @SuppressWarnings("unchecked") public MessageBodyWriter getMessageBodyWriter(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, RuntimeContext runtimeContext) { if (type == null) { throw new NullPointerException(Messages.getMessage("variableIsNull", "type")); //$NON-NLS-1$ //$NON-NLS-2$ } if (mediaType == null) { throw new NullPointerException(Messages.getMessage("variableIsNull", "mediaType")); //$NON-NLS-1$ //$NON-NLS-2$ } if (logger.isTraceEnabled()) { List anns = (annotations == null) ? null : Arrays.asList(annotations); logger .trace("Getting MessageBodyWriter for class type {}, genericType {}, annotations {}, and media type {}", //$NON-NLS-1$ new Object[] {type, genericType, anns, mediaType}); } List>.OFHolder>> writersFactories = messageBodyWriters.getProvidersByMediaType(mediaType, type); logger.trace("Found possible MessageBodyWriter ObjectFactories {}", writersFactories); //$NON-NLS-1$ Providers providersLogger = new Providers(); MessageBodyWriter ret = null; for (MediaTypeMap>.OFHolder> factory : writersFactories) { MessageBodyWriter writer = factory.getInstance(runtimeContext); if (isWriteable(writer, type, genericType, annotations, mediaType, runtimeContext, factory.isSystemProvider)) { if (logger.isTraceEnabled()) { List anns = (annotations == null) ? null : Arrays.asList(annotations); logger.trace("{}.isWriteable( {}, {}, {}, {} ) returned true", new Object[] { //$NON-NLS-1$ writer, type, genericType, anns, mediaType}); } ret = (MessageBodyWriter)writer; providersLogger.addMessageBodyWriter(writer, true); break; } else { providersLogger.addMessageBodyWriter(writer, false); } } if (ret == null && logger.isTraceEnabled()) { List anns = (annotations == null) ? null : Arrays.asList(annotations); logger .trace("No MessageBodyWriter returned true for isWriteable( {}, {}, {}, {} )", new Object[] { //$NON-NLS-1$ type, genericType, anns, mediaType}); } providersLogger.log(); return ret; } public Set getMessageBodyReaderMediaTypesLimitByIsReadable(Class type, RuntimeContext runtimeContext) { Set mediaTypes = new HashSet(); logger.trace("Searching MessageBodyReaders media types limited by class type {}", type); //$NON-NLS-1$ List>.OFHolder>> readerFactories = messageBodyReaders.getProvidersByMediaType(MediaType.WILDCARD_TYPE, type); logger.trace("Found all MessageBodyReader ObjectFactories limited by class type {}", //$NON-NLS-1$ readerFactories); Annotation[] ann = new Annotation[0]; for (MediaTypeMap>.OFHolder> factory : readerFactories) { MessageBodyReader reader = factory.getInstance(runtimeContext); Consumes consumes = factory.getInstanceClass().getAnnotation(Consumes.class); String[] values = null; if (consumes != null) { values = AnnotationUtils.parseConsumesProducesValues(consumes.value()); } else { values = new String[] {MediaType.WILDCARD}; } for (String v : values) { MediaType mt = MediaType.valueOf(v); if (isReadable(reader, type, type, ann, mt, runtimeContext, factory.isSystemProvider)) { logger.trace("Adding {} to media type set", mt); //$NON-NLS-1$ mediaTypes.add(mt); } } } logger .trace("Found {} from @Consumes values from all MessageBodyReader ObjectFactories compatible with Java type {}", //$NON-NLS-1$ mediaTypes, type); return mediaTypes; } public MediaType getMessageBodyWriterMediaTypeLimitByIsWritable(Class type, RuntimeContext runtimeContext) { logger.trace("Searching MessageBodyWriters media types limited by class type {}", type); //$NON-NLS-1$ List>.OFHolder>> writerFactories = messageBodyWriters.getProvidersByMediaType(MediaType.WILDCARD_TYPE, type); logger.trace("Found all MessageBodyWriter ObjectFactories limited by class type {}", //$NON-NLS-1$ writerFactories); Annotation[] ann = new Annotation[0]; for (MediaTypeMap>.OFHolder> factory : writerFactories) { MessageBodyWriter writer = factory.getInstance(runtimeContext); Produces produces = factory.getInstanceClass().getAnnotation(Produces.class); String[] values = null; if (produces != null) { values = AnnotationUtils.parseConsumesProducesValues(produces.value()); } else { values = new String[] {MediaType.WILDCARD}; } for (String v : values) { MediaType mt = MediaType.valueOf(v); if (isWriteable(writer, type, type, ann, mt, runtimeContext, factory.isSystemProvider)) { logger.trace("Returning media type {}", mt); //$NON-NLS-1$ return mt; } } } return null; } /** * @param factory * @param type * @param ann * @param reader * @param mt * @param runtimeContext * @return */ private boolean isReadable( MessageBodyReader reader, Class type, Type genericType, Annotation[] ann, MediaType mt, RuntimeContext runtimeContext, boolean isSystemProvider) { if (logger.isTraceEnabled()) { List anns = (ann == null) ? null : Arrays.asList(ann); logger.trace("Calling {}.isReadable( {}, {}, {}, {} )", new Object[] {reader, //$NON-NLS-1$ type, genericType, anns, mt}); } try { return reader.isReadable(type, genericType, ann, mt); } catch (RuntimeException ex) { ProviderUtils.logUserProviderException(ex, reader, ProviderUtils.PROVIDER_EXCEPTION_ORIGINATOR.isReadable, new Object[]{type, genericType, ann, mt}, runtimeContext); throw ex; } } /** * @param factory * @param type * @param genericType * @param ann * @param mt * @param runtimeContext * @return */ private boolean isWriteable( MessageBodyWriter writer, Class type, Type genericType, Annotation[] ann, MediaType mt, RuntimeContext runtimeContext, boolean isSystemProvider) { if (logger.isTraceEnabled()) { List anns = (ann == null) ? null : Arrays.asList(ann); logger.trace("Calling {}.isWritable( {}, {}, {}, {} )", new Object[] {writer, //$NON-NLS-1$ type, genericType, anns, mt}); } try { return writer.isWriteable(type, genericType, ann, mt); } catch (RuntimeException ex) { ProviderUtils.logUserProviderException(ex, writer, ProviderUtils.PROVIDER_EXCEPTION_ORIGINATOR.isWriteable, new Object[]{type, genericType, ann, mt}, runtimeContext); throw ex; } } public Set getMessageBodyWriterMediaTypes(Class type) { if (type == null) { throw new NullPointerException(Messages.getMessage("variableIsNull", "type")); //$NON-NLS-1$ //$NON-NLS-2$ } Set mediaTypes = messageBodyWriters.getProvidersMediaTypes(type); return mediaTypes; } private class ProducesMediaTypeMap extends MediaTypeMap { public ProducesMediaTypeMap(Class rawType) { super(rawType); } public void putProvider(PriorityObjectFactory objectFactory) { Produces produces = objectFactory.getInstanceClass().getAnnotation(Produces.class); if (produces == null) { put(MediaType.WILDCARD_TYPE, objectFactory); } else { String[] values = AnnotationUtils.parseConsumesProducesValues(produces.value()); for (String val : values) { put(MediaType.valueOf(val), objectFactory); } } } } private class ConsumesMediaTypeMap extends MediaTypeMap { public ConsumesMediaTypeMap(Class rawType) { super(rawType); } public void putProvider(PriorityObjectFactory objectFactory) { Consumes consumes = objectFactory.getInstanceClass().getAnnotation(Consumes.class); if (consumes == null) { put(MediaType.WILDCARD_TYPE, objectFactory); } else { String[] values = AnnotationUtils.parseConsumesProducesValues(consumes.value()); for (String val : values) { put(MediaType.valueOf(val), objectFactory); } } } } private abstract class MediaTypeMap { private volatile HashMap>> data = new HashMap>>(); @SuppressWarnings("unchecked") private volatile Entry>>[] entrySet = data .entrySet() .toArray(new Entry[0]); private final Class rawType; private final SoftConcurrentMap, SoftConcurrentMap>>> providersCache = new SoftConcurrentMap, SoftConcurrentMap>>>(); ; public MediaTypeMap(Class rawType) { super(); this.rawType = rawType; } boolean isMapEmpty() { return data.isEmpty(); } @SuppressWarnings("unchecked") synchronized void removeAll() { // order of operations for the next 4 lines matter Entry>>[] oldEntrySet = entrySet; entrySet = data.entrySet().toArray(new Entry[0]); data = new HashMap>>(); providersCache.clear(); for (Entry>> entry : oldEntrySet) { HashSet> set = entry.getValue(); for (PriorityObjectFactory of : set) { of.releaseAll(null); } } } /** * returns providers by mediaType and by type * * @param mediaType * @param cls * @return */ public List> getProvidersByMediaType(MediaType mediaType, Class cls) { String subtype = mediaType.getSubtype(); String type = mediaType.getType(); if (!mediaType.getParameters().isEmpty()) { mediaType = new MediaType(type, subtype); } logger .trace("Getting providers by media type by calling getProvidersByMediaType({}, {})", //$NON-NLS-1$ mediaType, cls); SoftConcurrentMap>> mediaTypeToProvidersCache = providersCache.get(cls); if (mediaTypeToProvidersCache == null) { logger .trace("MediaType to providers cache for class {} does not exist so creating", //$NON-NLS-1$ cls); mediaTypeToProvidersCache = new SoftConcurrentMap>>(); providersCache.put(cls, mediaTypeToProvidersCache); } List> list = mediaTypeToProvidersCache.get(mediaType); logger.trace("Get media type to providers cache for media type {} resulted in {}", //$NON-NLS-1$ mediaType, list); if (list == null) { list = internalGetProvidersByMediaType(mediaType, cls); mediaTypeToProvidersCache.put(mediaType, list); } return list; } public Collection> getProviderRecords() { List> compatible = new ArrayList>(); Entry>>[] registryEntrySet = entrySet; for (Entry>> entry : registryEntrySet) { TreeSet> entries = new TreeSet>(Collections.reverseOrder()); entries.addAll(entry.getValue()); for (PriorityObjectFactory of : entries) { compatible.add(new ProviderRecord(of.getInstanceClass(), entry.getKey(), rawType, of.isSystemProvider)); } } return compatible; } private List> internalGetProvidersByMediaType(MediaType mediaType, Class cls) { Set> compatible = new TreeSet>(Collections.reverseOrder()); for (Entry>> entry : entrySet) { if (areMediaTypesCompatible(entry.getKey(), mediaType)) { // media type is compatible, check generic type of the // subset for (PriorityObjectFactory of : entry.getValue()) { if (GenericsUtils.isGenericInterfaceAssignableFrom(cls, of .getInstanceClass(), rawType)) { // Both media type and generic types are compatible. // The assumption here that more specific media // types are added first so replacing the entity // with the same object factory of the different // media type, won't change the map. // This is done via the equals() of the OFHolder // which doesn't compare the MediaType compatible .add(new OFHolder(entry.getKey(), of, of.isSystemProvider)); } } } } @SuppressWarnings("unchecked") OFHolder[] tmp = compatible.toArray(new OFHolder[compatible.size()]); return Arrays.asList(tmp); } /** * Allow providers to be registered with a structured syntax suffix (e.g. '+xml' * or '+json') to narrow (or broaden) the applicability of a provider. *

* The structured syntax suffix (SSS) is defined in RFC6838, section 4.2.8 and * RFC6839, section 3.
* For providers, the SSS is used in addition to to the mimetype's main * type. For example, a mediatype of "application/vnd.custom+json" should be * considered compatible with a provider registered as either * "application/vnd.custom+json" or "application/json", but not with "text/json" * or "application/vnd.custom+xml". *

* @param providerMediaType the media type of the provider to check for * compatibility, cannot be null; * @param candidateMediaType the media type of check against, cannot be * null. * @return true if the given media types are compatible, * false otherwise. * @see http://tools.ietf.org/html/rfc6838#section-4.2.8 * @see http://tools.ietf.org/html/rfc6839#section-3 */ private boolean areMediaTypesCompatible(MediaType providerMediaType, MediaType candidateMediaType) { if (providerMediaType.isCompatible(candidateMediaType)) { // Easy case: directly compatible media types... return true; } // Are main types equal? If not, we're done really quickly... if (!providerMediaType.getType().equals(candidateMediaType.getType())) { return false; } // If the provider has a SSS in its subtype, it means that it is *specifically* // registered for that particular subtype + SSS, so do not strip it from the // subtype... String subtype1 = providerMediaType.getSubtype(); String subtype2 = candidateMediaType.getSubtype(); int idx = subtype2.indexOf('+'); if (idx >= 0) { // subtype contains a structured syntax suffix... subtype2 = subtype2.substring(idx + 1); } return subtype1.equals(subtype2); } public Set getProvidersMediaTypes(Class type) { Set mediaTypes = new LinkedHashSet(); l1: for (Entry>> entry : entrySet) { MediaType mediaType = entry.getKey(); Set> set = entry.getValue(); for (PriorityObjectFactory t : set) { if (GenericsUtils.isGenericInterfaceAssignableFrom(type, t.getInstanceClass(), rawType)) { mediaTypes.add(mediaType); continue l1; } } } return mediaTypes; } @SuppressWarnings("unchecked") synchronized void put(MediaType key, PriorityObjectFactory objectFactory) { HashMap>> copyOfMap = new HashMap>>(data); if (!key.getParameters().isEmpty()) { key = new MediaType(key.getType(), key.getSubtype()); } HashSet> set = data.get(key); if (set == null) { set = new HashSet>(); } else { set = new HashSet>(set); } copyOfMap.put(key, set); if (!set.add(objectFactory)) { if (logger.isTraceEnabled()) { logger.trace(Messages.getMessage("mediaTypeSetAlreadyContains", objectFactory)); //$NON-NLS-1$ } } else { // need to resort the entry set Entry>>[] newEntrySet = copyOfMap.entrySet().toArray(new Entry[0]); // It's important to sort the media types here to ensure that // provider of the more dominant media type will precede, when // adding to the compatible set. Arrays .sort(newEntrySet, Collections .reverseOrder(new Comparator>>>() { public int compare(Entry>> o1, Entry>> o2) { return MediaTypeUtils.compareTo(o1.getKey(), o2.getKey()); } })); if (logger.isTraceEnabled()) { logger.trace("Added ObjectFactory {} with MediaType {} to MediaTypeMap {}", //$NON-NLS-1$ new Object[] {objectFactory, key, this}); logger.trace("EntrySet is {}", newEntrySet); //$NON-NLS-1$ } entrySet = newEntrySet; data = copyOfMap; // the set of providers has been changed so must clear the cache providersCache.clear(); logger.trace("Cleared the providers cache"); //$NON-NLS-1$ } } @Override public String toString() { return toString(" ", false, true); //$NON-NLS-1$ } /** * @param userOnly only print user-defined entities * @param trace if calling toString as part of debugging, use * trace=false, if as part of trace or any other reason, use * trace=true * @return */ public String toString(boolean userOnly, boolean trace) { return toString(" ", userOnly, trace); } /** * @param indent how far to indent output * @param userOnly only log user-defined entities * @param trace if calling toString as part of debugging, use * trace=false, if as part of trace or any other reason, use * trace=true (debug prints slightly less verbose) * @return */ protected String toString(String indent, boolean userOnly, boolean trace) { StringBuffer sb = new StringBuffer(); sb.append("\nRawType: "); //$NON-NLS-1$ sb.append(String.valueOf(rawType)); sb.append("\nData Map: "); //$NON-NLS-1$ if (data.isEmpty()) { sb.append("{empty}"); //$NON-NLS-1$ } else { StringBuffer sb_map = new StringBuffer(); boolean userItemFound = !userOnly; // The data Map can be huge. Separate entries // to make it more understandable for (MediaType k : data.keySet()) { sb_map.append("MediaType key = "); //$NON-NLS-1$ sb_map.append(k); sb_map.append("\n"); //$NON-NLS-1$ sb_map.append("ObjectFactory Set value = {\n"); //$NON-NLS-1$ // Separate each ObjectFactory entry in the Set // into its own line for (ObjectFactory of : data.get(k)) { // assuming everything in the org.apache.wink.* package // space with "internal" in package name is system, not // user String instanceClassName = of.getInstanceClass().getName(); if ((userOnly && !(instanceClassName .startsWith("org.apache.wink.common.internal.") || instanceClassName.startsWith("org.apache.wink.server.internal."))) || !userOnly) { //$NON-NLS-1$ $NON-NLS-2$ userItemFound = true; sb_map.append(indent); if (trace) { // trace, print full // ObjectFactory.toString() sb_map.append(of); } else { // debug, print slightly less information sb_map.append(of.getInstanceClass()); } sb_map.append("\n"); //$NON-NLS-1$ } } sb_map.append("}\n"); //$NON-NLS-1$ } if ((sb_map.length() > 0) && userItemFound) { sb.append("\n" + sb_map.toString()); //$NON-NLS-1$ } else { sb.append("{empty}"); //$NON-NLS-1$ } } return sb.toString(); } @SuppressWarnings("hiding") class OFHolder implements ObjectFactory, Comparable> { private final PriorityObjectFactory of; private final MediaType mediaType; private final Class genericType; private final boolean isSystemProvider; public OFHolder(MediaType mediaType, PriorityObjectFactory of, boolean isSystemProvider) { super(); this.of = of; this.mediaType = mediaType; this.isSystemProvider = isSystemProvider; genericType = GenericsUtils.getClassType(GenericsUtils.getGenericInterfaceParamType(of .getInstanceClass(), rawType), rawType); } @Override public String toString() { return "OFHolder [" + (genericType != null ? "genericType=" + genericType + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ : "") //$NON-NLS-1$ + (mediaType != null ? "mediaType=" + mediaType + ", " : "") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + (of != null ? "of=" + of : "") //$NON-NLS-1$ //$NON-NLS-2$ + "]"; //$NON-NLS-1$ } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((of == null) ? 0 : of.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } OFHolder other = (OFHolder)obj; if (of == null) { if (other.of != null) { return false; } } else if (of != other.of) { return false; } return true; } public T getInstance(RuntimeContext context) { return of.getInstance(context); } public Class getInstanceClass() { return of.getInstanceClass(); } public void releaseInstance(T instance, RuntimeContext context) { of.releaseInstance(instance, context); } public void releaseAll(RuntimeContext context) { of.releaseAll(context); } public int compareTo(OFHolder o) { // check if this is a system provider // system providers are less than // WinkApplication.SYSTEM_PRIORITY + 0.1 (they start at // WinkApplication.SYSTEM_PRIORITY and // unless there are 10000000000, this shouldn't matter) if (isSystemProvider) { // this is a system provider if (!o.isSystemProvider) { // the other is a user provider so this is > 0.2 return -1; } } else if (o.isSystemProvider) { // the other is a system provider if (!isSystemProvider) { // this is a user provider return 1; } } // first compare by media type int compare = MediaTypeUtils.compareTo(mediaType, o.mediaType); if (compare != 0) { return compare; } // second compare by generic type if (genericType != o.genericType) { if (genericType.isAssignableFrom(o.genericType)) { return -1; } else { return 1; } } // last compare by priority return Double.compare(of.priority, o.of.priority); } } } private static class PriorityObjectFactory implements ObjectFactory, Comparable> { private final ObjectFactory of; private final double priority; final boolean isSystemProvider; private static double counter = 0.00000000001; private static final double inc = 0.00000000001; public PriorityObjectFactory(ObjectFactory of, double priority, boolean isSystemProvider) { super(); this.of = of; this.priority = priority + (counter += inc); this.isSystemProvider = isSystemProvider; } public T getInstance(RuntimeContext context) { return of.getInstance(context); } public Class getInstanceClass() { return of.getInstanceClass(); } public void releaseInstance(T instance, RuntimeContext context) { of.releaseInstance(instance, context); } public void releaseAll(RuntimeContext context) { of.releaseAll(context); } // this compare is used by exception mappers public int compareTo(PriorityObjectFactory o) { return Double.compare(priority, o.priority); } @Override public String toString() { return String .format("Priority: %f, ObjectFactory: %s", priority, of.toString().replace("class ", "")); //$NON-NLS-1$ } } public static class ProviderRecord { private final MediaType mediaType; private final Class genericType; private final boolean isSystemProvider; private final Class providerClass; public ProviderRecord(Class providerClass, MediaType mediaType, Class rawType, boolean isSystemProvider) { super(); this.mediaType = mediaType; this.isSystemProvider = isSystemProvider; this.providerClass = providerClass; Type t = GenericsUtils.getGenericInterfaceParamType(providerClass, rawType); if (t == null) { this.genericType = Object.class; } else { this.genericType = GenericsUtils.getClassType(t, providerClass); } } public MediaType getMediaType() { return mediaType; } public Class getGenericType() { return genericType; } public boolean isSystemProvider() { return isSystemProvider; } public Class getProviderClass() { return providerClass; } } /** * @param userOnly true = log user providers only, false = log all providers */ public String getLogFormattedProvidersList(boolean userOnly) { StringBuffer sb = new StringBuffer(); sb.append(this.messageBodyReaders.toString(userOnly, false)); sb.append(this.messageBodyWriters.toString(userOnly, false)); sb.append(this.contextResolvers.toString(userOnly, false)); if (userOnly) { return Messages.getMessage("followingProvidersUserDefined", sb.toString()); } else { return Messages.getMessage("followingProviders", sb.toString()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy