我的应用程序有两个传出SOAP连接。对于这些,我想实现TLS。两者都是使用CXF创建的。
javax.xml.ws.Service.getPort()
返回一个单独的bindingProvider(两个连接都使用它们自己的WSDL),但是两者都使用相同的org.apache.cxf.bus.spring.SpringBus
实例。
在使用bindingProvider之前,我在管道上设置TLS客户端参数:
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);
问题是,两个连接的检索到的客户端不同,但是管道是同一对象。因此,当我在同一对象上设置第二个连接的参数时,我将覆盖先前设置的设置。
常见问题解答答案if CXF is threadsafe带有“是”和许多例外。我认为第二个例外在这里适用。它说:
CXF答案:对于许多用例,CXF代理都是线程安全的。例外是:
[...]
导管上的设置-如果使用代码或配置直接操作导管(例如设置TLS设置或类似设置),则这些设置不是线程安全的。该管道是基于实例的,因此将共享那些设置。另外,如果使用FailoverFeature和LoadBalanceFeatures,则将即时更换导管。因此,在导管上设置的设置在用于设置线程之前可能会丢失。
[...]
对于导管问题,您可以安装一个使用本地或类似线程的新ConduitSelector。不过,这有点复杂。
我不确定线程安全是否是我的问题。我在各自的组件中创建了两个连接。 Springs仅使用一个线程来初始化所有组件,因此两个连接都由同一线程初始化。但是之后,该连接将使用来自池的线程。覆盖设置是在初始化期间发生的,因此在使用不同的线程发送实际的SOAP消息之前。
在org.apache.cxf.endpoint.AbstractConduitSelector#getSelectedConduit
中创建管道时,使用SpringBus
来完成,这是两个对象的实例。
因此,常见问题解答告诉我使用我自己的自定义ConduitSelector。 我尝试在上述初始化之前设置它:
Client client = ClientProxy.getClient(bindingProvider);
client.setConduitSelector(
new UpfrontConduitSelector(
new URLConnectionHTTPConduit(client.getBus(),
client.getEndpoint().getEndpointInfo())));
,初始化后我尝试了同样的方法。在这两种情况下,设置管道选择器后,当某些对象使用BindingProvider(它是一个代理对象)时,尽管该对象不为null,它仍会收到NullPointerException。
我的问题是使自定义管道选择器运行,或者看到我的问题可以完全解决,或者只是为了获得启发:)
SO上的某个人似乎已经解决了这个here,但是他的问题的答案对我没有帮助。
答案 0 :(得分:1)
我找到了解决方法。
这个问题确实与多线程无关,而是与SpringBus连接到我的对象的方式以及如何从中创建导管有关。
解决方案是为每个服务提供自己的SpringBus。
因此,在我通过在javax.xml.ws.Service
中调用c'tor创建每个SOAP服务之前,我已经完成
BusFactory bf = BusFactory.newInstance();
Bus b = bf.createBus();
BusFactory.setThreadDefaultBus(b);
设置新的本地线程默认总线,然后将其用于创建的服务。因此,我的两个服务都具有自己的SpringBus,并且它们都创建了自己的管道。
之所以可行,是因为每个服务都是一个弹簧@Component
,并且所有弹簧组件都是由主线程创建的。因此,只有一个线程,并且无法按顺序执行此代码。