Как установить параметры TLS для канала CXF?
Мое приложение имеет два исходящих соединения SOAP. Для тех, кто хочет реализовать TLS. Оба созданы с использованием CXF.
javax.xml.ws.Service.getPort()
возвращает индивидуальный bindingProvider (оба соединения используют свой собственный WSDL), но оба используют один и тот же org.apache.cxf.bus.spring.SpringBus
пример.
Перед использованием bindingProvider я установил параметры клиента TLS в 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);
Проблема в том, что извлеченный клиент различен для обоих соединений, но канал - это один и тот же объект. Таким образом, когда я устанавливаю параметры для второго соединения для того же объекта, я перезаписываю ранее установленные настройки.
Ответы на часто задаваемые вопросы, если CXF является потокобезопасным, с "да" и рядом исключений. Я думаю, что второе исключение применимо здесь. Это говорит:
Ответ CXF: прокси-серверы CXF поточно-ориентированы для многих случаев использования. Исключения:
[...]
Настройки в канале - если вы используете код или конфигурацию для прямого манипулирования каналом (например, для установки настроек TLS или аналогичных), они не являются поточно-ориентированными. Канал для каждого экземпляра и, таким образом, эти параметры будут общими. Кроме того, если вы используете FailoverFeature и LoadBalanceFeatures, канал будет заменен на лету. Таким образом, настройки, установленные на кабелепроводе, могут быть потеряны перед использованием в потоке настройки.
[...]
В случае проблем с каналом вы МОЖЕТЕ установить новый ConduitSelector, использующий локальный поток или аналогичный. Это немного сложно, хотя.
Я не совсем уверен, является ли безопасность потоков моей проблемой. Я создаю два соединения, каждое в своем собственном компоненте. Springs использует только один поток для инициализации всех компонентов, поэтому оба соединения инициализируются одним и тем же потоком. Но впоследствии соединение использует потоки из пула. Перезапись настроек происходит во время инициализации, поэтому перед отправкой реальных сообщений SOAP используются разные потоки.
Когда проводник создан в org.apache.cxf.endpoint.AbstractConduitSelector#getSelectedConduit
это делается с помощью SpringBus
который является одним и тем же экземпляром для обоих объектов.
Итак, в FAQ сказано, чтобы я использовал свой собственный ConduitSelector. Я попытался установить его до инициализации выше:
Client client = ClientProxy.getClient(bindingProvider);
client.setConduitSelector(
new UpfrontConduitSelector(
new URLConnectionHTTPConduit(client.getBus(),
client.getEndpoint().getEndpointInfo())));
и я попробовал то же самое после инициализации. В обоих случаях после установки селектора канала, когда что-то использует BindingProvider (который является прокси-объектом), он получает исключение NullPointerException, хотя объект не является нулевым.
Моя проблема здесь заключается в том, чтобы либо запустить пользовательский селектор кабелепровода, либо увидеть, что моя проблема может быть решена совершенно по-другому, или просто получить вдохновение:)
Какой-то парень из SO, похоже, решил это здесь, но ответ на его вопрос мне не помогает.
1 ответ
Я нашел решение.
Проблема действительно не имела ничего общего с многопоточностью, но с тем, как SpringBus подключен к моим объектам и как из него создается Conduit.
Решением было предоставить каждому сервису свой SpringBus.
Поэтому, прежде чем я создам каждый SOAP-сервис, вызывая его c'tor в javax.xml.ws.Service
, Я делаю
BusFactory bf = BusFactory.newInstance();
Bus b = bf.createBus();
BusFactory.setThreadDefaultBus(b);
которая устанавливает новую локальную шину по умолчанию, которая затем используется для созданной службы. Таким образом, каждый из двух моих сервисов имеет свой собственный SpringBus, и они оба создают свой собственный Conduit.
Это работает, потому что каждая услуга является весной @Component
и все пружинные компоненты создаются основной резьбой. Таким образом, существует только один поток, и ни в коем случае этот код не будет выполняться последовательно.