BingAds / BingAds-Java-SDK

Other
42 stars 48 forks source link

ConcurrentModificationException at apache-cxf #184

Closed schabe77 closed 6 months ago

schabe77 commented 6 months ago

After switching to 13.0.20 I sometimes get this the exception

java.util.ConcurrentModificationException: null
    at java.base/java.util.ArrayList.sort(ArrayList.java:1806)
    at org.apache.cxf.jaxrs.provider.ProviderFactory.sortReaders(ProviderFactory.java:740)
    at org.apache.cxf.jaxrs.provider.ProviderFactory.setCommonProviders(ProviderFactory.java:668)
    at org.apache.cxf.jaxrs.client.ClientProviderFactory.setProviders(ClientProviderFactory.java:73)
    at org.apache.cxf.jaxrs.provider.ProviderFactory.setUserProviders(ProviderFactory.java:868)
    at org.apache.cxf.jaxrs.client.spec.ClientImpl$WebTargetImpl.request(ClientImpl.java:282)
    at com.microsoft.bingads.internal.restful.RestfulServiceClient.createRequestBuilder(RestfulServiceClient.java:126)
    at com.microsoft.bingads.internal.restful.RestfulServiceClient.processRequestAsync(RestfulServiceClient.java:336)
    at com.microsoft.bingads.internal.restful.BulkService.sendRequestAsync(BulkService.java:83)
    at com.microsoft.bingads.internal.restful.BulkService.uploadEntityRecordsAsync(BulkService.java:202)
    at com.microsoft.bingads.v13.bulk.BulkServiceManager.uploadEntityRecordsImpl(BulkServiceManager.java:196)
    at com.microsoft.bingads.v13.bulk.BulkServiceManager.uploadEntitiesAsync(BulkServiceManager.java:181)

it seems the ProviderFactory isn't thread safe. would it be possible to put the access to WebTarget within a synchronized block?

schabe77 commented 6 months ago

I fixed it by replacing the rather poor cxf library with jersey client.

dennis-yemelyanov commented 6 months ago

thank you for reporting this! Yes, looks like CXF is not thread safe. A fix should be released soon

schabe77 commented 6 months ago

Thank you. I changed my dependencies this way and it seems to work:

        <dependency>
            <groupId>com.microsoft.bingads</groupId>
            <artifactId>microsoft.bingads</artifactId>
            <version>13.0.20</version>
            <exclusions>
                <exclusion>
                    <artifactId>cxf-rt-rs-client</artifactId>
                    <groupId>org.apache.cxf</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-client</artifactId>
        </dependency>

With jersey I don't have these multithreading problems. I'm not sure if it's a good idea to synchronize the access at an unknown amount of positions because a rs-implementation is poorly implemented.

dennis-yemelyanov commented 6 months ago

Yeah the standard is also not clear on what should be thread safe or not, looks like there is an open issue regarding this: https://github.com/jakartaee/rest/issues/494

I'm not sure if other implementations are thread safe, so it's probably better to add synchronization around the request() call as you suggested. 13.0.20.1 has a fix for this.

By default we use CXF since it usually has better performance, but it's true that sometimes we may run into tricky issues like this.

By the way, when using a different RS implementation, we recommend to enable message compression for better performance. For example, this works for Jersey and 13.0.20.1:

GlobalSettings.setHttpClientProvider(new HttpClientProvider() 
{ 
    @Override 
    protected ClientBuilder configureClientBuilder(ClientBuilder clientBuilder) { 
        return super.configureClientBuilder(clientBuilder)
            .register(org.glassfish.jersey.message.GZipEncoder.class)
            .register(org.glassfish.jersey.client.filter.EncodingFilter.class);
    }
});