JAX-WS = Когда Apache CXF установлен, он "крадет" стандартную реализацию JDK JAX-WS по умолчанию, как ее решить?
У меня странная проблема.
-
Используя wsimport, я сгенерировал код JAX-WS из WSDL (в специальном проекте java-проекта eclipse). Это отлично работает в JDK6 без каких-либо внешних зависимостей (работает в Eclipse)
-
У меня есть второй проект, в котором я когда-то использовал Apache CXF. Если я копирую код, описанный в 1.) в этот проект, вдруг JDK не выполняет JAX-WS (файлы, которые я сгенерировал), а Apache CXF.
Как я могу запретить Apache CXF запускать JAX-WS. (Проблема в том, что CXF не работает для запуска кода...). Я также полностью не понимаю, как Apache CXF обнаруживает эти классы. Я их не зарегистрировал?
Большое спасибо!
Маркус
Ответы
Ответ 1
Apache CXF (cxf-rt-frontend-jaxws-*.jar
, если быть точным) регистрируется как поставщик JAX-WS в JVM. Внутри вышеупомянутого JAR есть файл с именем: /META-INF/services/javax.xml.ws.spi.Provider
со следующим содержимым:
org.apache.cxf.jaxws.spi.ProviderImpl
Если вы теперь посмотрите на метод javax.xml.ws.spi.FactoryFinder#find
, вы обнаружите, что JDK ищет CLASSPATH для наличия файла javax.xml.ws.spi.Provider
и возвращается к реализации Sun по умолчанию, если она недоступна. Таким образом, у вас есть два варианта принудительного возврата:
-
либо удалите cxf-rt-frontend-jaxws-*.jar
из CLASSPATH
-
или переопределить javax.xml.ws.spi.Provider
файл, предоставленный CXF, чтобы указать на резервное местоположение
Второй вариант на самом деле немного проще. Просто создайте:
/src/main/resources/META-INF/services/javax.xml.ws.spi.Provider
(при условии, что вы используете Maven) со следующим содержимым:
org.apache.cxf.jaxws.spi.ProviderImpl
Это он, протестированный с помощью javax.xml.ws.Endpoint#publish
.
Ответ 2
Для реализации по умолчанию put:
com.sun.xml.internal.ws.spi.ProviderImpl
внутри/src/main/resources/META-INF/services/javax.xml.ws.spi.Provider
Ответ 3
Я попробовал другой, и я просто не мог заставить его работать вообще, поэтому для установки CXF, если он не был установлен в CXF, я просто переопределяю делегат внутри службы.
try {
loc = this.getClass().getResource(wsdlResource);
QName qName = new QName( wsTargetNamespace, wsName );
service = new YourWS(loc, qName);
Field delegateField = Service.class.getDeclaredField("delegate"); //ALLOW CXF SPECIFIC SERVICE DELEGATE ONLY!
delegateField.setAccessible(true);
ServiceDelegate previousDelegate = (ServiceDelegate) delegateField.get(service);
if (!previousDelegate.getClass().getName().contains("cxf")) {
ServiceDelegate serviceDelegate = ((Provider) Class.forName("org.apache.cxf.jaxws.spi.ProviderImpl").newInstance())
.createServiceDelegate(loc, qName, service.getClass());
log.info("The " + getClass().getSimpleName() + " delegate is changed from " + "[" + previousDelegate + "] to [" +
serviceDelegate +
"]");
delegateField.set(service, serviceDelegate);
}
port = service.getYourWSSoap();
Ответ 4
Стандартные механизмы поиска не работают хорошо в OSGi (*).
Есть два способа заставить службу забрать реализацию CXF javax.xml.ws.spi.Provider
:
-
подход установки delegate
отражением, данным в EpicPandaForce, отвечает на этот вопрос (fooobar.com/questions/116825/...)
-
вызов нижнего уровня JaxWsProxyFactoryBean
; это, как представляется, позволяет избежать всех вызовов javax.xml.ws.spi.FactoryFinder
, включенных в Java, который является корнем проблемы.
Вот пример последнего, для менее бесстрашных кодеров, которые предпочитают не отражать меняющиеся частные поля:
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getClientFactoryBean().getServiceFactory().setWsdlURL(WinRmService.WSDL_LOCATION);
factory.setServiceName(WinRmService.SERVICE);
factory.setEndpointName(WinRmService.WinRmPort);
// factory.setFeatures(...); // if required
Service winrm = factory.create(WinRm.class);
Client client = ClientProxy.getClient(winrm);
Несколько примечаний:
-
Передача URL
, как указано выше, а не простой factory.setWsdlURL(String)
может потребоваться, если WSDL является ресурсом в пути к классам (избегайте неразрешимых URL-адресов bundle://...
для элементов pathpath)
-
Вам могут потребоваться дополнительные пакеты для функций (таких как адресация)
(*) Что касается того, почему механизмы поиска не работают в большинстве контейнеров OSGi, ознакомьтесь с этим немного неприятным в Oracle Java FactoryFinder
:
private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "com.sun.org.glassfish.hk2.osgiresourcelocator.ServiceLoader";
private static boolean isOsgi() {
try {
Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
return true;
} catch (ClassNotFoundException ignored) {
}
return false;
}
OSGi = Glassfish? Фиши действительно!
Ответ 5
У меня была аналогичная проблема. В моем случае мне пришлось использовать org.apache.cxf.jaxws.spi.ProviderImpl
для материала JAX-WS (создание конечных точек webservice и т.д.) И com.sun.xml.internal.ws.spi.ProviderImpl
для публикации конечных точек на com.sun.net.httpserver.HttpsServer
.
Мне удалось решить эту проблему, создав моего собственного провайдера, который расширяет javax.xml.ws.spi.Provider
и использует его вместо стандартного.
package provider;
import java.net.URL;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.ws.Endpoint;
import javax.xml.ws.EndpointReference;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.spi.Provider;
import javax.xml.ws.spi.ServiceDelegate;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import org.w3c.dom.Element;
public class MyProvider extends Provider
{
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public ServiceDelegate createServiceDelegate(URL wsdlDocumentLocation, QName serviceName, Class serviceClass)
{
try {
return ((Provider) Class.forName("org.apache.cxf.jaxws.spi.ProviderImpl").newInstance()).createServiceDelegate(wsdlDocumentLocation, serviceName, serviceClass.getClass());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public Endpoint createEndpoint(String bindingId, Object implementor)
{
try {
return ((Provider) Class.forName("com.sun.xml.internal.ws.spi.ProviderImpl").newInstance()).createEndpoint(bindingId, implementor);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public Endpoint createAndPublishEndpoint(String address, Object implementor)
{
try {
return ((Provider) Class.forName("com.sun.xml.internal.ws.spi.ProviderImpl").newInstance()).createAndPublishEndpoint(address, implementor);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public EndpointReference readEndpointReference(Source eprInfoset)
{
try {
return ((Provider) Class.forName("org.apache.cxf.jaxws.spi.ProviderImpl").newInstance()).readEndpointReference(eprInfoset);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public <T> T getPort(EndpointReference endpointReference, Class<T> serviceEndpointInterface, WebServiceFeature... features)
{
try {
return ((Provider) Class.forName("org.apache.cxf.jaxws.spi.ProviderImpl").newInstance()).getPort(endpointReference, serviceEndpointInterface, features);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public W3CEndpointReference createW3CEndpointReference(String address, QName serviceName, QName portName, List<Element> metadata, String wsdlDocumentLocation, List<Element> referenceParameters)
{
try {
return ((Provider) Class.forName("org.apache.cxf.jaxws.spi.ProviderImpl").newInstance()).createW3CEndpointReference(address, serviceName, portName, metadata, wsdlDocumentLocation,
referenceParameters);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Затем просто создайте:
/src/main/resources/META-INF/services/javax.xml.ws.spi.Provider
(при условии, что вы используете Maven) со следующим содержимым:
package.MyProvider