quarkiverse / quarkus-cxf

Quarkus CXF Extension to support SOAP based web services.
Apache License 2.0
71 stars 57 forks source link

ConcurrentModificationException when using JaxWsProxyFactoryBean #1397

Open nithom1 opened 1 month ago

nithom1 commented 1 month ago

When using this following code (A). Sometimes (possibly under highload) I get the mentioned ConcurrentModificationException (B). What do iam wrong, when using the section (A) on serveral several threads? Using io.quarkiverse.cxf:quarkus-cxf:4.0.4

(A)

final JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setAddress(clientEndpointUrl);
final DokumentenAbrufUR dokumentenAbrufUR = factory.create(DokumentenAbrufUR.class);

try (Client c = ClientProxy.getClient(dokumentenAbrufUR)) {

In the last line the Exception (B) is throwed

(B)

java.util.ConcurrentModificationException
    at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1229)
    at org.glassfish.jaxb.runtime.v2.model.impl.TypeInfoSetImpl.getXmlNs(TypeInfoSetImpl.java:298)
    at org.glassfish.jaxb.runtime.v2.schemagen.XmlSchemaGenerator$Namespace.writeTo(XmlSchemaGenerator.java:551)
    at org.glassfish.jaxb.runtime.v2.schemagen.XmlSchemaGenerator.write(XmlSchemaGenerator.java:413)
    at org.glassfish.jaxb.runtime.v2.runtime.JAXBContextImpl.generateSchema(JAXBContextImpl.java:770)
    at org.apache.cxf.common.jaxb.JAXBUtils.generateJaxbSchemas(JAXBUtils.java:810)
    at org.apache.cxf.jaxb.JAXBDataBinding.generateJaxbSchemas(JAXBDataBinding.java:474)
    at org.apache.cxf.jaxb.JAXBDataBinding.initialize(JAXBDataBinding.java:391)
    at org.apache.cxf.service.factory.AbstractServiceFactoryBean.initializeDataBindings(AbstractServiceFactoryBean.java:87)
    at org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean.buildServiceFromClass(ReflectionServiceFactoryBean.java:469)
    at org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean.buildServiceFromClass(JaxWsServiceFactoryBean.java:693)
    at org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean.initializeServiceModel(ReflectionServiceFactoryBean.java:529)
    at org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean.create(ReflectionServiceFactoryBean.java:262)
    at org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean.create(JaxWsServiceFactoryBean.java:199)
    at org.apache.cxf.frontend.AbstractWSDLBasedEndpointFactory.createEndpoint(AbstractWSDLBasedEndpointFactory.java:103)
    at org.apache.cxf.frontend.ClientFactoryBean.create(ClientFactoryBean.java:91)
    at org.apache.cxf.frontend.ClientProxyFactoryBean.create(ClientProxyFactoryBean.java:158)
    at org.apache.cxf.jaxws.JaxWsProxyFactoryBean.create(JaxWsProxyFactoryBean.java:142)
    at org.apache.cxf.frontend.ClientProxyFactoryBean.create(ClientProxyFactoryBean.java:95)
ppalaga commented 1 month ago

Apparently TypeInfoSetImpl.getXmlNs() was not designed for concurrent access. A quick and potentially non-performant workaround would be to prevent it by using synchronized or similar.

I wonder why you need to create a new client every time? Does the clientEndpointUrl change that often?

nithom1 commented 1 month ago

No clientEndpointUrl is changed only once, when its declared. So do you suggest to reuse that client for all threads?

ppalaga commented 1 month ago

Yes, definitely. You may want to read https://docs.quarkiverse.io/quarkus-cxf/dev/user-guide/first-soap-client.html

nithom1 commented 1 month ago

I see I need to rebuild the client because the timeout of the soap requests are variable. Is there a other possible Way to archive this without rebuild the client?

ppalaga commented 4 weeks ago

I need to rebuild the client

What kind of rebuild do you have on mind? Like change the timeout in application.properties and re-run maven?

because the timeout of the soap requests are variable.

How variable are they? You need to change it at runtime?

nithom1 commented 4 weeks ago

The timeout would change at runtime for some kind of request timeout A for other request timeout B. Because of thif i need to rebuild the client for each request.

ppalaga commented 4 weeks ago

If you really need to change the client configuration at runtime and there is no way to use some finite set of n clients with n different constant timeouts, then you may consider creating a pool of clients from which you take a client for the given task, make sure it is not used concurrently and as long as it is safeguarded from a concurrent access you may reconfigure it however you want, something like the following:


MyService clientProxy = pool.acquire();
try {
    Client client = ClientProxy.getClient(clientProxy);
    HTTPConduit http = (HTTPConduit) client.getConduit();
    HTTPClientPolicy httpClientPolicy = http.getClient();

    httpClientPolicy.setConnectionTimeout(...);
    httpClientPolicy.setReceiveTimeout(...);

    clientProxy.hello("World")
} finally {
    pool.release(clientProxy);
}