javasslsoapcxfcxf-client

How to set TLS parameters on a CXF conduit?


My application has two outgoing SOAP connections. For those, I want to implement TLS. Both are created using CXF.

The javax.xml.ws.Service.getPort() returns an individual bindingProvider (both connections use their own WSDL) but both use the same org.apache.cxf.bus.spring.SpringBus instance.

Before using the bindingProvider, I set the TLS client parameters on the Conduit:

Client client = ClientProxy.getClient(bindingProvider); // different
HTTPConduit httpConduit = (HTTPConduit) client.getConduit(); // same for both connections
TLSClientParameters tlsClientParameters = new TLSClientParameters();
tlsClientParameters.setTrustManagers(getTrustmanagers());
httpConduit.setTlsClientParameters(tlsClientParameters);

The issue is, the retrieved client is different for both connections, but the conduit is the same object. Thus, when I set the parameters for the second connection on the same object, I overwrite the priorly set settings.

The FAQ answer if CXF is threadsafe with "yes" and a number of exceptions. I think the second exception applies here. It says:

CXF answer: CXF proxies are thread safe for MANY use cases. The exceptions are:

  • [...]

  • Settings on the conduit - if you use code or configuration to directly manipulate the conduit (like to set TLS settings or similar), those are not thread safe. The conduit is per-instance and thus those settings would be shared. Also, if you use the FailoverFeature and LoadBalanceFeatures, the conduit is replaced on the fly. Thus, settings set on the conduit could get lost before being used on the setting thread.

  • [...]

For the conduit issues, you COULD install a new ConduitSelector that uses a thread local or similar. That's a bit complex though.

I'm not entirely sure if thread safety is my issue. I create the two connections each in their own component. Springs uses only one thread for initializing all the components, so both connections are initialized by the same thread. But afterwards, the connection is used threads from a pool. Overwriting the settings happens during intialization, so before sending actual SOAP messages using different threads.

When the Conduit is created in org.apache.cxf.endpoint.AbstractConduitSelector#getSelectedConduit, it is done using the SpringBus which is the same instance for both objects.

So, the FAQ tell me to use my own custom ConduitSelector. I tried setting it before the initialization above:

Client client = ClientProxy.getClient(bindingProvider);
client.setConduitSelector(
    new UpfrontConduitSelector(
        new URLConnectionHTTPConduit(client.getBus(), 
                                     client.getEndpoint().getEndpointInfo())));

and I tried the same after the initialization. In both cases, after setting the conduit selector, when something uses the BindingProvider (which is a Proxy-object), it gets a NullPointerException although the object is not null.

My issue here is to either get the custom conduit selector running or to see that my issue can be solved completely differently or just to get some inspiration :)

Some guy on SO seems to have solved this here, but the answer to his question is not helping me.


Solution

  • I found a solution.

    The issue indeed had nothing to do with multithreading, but with the way the SpringBus is wired into my objects and how the Conduit is created from it.

    The solution was to give each service its own SpringBus.

    So before I create each SOAP-service by calling its c'tor in javax.xml.ws.Service, I do

    BusFactory bf = BusFactory.newInstance();
    Bus b = bf.createBus();
    BusFactory.setThreadDefaultBus(b);
    

    which sets a new threadlocal default bus which is then used for the service created. Thus, my two services each have their own SpringBus and they both create their own Conduit.

    This works because each service is a spring @Component and all spring components are created by the main thread. So there is only one thread and no way this code will not be executed sequentially.