Uploaded image for project: 'RESTEasy'
  1. RESTEasy
  2. RESTEASY-2661

MediaTypeMap shared across threads, but cache is not thread-safe

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Critical Critical
    • 4.5.8.Final
    • 4.5.5.Final
    • jaxrs
    • None
    • Hide

      I expect this to be difficult to reproduce under controlled conditions.

      Show
      I expect this to be difficult to reproduce under controlled conditions.

      Under high contention during startup, we observe:

           Caused by: javax.ws.rs.ProcessingException: java.lang.NullPointerException: Cannot invoke "java.util.Map.put(Object, Object)" because "cache" is null
          	at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:251)
          	at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:88)
          	at org.jboss.resteasy.specimpl.AbstractBuiltResponse.readEntity(AbstractBuiltResponse.java:256)
          	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:163)
          	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.lambda$getResponseTypeExtractor$6(ClientInvocation.java:613)
          	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.lambda$asyncSubmit$10(ClientInvocation.java:756)
          	at org.jboss.resteasy.client.jaxrs.engines.jetty.JettyClientEngine$2.complete(JettyClientEngine.java:198)
          	at org.jboss.resteasy.client.jaxrs.engines.jetty.JettyClientEngine$2.onSuccess(JettyClientEngine.java:184)
          	... 24 more
          Caused by: java.lang.NullPointerException: Cannot invoke "java.util.Map.put(Object, Object)" because "cache" is null
          	at org.jboss.resteasy.core.MediaTypeMap.getPossible(MediaTypeMap.java:584)
          	at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.resolveMessageBodyReader(ResteasyProviderFactoryImpl.java:576)
          	at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.getClientMessageBodyReader(ResteasyProviderFactoryImpl.java:568)
          	at org.jboss.resteasy.core.interception.jaxrs.ClientReaderInterceptorContext.resolveReader(ClientReaderInterceptorContext.java:54)
          	at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:130)
          	at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:75)
          	at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:214)

       

      ResteasyProviderFactoryImpl holds a ConcurrentMap with MediaTypeMap values.

      MediaTypeMap maintains a cache:

            if (useCache) {
               // don't care about variable volatility.  Really rare to add entries post boot
               Map<CachedMediaTypeAndClass, List<T>> cache = classCache;
               if (classCache == null) {
                  cache = new HashMap<>();
                  classCache = cache;
               }
               cache.put(cacheEntry, cached);
            } 

      Note in particular, classCache is read twice.  If the first read yields a null value, but a competing thread writes before the second read, you may end up storing into a null map.  I appreciate that this code probably wants to be fast rather than cache perfectly, but this is not thread-safe in a way that may crash rather than simply under-perform.

              rhn-engineering-ema Jim Ma
              stevenschlansker Steven Schlansker (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Created:
                Updated:
                Resolved: