users@jersey.java.net

Re: [Jersey] SAXParserContextProvider is a contention point

From: Paul Sandoz <Paul.Sandoz_at_oracle.com>
Date: Mon, 30 Aug 2010 12:11:36 +0200

Hi Felix,

I have just committed a fix so that the async client uses
ExecutorService rather than creating a new thread. By default the
implement returned from Executors.newCachedThreadPool() is utilized
but you can set your own implementation on the Client.

Paul.

On Aug 25, 2010, at 10:54 AM, Paul Sandoz wrote:

> Hi Felix,
>
> After some email exchange with one of your colleagues i understand
> the problem now, i overlooked the "asynchronous" in your first email.
>
> Because we create a new thread for every client request (the
> illusion of async) a new parser instance will be created every time,
> hence the contention you point out.
>
> I think the right solution here is to use a proper underlying async
> API, which obviously requires a little more work (and we may be
> required to break some APIs, also filtering needs to be changed, i
> wrote a proposal on this list a while ago on how to do that).
>
> Another simpler interim solution is consider using of a thread pool.
> I think the async handler impl:
>
> public Future<ClientResponse> handle(final ClientRequest request,
> final FutureListener<ClientResponse> l) {
> Callable c = new Callable() {
> public Object call() throws Exception {
> return getHeadHandler().handle(request);
> }
> };
> FutureTask<ClientResponse> ft = new
> FutureTask<ClientResponse>(c) {
> @Override
> protected void done() {
> try {
> l.onComplete(this);
> } catch (Throwable t) {
> LOGGER.log(Level.SEVERE,
> "Throwable caught on call to
> ClientResponseListener.onComplete",
> t);
> }
> }
> };
>
> new Thread(ft).start();
> return ft;
> }
>
> could use a pool and the done methods of "ft" could return the
> thread to the pool. It does mean that the number of requests could
> be limited waiting for a thread to become available (but there would
> i suspect be some blocking lower down in the HttpURLConnection at
> some point when enough threads are utilized as they will contend for
> IO/CPU resources).
>
> Paul.
>
>
> On Aug 19, 2010, at 6:29 PM, Paul Sandoz wrote:
>
>> Hi Felix,
>>
>> The SAXParserContextProvider that extends
>> ThreadLocalSingletonContextProvider only creates a new instance of
>> SAXParserFactory per-thread (i.e. when a get is called on the
>> thread local and the value has not been set), it will not be
>> creating a new instance of SAXParserFactory every time one is
>> created, so i would expect any contention to be limited to an
>> initialization period.
>>
>> See:
>>
>> http://download.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html#initialValue%28%29
>>
>> Now that i think about it i am wondering why it is like this:
>>
>> protected ThreadLocalSingletonContextProvider(Class<T> t) {
>> this.t = t;
>> this.rf = new ThreadLocal<T>() {
>> @Override
>> protected synchronized T initialValue() {
>> return getInstance();
>> }
>> };
>> }
>>
>> and the synchronized is used. I think it was because i was not sure
>> what would happen if two threads were executing the following at
>> the same time:
>>
>> SAXParserFactory f = SAXParserFactory.newInstance();
>>
>>
>> And that is the general why we keep instances of SAXParserFactory
>> per-thread: we cannot guarantee that those implementations are
>> thread safe when utilized to obtain SAXParser instances. The spec
>> and JavaDoc are vague on the topic.
>>
>> You can override by registering your own InjectableProvider
>> implementation that supports the SAXParserFactory type and
>> registering that implementation as a provider (annotate with
>> @Provider to identify and then register explicitly or via scanning).
>>
>> Paul.
>>
>>
>> On Aug 19, 2010, at 5:06 PM, felix-etienne.trepanier_at_nokia.com wrote:
>>
>>> Hi,
>>>
>>> I’ve been using the jersey default client (new Client()) using the
>>> asynchronous API on several thread simultaneously and noticed a
>>> contention point (see stacktrace bellow). The
>>> XMLRootElementProvider get a SAXParserContextProvider and used it
>>> to read the XML stream. The implementation of the
>>> SAXParserContextProvider is based on the
>>> ThreadLocalSingletonContextProvider which synchronizes the
>>> getInitialValue() but the implementation of
>>> SAXParserContextProvider recreates a new SAXParserFactory every
>>> time and this seems to be a costly operation. This causes
>>> contention.
>>>
>>> I’m trying to figure out how to inject another implementation of
>>> the SAXParserContextProvider that would not synchronize the
>>> creation of the SAXParserFactory. Pointers would be helpful.
>>>
>>> Thanks,
>>>
>>> Felix
>>> …
>>> at
>>> java.lang.ClassLoader.getResourceAsStream(ClassLoader.java:1168)
>>> at javax.xml.parsers.SecuritySupport
>>> $4.run(SecuritySupport.java:96)
>>> at java.security.AccessController.doPrivileged(Native Method)
>>> at
>>> javax
>>> .xml
>>> .parsers.SecuritySupport.getResourceAsStream(SecuritySupport.java:
>>> 89)
>>> at
>>> javax
>>> .xml
>>> .parsers.FactoryFinder.findJarServiceProvider(FactoryFinder.java:
>>> 250)
>>> at javax.xml.parsers.FactoryFinder.find(FactoryFinder.java:
>>> 223)
>>> at
>>> javax
>>> .xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:128)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .core
>>> .impl
>>> .provider
>>> .xml
>>> .SAXParserContextProvider
>>> .getInstance(SAXParserContextProvider.java:65)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .core
>>> .impl
>>> .provider
>>> .xml
>>> .SAXParserContextProvider
>>> .getInstance(SAXParserContextProvider.java:51)
>>> at
>>> com
>>> .sun
>>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider
>>> $1.initialValue(ThreadLocalSingletonContextProvider.java:61)
>>> - locked <0x00002aaab578ecd8> (a
>>> com
>>> .sun
>>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider
>>> $1)
>>> at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:141)
>>> at java.lang.ThreadLocal.get(ThreadLocal.java:131)
>>> at
>>> com
>>> .sun
>>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider
>>> $2.getValue(ThreadLocalSingletonContextProvider.java:74)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .core
>>> .impl
>>> .provider
>>> .entity
>>> .XMLRootElementProvider.readFrom(XMLRootElementProvider.java:110)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .core
>>> .provider
>>> .jaxb
>>> .AbstractRootElementProvider
>>> .readFrom(AbstractRootElementProvider.java:106)
>>> at
>>> com
>>> .sun
>>> .jersey.api.client.ClientResponse.getEntity(ClientResponse.java:549)
>>> at
>>> com
>>> .sun
>>> .jersey.api.client.ClientResponse.getEntity(ClientResponse.java:502)
>>> at com.sun.jersey.api.client.AsyncWebResource
>>> $3.get(AsyncWebResource.java:706)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .client
>>> .impl
>>> .async
>>> .FutureClientResponseListener
>>> .onComplete(FutureClientResponseListener.java:103)
>>> at com.sun.jersey.api.client.AsyncWebResource
>>> $6.done(AsyncWebResource.java:749)
>>> at java.util.concurrent.FutureTask
>>> $Sync.innerSet(FutureTask.java:251)
>>> at java.util.concurrent.FutureTask
>>> $Sync.innerRun(FutureTask.java:303)
>>> at java.util.concurrent.FutureTask.run(FutureTask.java:138)
>>> at java.lang.Thread.run(Thread.java:619)
>>>
>>> "Thread-42745" daemon prio=10 tid=0x00002aaafa221000 nid=0x9ca
>>> waiting for monitor entry [0x00002aab74f8a000..0x00002aab74f8ab10]
>>> java.lang.Thread.State: BLOCKED (on object monitor)
>>> at
>>> com
>>> .sun
>>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider
>>> $1.initialValue(ThreadLocalSingletonContextProvider.java:61)
>>> - waiting to lock <0x00002aaab578ecd8> (a
>>> com
>>> .sun
>>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider
>>> $1)
>>> at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:141)
>>> at java.lang.ThreadLocal.get(ThreadLocal.java:131)
>>> at
>>> com
>>> .sun
>>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider
>>> $2.getValue(ThreadLocalSingletonContextProvider.java:74)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .core
>>> .impl
>>> .provider
>>> .entity
>>> .XMLRootElementProvider.readFrom(XMLRootElementProvider.java:110)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .core
>>> .provider
>>> .jaxb
>>> .AbstractRootElementProvider
>>> .readFrom(AbstractRootElementProvider.java:106)
>>> at
>>> com
>>> .sun
>>> .jersey.api.client.ClientResponse.getEntity(ClientResponse.java:549)
>>> at
>>> com
>>> .sun
>>> .jersey.api.client.ClientResponse.getEntity(ClientResponse.java:502)
>>> at com.sun.jersey.api.client.AsyncWebResource
>>> $3.get(AsyncWebResource.java:706)
>>> at
>>> com
>>> .sun
>>> .jersey
>>> .client
>>> .impl
>>> .async
>>> .FutureClientResponseListener
>>> .onComplete(FutureClientResponseListener.java:103)
>>> at com.sun.jersey.api.client.AsyncWebResource
>>> $6.done(AsyncWebResource.java:749)
>>> at java.util.concurrent.FutureTask
>>> $Sync.innerSet(FutureTask.java:251)
>>> at java.util.concurrent.FutureTask
>>> $Sync.innerRun(FutureTask.java:303)
>>> at java.util.concurrent.FutureTask.run(FutureTask.java:138)
>>> at java.lang.Thread.run(Thread.java:619)
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>