users@jersey.java.net

Re: [Jersey] Thread access to _at_Context objects?

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Mon, 02 Nov 2009 11:08:27 +0100

On Nov 2, 2009, at 3:59 AM, Chris Hubick wrote:

> On Fri, 2009-10-23 at 21:52 +0200, Paul Sandoz wrote:
>> On Oct 23, 2009, at 9:22 PM, Chris Hubick wrote:
>>> I run all Servlet API access through a ServletUtil class which
>>> synchronizes all calls, which works fine directly under Tomcat. How
>>> can I make this work under Jersey?
>>>
>>> Will finishing the port to the JAX-RS (HttpHeaders/UriInfo/etc) API
>>> solve this, or will I have the same problem?
>>
>> If your resource classes are in the default per-request scope then
>> there will be no issue as direct references of the JAX-RS classes you
>> mention will be injected.
>
> OK, so I finished enough of my conversion to the JAX-RS API to re-
> enable
> threading, and immediately got a ConcurrentModificationException
> inside
> java.util.ArrayList$Itr.next() when multiple threads (all inside the
> request scope) try to simultaneously iterate (read-only) through the
> List<Locale> returned by HttpHeaders.getAcceptableLanguages().
>
> It appears as though the implementation at
> com.sun.jersey.spi.container.ContainerRequest.getAcceptableLanguages()
> (as well as other methods in that class) returns the result using a
> non-thread-safe ArrayList, rather than say, a thread-safe Vector.
>
> JAX-RS API thread safety would be a huge benefit to me, saving a lot
> of
> ugly synchronization wrapper code around every call, like I needed for
> Servlet API access. Is this a bug? I don't see anything in the
> specification either way? Does that mean that, to work across JAX-RS
> implementations, I am basically forced to synchronize myself? :(
>

Yes. Those classes are not designed to be re-entrant (for the reasons
that Craig and Tatu give).

IIRC HttpServletRequest is not meant to be re-entrant either. But it
just so happens you are using something that is, Enumeration (created
from Vector), because the servlet API is old.

The use of Vector is an implementation detail. In a different servlet
implementation an implementation of List might be used in conjunction
with Collections.enumeration. So your previous JAX-RS code may
potentially break on different Web containers or in future versions of
the same Web container.

If you want we can work on providing thread-safe adapters for non-
thread-local proxy instances.

public class ReentrantHttpHeaders implements HttpHeaders {
   private final HttpHeaders hhs;

   public ReentrantHttpHeaders(HttpHeaders hhs) {
     this.hhs = hhs;
   }

   public synchronized List<java.util.Locale> getAcceptableLanguages() {
     return Collections.synchronizedList(hhs. getAcceptableLanguages());
   }

   ...

   // Adapting MutilvaluedMap requires a little more work.

}

So you can pass the adapted instance.

Or simple pass:

   Collections.synchronizedList(hhs. getAcceptableLanguages());

to the worker threads if that is sufficient, which as Craig suggests
may separate out your worker threads from explicit JAX-RS dependencies
(which should make it easier to test independently).

Paul.