Можно ли избежать использования провайдера JAX-WS, который подбирается через определение услуги в META-INF?

64
8

Я использую следующую строку для запуска конечной точки JAX-WS:

Endpoint.publish(url, impl);

У меня есть CXF на пути к классам, поэтому его реализация Provider получает время выполнения через механизм ServiceLoader. Если я правильно понимаю код, среда выполнения просто выбирает первую реализацию Provider возвращаемую ServiceLoader. В результате запускается конечная точка CXF.

Я бы хотел этого избежать. Я знаю, что реализация JAX-WS по умолчанию поставляется с JDK (в моем случае это JDK 8). Можно ли исключить Provider CXF или установить приоритет по умолчанию, который нужно выбрать первым? (Предпочтительно без хаков, таких как удаление описания служебного файла из флага CXF).

спросил(а) 2021-01-25T14:45:31+03:00 6 месяцев назад
1
Решение
63

Вы должны сами найти поставщиков:

public static Endpoint publish(String address,
Object implementor) {

for (Provider provider : ServiceLoader.load(Provider.class)) {
if (!provider.getClass().getName().contains("cxf")) {
return provider.createAndPublishEndpoint(address, implementor);
}
}

throw new RuntimeException(
"No non-CXF JAW-WS provider found in classpath.");
}

ответил(а) 2021-01-25T14:45:31+03:00 6 месяцев назад
45

Endpoint.publish() ищет Provider как это:

Получите Iterator из ServiceLoader и возьмите первый элемент этого итератора Только если Iterator пуст, приступайте к резервному разрешению Provider

В моем случае в итераторе всегда есть что-то, поэтому Endpoint.publish() не является опцией.

Я также попробовал решение, предложенное @VGR, но он не работает, как у меня не был никакой другой Provider реализации имеющейся в ServiceProvider (и по умолчанию, поставляемый с JDK, не доступен через ServiceProvider).

Я попытался добавить другого провайдера, использующего Metro RT:

<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>rt</artifactId>
<version>2.3.0</version>
<scope>test</scope>
</dependency>

Но тогда у меня другая проблема:


java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to javax.xml.soap.SOAPElement

что, вероятно, было вызвано тем, что и Metro, и старая версия CXF находились в пути к классам.

То, что сработало для меня, не очень элегантно: я просто создал экземпляр com.sun.xml.internal.ws.spi.ProviderImpl по умолчанию (используя рефлексию) и добавил пару свойств системы для проблем вокруг работы, на которые я наткнулся:

System.setProperty("javax.xml.bind.JAXBContext", "com.sun.xml.internal.bind.v2.ContextFactory");
Endpoint endpoint = createWSProvider().createAndPublishEndpoint(url, impl);

а также

private static Provider createWSProvider() {
final String className = "com.sun.xml.internal.ws.spi.ProviderImpl";

Class<?> aClass;
try {
aClass = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}

try {
return (Provider) aClass.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}

Я использовал отражение, потому что com.sun.xml.internal.ws.spi.ProviderImpl - это внутренний класс реализации, и javac откажется его видеть. javax.xml.bind.JAXBContext настроен на работу вокруг java.lang.ClassCastException: blablabla$JaxbAccessorF_bin cannot be cast to com.sun.xml.bind.v2.runtime.reflect.Accessor

ответил(а) 2021-01-25T14:45:31+03:00 6 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема