You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wink.apache.org by el...@apache.org on 2009/07/12 09:21:39 UTC

svn commit: r793282 - in /incubator/wink/trunk/wink-common/src: main/java/org/apache/wink/common/internal/registry/ProvidersRegistry.java test/java/org/apache/wink/common/internal/providers/ProvidersContextResolverTest.java

Author: elman
Date: Sun Jul 12 07:21:39 2009
New Revision: 793282

URL: http://svn.apache.org/viewvc?rev=793282&view=rev
Log:
Added support for searching providers using Wildcards. See [WINK-47]

Modified:
    incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/ProvidersRegistry.java
    incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/providers/ProvidersContextResolverTest.java

Modified: incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/ProvidersRegistry.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/ProvidersRegistry.java?rev=793282&r1=793281&r2=793282&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/ProvidersRegistry.java (original)
+++ incubator/wink/trunk/wink-common/src/main/java/org/apache/wink/common/internal/registry/ProvidersRegistry.java Sun Jul 12 07:21:39 2009
@@ -26,11 +26,13 @@
 import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.Map.Entry;
 import java.util.concurrent.locks.Lock;
@@ -63,23 +65,30 @@
  */
 public class ProvidersRegistry {
 
-    private static Logger                                    logger             = LoggerFactory.getLogger(ProvidersRegistry.class);
-
-    private final ProducesMediaTypeMap<ContextResolver<?>>   contextResolvers   = new ProducesMediaTypeMap<ContextResolver<?>>(
-                                                                                    ContextResolver.class);
-    private final Set<ObjectFactory<ExceptionMapper<?>>>     exceptionMappers   = new TreeSet<ObjectFactory<ExceptionMapper<?>>>(
-                                                                                    Collections.reverseOrder());
-    private final ConsumesMediaTypeMap<MessageBodyReader<?>> messageBodyReaders = new ConsumesMediaTypeMap<MessageBodyReader<?>>(
-                                                                                    MessageBodyReader.class);
-    private final ProducesMediaTypeMap<MessageBodyWriter<?>> messageBodyWriters = new ProducesMediaTypeMap<MessageBodyWriter<?>>(
-                                                                                    MessageBodyWriter.class);
+    private static Logger                                    logger             =
+                                                                                    LoggerFactory
+                                                                                        .getLogger(ProvidersRegistry.class);
+
+    private final ProducesMediaTypeMap<ContextResolver<?>>   contextResolvers   =
+                                                                                    new ProducesMediaTypeMap<ContextResolver<?>>(
+                                                                                                                                 ContextResolver.class);
+    private final Set<ObjectFactory<ExceptionMapper<?>>>     exceptionMappers   =
+                                                                                    new TreeSet<ObjectFactory<ExceptionMapper<?>>>(
+                                                                                                                                   Collections
+                                                                                                                                       .reverseOrder());
+    private final ConsumesMediaTypeMap<MessageBodyReader<?>> messageBodyReaders =
+                                                                                    new ConsumesMediaTypeMap<MessageBodyReader<?>>(
+                                                                                                                                   MessageBodyReader.class);
+    private final ProducesMediaTypeMap<MessageBodyWriter<?>> messageBodyWriters =
+                                                                                    new ProducesMediaTypeMap<MessageBodyWriter<?>>(
+                                                                                                                                   MessageBodyWriter.class);
     private final ApplicationValidator                       applicationValidator;
-    private final LifecycleManagersRegistry                          factoryFactoryRegistry;
+    private final LifecycleManagersRegistry                  factoryFactoryRegistry;
     private final Lock                                       readersLock;
     private final Lock                                       writersLock;
 
     public ProvidersRegistry(LifecycleManagersRegistry factoryRegistry,
-        ApplicationValidator applicationValidator) {
+                             ApplicationValidator applicationValidator) {
         this.factoryFactoryRegistry = factoryRegistry;
         this.applicationValidator = applicationValidator;
         ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
@@ -128,19 +137,19 @@
         }
 
         if (ContextResolver.class.isAssignableFrom(cls)) {
-            contextResolvers.putProvider((ObjectFactory<ContextResolver<?>>) objectFactory);
+            contextResolvers.putProvider((ObjectFactory<ContextResolver<?>>)objectFactory);
             retValue = true;
         }
         if (ExceptionMapper.class.isAssignableFrom(cls)) {
-            exceptionMappers.add((ObjectFactory<ExceptionMapper<?>>) objectFactory);
+            exceptionMappers.add((ObjectFactory<ExceptionMapper<?>>)objectFactory);
             retValue = true;
         }
         if (MessageBodyReader.class.isAssignableFrom(cls)) {
-            messageBodyReaders.putProvider((ObjectFactory<MessageBodyReader<?>>) objectFactory);
+            messageBodyReaders.putProvider((ObjectFactory<MessageBodyReader<?>>)objectFactory);
             retValue = true;
         }
         if (MessageBodyWriter.class.isAssignableFrom(cls)) {
-            messageBodyWriters.putProvider((ObjectFactory<MessageBodyWriter<?>>) objectFactory);
+            messageBodyWriters.putProvider((ObjectFactory<MessageBodyWriter<?>>)objectFactory);
             retValue = true;
         }
         if (retValue == false) {
@@ -160,7 +169,8 @@
 
     @SuppressWarnings("unchecked")
     public <T> ContextResolver<T> getContextResolver(final Class<T> contextType,
-        MediaType mediaType, RuntimeContext runtimeContext) {
+                                                     MediaType mediaType,
+                                                     RuntimeContext runtimeContext) {
         if (contextType == null) {
             throw new NullPointerException("contextType");
         }
@@ -169,48 +179,58 @@
         }
         readersLock.lock();
         try {
-            final List<ObjectFactory<ContextResolver<?>>> factories = contextResolvers.getProvidersByMediaType(
-                mediaType, contextType);
+            final List<ObjectFactory<ContextResolver<?>>> factories =
+                contextResolvers.getProvidersByMediaType(mediaType, contextType);
 
             if (factories.isEmpty()) {
                 return null;
             }
 
             if (factories.size() == 1) {
-                return (ContextResolver<T>) factories.get(0).getInstance(runtimeContext);
+                return (ContextResolver<T>)factories.get(0).getInstance(runtimeContext);
             }
 
             // creates list of providers that is used by the proxy
-            // this solution can be improved by creating providers inside 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<ContextResolver<?>> providers = new ArrayList<ContextResolver<?>>(
-                factories.size());
+            final List<ContextResolver<?>> providers =
+                new ArrayList<ContextResolver<?>>(factories.size());
             for (ObjectFactory<ContextResolver<?>> factory : factories) {
                 providers.add(factory.getInstance(runtimeContext));
             }
 
-            return (ContextResolver<T>) 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
-                            && args.length == 1
-                            && (args[0] == null || args[0].getClass().equals(Class.class))) {
-                            for (ContextResolver<?> resolver : providers) {
-                                Object context = resolver.getContext((Class<?>) args[0]);
-                                if (context != null) {
-                                    return context;
-                                }
-                            }
-                            return null;
-                        } else {
-                            return method.invoke(proxy, args);
-                        }
-                    }
-                });
+            return (ContextResolver<T>)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
+                                                                          && args.length == 1
+                                                                          && (args[0] == null || args[0]
+                                                                              .getClass()
+                                                                              .equals(Class.class))) {
+                                                                          for (ContextResolver<?> resolver : providers) {
+                                                                              Object context =
+                                                                                  resolver
+                                                                                      .getContext((Class<?>)args[0]);
+                                                                              if (context != null) {
+                                                                                  return context;
+                                                                              }
+                                                                          }
+                                                                          return null;
+                                                                      } else {
+                                                                          return method
+                                                                              .invoke(proxy, args);
+                                                                      }
+                                                                  }
+                                                              });
         } finally {
             readersLock.unlock();
         }
@@ -218,7 +238,7 @@
 
     @SuppressWarnings("unchecked")
     public <T extends Throwable> ExceptionMapper<T> getExceptionMapper(Class<T> type,
-        RuntimeContext runtimeContext) {
+                                                                       RuntimeContext runtimeContext) {
         if (type == null) {
             throw new NullPointerException("type");
         }
@@ -228,8 +248,9 @@
 
             for (ObjectFactory<ExceptionMapper<?>> factory : exceptionMappers) {
                 ExceptionMapper<?> exceptionMapper = factory.getInstance(runtimeContext);
-                Type genericType = GenericsUtils.getGenericInterfaceParamType(
-                    exceptionMapper.getClass(), ExceptionMapper.class);
+                Type genericType =
+                    GenericsUtils.getGenericInterfaceParamType(exceptionMapper.getClass(),
+                                                               ExceptionMapper.class);
                 Class<?> classType = GenericsUtils.getClassType(genericType);
                 if (classType.isAssignableFrom(type)) {
                     matchingMappers.add(exceptionMapper);
@@ -241,14 +262,17 @@
             }
 
             while (matchingMappers.size() > 1) {
-                Type first = GenericsUtils.getGenericInterfaceParamType(
-                    matchingMappers.get(0).getClass(), ExceptionMapper.class);
-                Type second = GenericsUtils.getGenericInterfaceParamType(
-                    matchingMappers.get(1).getClass(), ExceptionMapper.class);
+                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);
                 Class<?> secondClass = GenericsUtils.getClassType(second);
                 if (firstClass == secondClass) {
-                    // the first one has higher priority, so remove the second one for the same classes!
+                    // 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);
@@ -257,15 +281,18 @@
                 }
             }
 
-            return (ExceptionMapper<T>) matchingMappers.get(0);
+            return (ExceptionMapper<T>)matchingMappers.get(0);
         } finally {
             readersLock.unlock();
         }
     }
 
     @SuppressWarnings("unchecked")
-    public <T> MessageBodyReader<T> getMessageBodyReader(Class<T> type, Type genericType,
-        Annotation[] annotations, MediaType mediaType, RuntimeContext runtimeContext) {
+    public <T> MessageBodyReader<T> getMessageBodyReader(Class<T> type,
+                                                         Type genericType,
+                                                         Annotation[] annotations,
+                                                         MediaType mediaType,
+                                                         RuntimeContext runtimeContext) {
         if (type == null) {
             throw new NullPointerException("type");
         }
@@ -274,12 +301,12 @@
         }
         readersLock.lock();
         try {
-            List<ObjectFactory<MessageBodyReader<?>>> factories = messageBodyReaders.getProvidersByMediaType(
-                mediaType, type);
+            List<ObjectFactory<MessageBodyReader<?>>> factories =
+                messageBodyReaders.getProvidersByMediaType(mediaType, type);
             for (ObjectFactory<MessageBodyReader<?>> factory : factories) {
                 MessageBodyReader<?> reader = factory.getInstance(runtimeContext);
                 if (reader.isReadable(type, genericType, annotations, mediaType)) {
-                    return (MessageBodyReader<T>) reader;
+                    return (MessageBodyReader<T>)reader;
                 }
             }
             return null;
@@ -289,8 +316,11 @@
     }
 
     @SuppressWarnings("unchecked")
-    public <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> type, Type genericType,
-        Annotation[] annotations, MediaType mediaType, RuntimeContext runtimeContext) {
+    public <T> MessageBodyWriter<T> getMessageBodyWriter(Class<T> type,
+                                                         Type genericType,
+                                                         Annotation[] annotations,
+                                                         MediaType mediaType,
+                                                         RuntimeContext runtimeContext) {
         if (type == null) {
             throw new NullPointerException("type");
         }
@@ -299,12 +329,12 @@
         }
         readersLock.lock();
         try {
-            List<ObjectFactory<MessageBodyWriter<?>>> writersFactories = messageBodyWriters.getProvidersByMediaType(
-                mediaType, type);
+            List<ObjectFactory<MessageBodyWriter<?>>> writersFactories =
+                messageBodyWriters.getProvidersByMediaType(mediaType, type);
             for (ObjectFactory<MessageBodyWriter<?>> factory : writersFactories) {
                 MessageBodyWriter<?> writer = factory.getInstance(runtimeContext);
                 if (writer.isWriteable(type, genericType, annotations, mediaType)) {
-                    return (MessageBodyWriter<T>) writer;
+                    return (MessageBodyWriter<T>)writer;
                 }
             }
             return null;
@@ -361,7 +391,8 @@
 
     private abstract class MediaTypeMap<T> {
 
-        private final Map<MediaType, Set<ObjectFactory<T>>> data = new LinkedHashMap<MediaType, Set<ObjectFactory<T>>>();
+        private final Map<MediaType, Set<ObjectFactory<T>>> data =
+                                                                     new LinkedHashMap<MediaType, Set<ObjectFactory<T>>>();
         private final Class<?>                              rawType;
 
         public MediaTypeMap(Class<?> rawType) {
@@ -370,27 +401,80 @@
         }
 
         /**
-         * returns providers by mediaType and by
+         * returns providers by mediaType and by type
          * 
          * @param mediaType
-         * @param type
+         * @param cls
          * @return
          */
-        public List<ObjectFactory<T>> getProvidersByMediaType(MediaType mediaType, Class<?> type) {
-            if (!mediaType.getParameters().isEmpty()) {
-                mediaType = new MediaType(mediaType.getType(), mediaType.getSubtype());
+        public List<ObjectFactory<T>> getProvidersByMediaType(MediaType mediaType, Class<?> cls) {
+
+            String subtype = mediaType.getSubtype();
+            String type = mediaType.getType();
+            if (subtype.equals(MediaType.MEDIA_TYPE_WILDCARD) || type
+                .equals(MediaType.MEDIA_TYPE_WILDCARD)) {
+                return getProvidersByWildcardMediaType(mediaType, cls);
             }
             List<ObjectFactory<T>> list = new ArrayList<ObjectFactory<T>>();
+            if (!mediaType.getParameters().isEmpty()) {
+                mediaType = new MediaType(type, subtype);
+            }
             Set<ObjectFactory<T>> set = data.get(mediaType);
-            limitByType(list, set, type);
-            if (!mediaType.getSubtype().equals("*")) {
-                set = data.get(new MediaType(mediaType.getType(), "*"));
-                limitByType(list, set, type);
-                if (!mediaType.getType().equals("*")) {
-                    set = data.get(MediaType.WILDCARD_TYPE);
-                    limitByType(list, set, type);
+            limitByType(list, set, cls);
+            set = data.get(new MediaType(type, MediaType.WILDCARD));
+            limitByType(list, set, cls);
+            set = data.get(MediaType.WILDCARD_TYPE);
+            limitByType(list, set, cls);
+
+            return list;
+        }
+
+        private List<ObjectFactory<T>> getProvidersByWildcardMediaType(MediaType mediaType,
+                                                                       Class<?> cls) {
+
+            // according to JSR311 3.8, the providers must be searched
+            // using a concrete type
+            // if the providers are searched using a wildcard, it means
+            // that the call is done
+            // from the Providers interface, therefore isCompatible method
+            // should be used
+            // the search here is less efficient that the regular search
+
+            List<ObjectFactory<T>> list = new ArrayList<ObjectFactory<T>>();
+
+            ArrayList<Entry<MediaType, Set<ObjectFactory<T>>>> compatibleList =
+                new ArrayList<Entry<MediaType, Set<ObjectFactory<T>>>>();
+            for (Entry<MediaType, Set<ObjectFactory<T>>> entry : data.entrySet()) {
+                if (entry.getKey().isCompatible(mediaType)) {
+                    compatibleList.add(entry);
                 }
             }
+            // sort list according to n / m > n / * > * / *
+            Collections.sort(compatibleList,
+                             new Comparator<Entry<MediaType, Set<ObjectFactory<T>>>>() {
+
+                                 public int compare(Entry<MediaType, Set<ObjectFactory<T>>> o1,
+                                                    Entry<MediaType, Set<ObjectFactory<T>>> o2) {
+                                     MediaType m1 = o1.getKey();
+                                     MediaType m2 = o2.getKey();
+                                     if (m1.getType().equals(MediaType.MEDIA_TYPE_WILDCARD)) {
+                                         if (m2.getType().equals(MediaType.MEDIA_TYPE_WILDCARD)) {
+                                             return 0;
+                                         }
+                                         // m2 > m1
+                                         return -1;
+                                     } else {
+                                         if (!m2.getType().equals(MediaType.MEDIA_TYPE_WILDCARD)) {
+                                             return 0;
+                                         }
+                                         // m1 > m2
+                                         return 1;
+                                     }
+                                 }
+                             });
+            for (Entry<MediaType, Set<ObjectFactory<T>>> entry : compatibleList) {
+                limitByType(list, entry.getValue(), cls);
+            }
             return list;
         }
 
@@ -401,8 +485,9 @@
                 MediaType mediaType = entry.getKey();
                 Set<ObjectFactory<T>> set = entry.getValue();
                 for (ObjectFactory<T> t : set) {
-                    if (GenericsUtils.isGenericInterfaceAssignableFrom(type, t.getInstanceClass(),
-                        rawType)) {
+                    if (GenericsUtils.isGenericInterfaceAssignableFrom(type,
+                                                                       t.getInstanceClass(),
+                                                                       rawType)) {
                         mediaTypes.add(mediaType);
                         continue l1;
                     }
@@ -411,12 +496,14 @@
             return mediaTypes;
         }
 
-        private void limitByType(List<ObjectFactory<T>> list, Set<ObjectFactory<T>> set,
-            Class<?> type) {
+        private void limitByType(List<ObjectFactory<T>> list,
+                                 Set<ObjectFactory<T>> set,
+                                 Class<?> type) {
             if (set != null) {
                 for (ObjectFactory<T> t : set) {
-                    if (GenericsUtils.isGenericInterfaceAssignableFrom(type, t.getInstanceClass(),
-                        rawType)) {
+                    if (GenericsUtils.isGenericInterfaceAssignableFrom(type,
+                                                                       t.getInstanceClass(),
+                                                                       rawType)) {
                         list.add(t);
                     }
                 }

Modified: incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/providers/ProvidersContextResolverTest.java
URL: http://svn.apache.org/viewvc/incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/providers/ProvidersContextResolverTest.java?rev=793282&r1=793281&r2=793282&view=diff
==============================================================================
--- incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/providers/ProvidersContextResolverTest.java (original)
+++ incubator/wink/trunk/wink-common/src/test/java/org/apache/wink/common/internal/providers/ProvidersContextResolverTest.java Sun Jul 12 07:21:39 2009
@@ -31,7 +31,6 @@
 import org.apache.wink.common.internal.lifecycle.LifecycleManagersRegistry;
 import org.apache.wink.common.internal.registry.ProvidersRegistry;
 
-
 import junit.framework.TestCase;
 
 public class ProvidersContextResolverTest extends TestCase {
@@ -39,13 +38,14 @@
     public static class NotAProvider {
     }
 
-    private static final String  STRING = "String";
-    private static final String  ATOM   = "Atom";
-    private static final byte[]  BYTE   = new byte[0];
-    private static final Integer _12345 = new Integer(12345);
+    private static final String  STRING  = "String";
+    private static final String  ATOM    = "Atom";
+    private static final byte[]  BYTE    = new byte[0];
+    private static final Integer _12345  = new Integer(12345);
+    private static final MyClass MYCLASS = new MyClass();
 
     @Provider
-    @Produces( { MediaType.TEXT_PLAIN, MediaType.WILDCARD })
+    @Produces( {MediaType.TEXT_PLAIN, MediaType.WILDCARD})
     public static class StringContextResolver implements ContextResolver<String> {
 
         public String getContext(Class<?> type) {
@@ -54,7 +54,21 @@
     }
 
     @Provider
-    @Produces( { MediaType.APPLICATION_ATOM_XML, MediaType.WILDCARD })
+    @Produces(MediaType.APPLICATION_FORM_URLENCODED)
+    // intentionally using a MediaType with a '-' to exercise regex code
+    public static class MyContextResolver implements ContextResolver<MyClass> {
+
+        public MyClass getContext(Class<?> type) {
+            return MYCLASS;
+        }
+    }
+
+    public static class MyClass {
+
+    }
+
+    @Provider
+    @Produces( {MediaType.APPLICATION_ATOM_XML, MediaType.WILDCARD})
     public static class AtomContextResolver implements ContextResolver<String> {
 
         public String getContext(Class<?> type) {
@@ -66,7 +80,7 @@
     }
 
     @Provider
-    @Produces( { MediaType.TEXT_PLAIN, MediaType.WILDCARD })
+    @Produces( {MediaType.TEXT_PLAIN, MediaType.WILDCARD})
     public static class ByteContextResolver implements ContextResolver<byte[]> {
 
         public byte[] getContext(Class<?> type) {
@@ -75,7 +89,7 @@
     }
 
     @Provider
-    @Produces( { "text/decimal" })
+    @Produces( {"text/decimal"})
     public static class IntegerContextResolver implements ContextResolver<Integer> {
 
         public Integer getContext(Class<?> type) {
@@ -92,11 +106,12 @@
     }
 
     private ProvidersRegistry createProvidersRegistryImpl() {
-        ProvidersRegistry providers = new ProvidersRegistry(new LifecycleManagersRegistry(),
-            new ApplicationValidator());;
+        ProvidersRegistry providers =
+            new ProvidersRegistry(new LifecycleManagersRegistry(), new ApplicationValidator());
+        ;
         return providers;
     }
-    
+
     public void testContextResolvers() {
         ProvidersRegistry providers = createProvidersRegistryImpl();
         assertTrue(providers.addProvider(new AtomContextResolver()));
@@ -109,41 +124,71 @@
         /*
          * String and text/pain, should invoke StringContextResolver
          */
-        assertEquals(STRING,
-            providers.getContextResolver(String.class, MediaType.TEXT_PLAIN_TYPE, null).getContext(null));
+        assertEquals(STRING, providers.getContextResolver(String.class,
+                                                          MediaType.TEXT_PLAIN_TYPE,
+                                                          null).getContext(null));
 
         /*
          * byte[] and text/plain, should invoke ByteContextResolver
          */
-        assertEquals(BYTE,
-            providers.getContextResolver(byte[].class, MediaType.TEXT_PLAIN_TYPE, null).getContext(null));
+        assertEquals(BYTE, providers.getContextResolver(byte[].class,
+                                                        MediaType.TEXT_PLAIN_TYPE,
+                                                        null).getContext(null));
 
         /*
          * There is no context resolver that handlers Integer and /
          */
-        assertEquals(null,
-            providers.getContextResolver(Integer.class, MediaType.WILDCARD_TYPE, null));
+        assertEquals(_12345, providers.getContextResolver(Integer.class,
+                                                        MediaType.WILDCARD_TYPE,
+                                                        null).getContext(null));
 
         /*
          * AtomContextResolver comes before StringContextResolver, therefore it
          * should be invoked after
          */
-        assertEquals(STRING,
-            providers.getContextResolver(String.class, MediaType.WILDCARD_TYPE, null).getContext(null));
+        assertEquals(STRING, providers.getContextResolver(String.class,
+                                                          MediaType.WILDCARD_TYPE,
+                                                          null).getContext(null));
 
         /*
          * AtomContextResolver returnes null, if the parameter is not null,
          * therefore StringContextResolver should be invoked
          */
-        assertEquals(STRING,
-            providers.getContextResolver(String.class, MediaType.WILDCARD_TYPE, null).getContext(
-                String.class));
+        assertEquals(STRING, providers.getContextResolver(String.class,
+                                                          MediaType.WILDCARD_TYPE,
+                                                          null).getContext(String.class));
 
         /*
          * test ContextResolver with collections
          */
         assertEquals(Collections.emptyList(), providers.getContextResolver(List.class,
-            MediaType.WILDCARD_TYPE, null).getContext(null));
+                                                                           MediaType.WILDCARD_TYPE,
+                                                                           null).getContext(null));
+    }
+
+    public void testContextResolverWildCards() {
+        ProvidersRegistry providers = createProvidersRegistryImpl();
+        assertTrue(providers.addProvider(new MyContextResolver()));
+
+        /*
+         * Check various wildcard permutations
+         */
+        assertSame(MYCLASS, providers.getContextResolver(MyClass.class,
+                                                         MediaType.WILDCARD_TYPE,
+                                                         null).getContext(MyClass.class));
+        assertSame(MYCLASS, providers.getContextResolver(MyClass.class,
+                                                         new MediaType("*", "*"),
+                                                         null).getContext(MyClass.class));
+        assertSame(MYCLASS, providers.getContextResolver(MyClass.class,
+                                                         new MediaType("application", "*"),
+                                                         null).getContext(MyClass.class));
+        assertSame(MYCLASS, providers.getContextResolver(MyClass.class,
+                                                         new MediaType("application",
+                                                                       "x-www-form-urlencoded"),
+                                                         null).getContext(MyClass.class));
+        assertSame(MYCLASS, providers
+            .getContextResolver(MyClass.class, new MediaType("*", "x-www-form-urlencoded"), null)
+            .getContext(MyClass.class));
     }
 
 }