Java JAX-WS = 当安装 Apache CXF 时它“窃取”了默认的 JDK JAX-WS 实现,如何解决?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6364333/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 05:21:23  来源:igfitidea点击:

JAX-WS = When Apache CXF is installed it "steals" default JDK JAX-WS implementation, how to solve?

javaweb-servicesjax-wscxf

提问by Markus

I have a strange problem.

我有一个奇怪的问题。

  1. Using wsimport I generated als JAX-WS Code from a WSDL (in a dedicated eclipse java project). This works fine in JDK6 without any external dependencies (running in Eclipse)

  2. I have second project where I once used Apache CXF. If I copy the Code described in 1.) into this project, suddenly not the JDK executes the JAX-WS stuff (files I generated), but rather Apache CXF.

  1. 使用 wsimport,我从 WSDL(在一个专用的 eclipse java 项目中)生成了 als JAX-WS 代码。这在 JDK6 中运行良好,没有任何外部依赖(在 Eclipse 中运行)

  2. 我有第二个项目,我曾经在其中使用过 Apache CXF。如果我将 1.) 中描述的代码复制到这个项目中,突然不是 JDK 执行 JAX-WS 东西(我生成的文件),而是 Apache CXF。

How can I prevent Apache CXF "running" the JAX-WS stuff. (Problem is, CXF Fails to run the code...). I also completely do not understand how Apache CXF discovers these classes. I did not register them anywere?

如何防止 Apache CXF“运行”JAX-WS 的东西。(问题是,CXF 无法运行代码...)。我也完全不明白 Apache CXF 如何发现这些类。我没有注册他们吗?

Thank you very much! Markus

非常感谢!马库斯

采纳答案by Tomasz Nurkiewicz

Apache CXF (cxf-rt-frontend-jaxws-*.jarto be precise) registers itself as a JAX-WS provider in the JVM. Inside the aforementioned JAR there is a file named: /META-INF/services/javax.xml.ws.spi.Providerwith the following contents:

Apache CXF(cxf-rt-frontend-jaxws-*.jar准确地说)将自己注册为 JVM 中的 JAX-WS 提供者。在前面提到的 JAR 中有一个名为:的文件/META-INF/services/javax.xml.ws.spi.Provider,内容如下:

org.apache.cxf.jaxws.spi.ProviderImpl

If you now look at javax.xml.ws.spi.FactoryFinder#findmethod you will discover that JDK searches the CLASSPATH for the presence of javax.xml.ws.spi.Providerfile and falls back to default Sun implementation if not available. So you have two options to force fallback:

如果您现在查看javax.xml.ws.spi.FactoryFinder#find方法,您会发现 JDK 在 CLASSPATH 中搜索javax.xml.ws.spi.Provider文件是否存在,如果不可用则回退到默认的 Sun 实现。因此,您有两个选项可以强制回退:

  • either remove cxf-rt-frontend-jaxws-*.jarfrom CLASSPATH

  • or override javax.xml.ws.spi.Providerfile provided by CXF to point to fallback location

  • cxf-rt-frontend-jaxws-*.jar从 CLASSPATH 中删除

  • 或覆盖javax.xml.ws.spi.ProviderCXF 提供的文件以指向后备位置

The second option is actually a bit easier. Simply create:

第二种选择实际上更容易一些。只需创建:

/src/main/resources/META-INF/services/javax.xml.ws.spi.Provider

file (assuming you are using Maven) with the following contents:

文件(假设您使用的是 Maven),其中包含以下内容:

org.apache.cxf.jaxws.spi.ProviderImpl

That's it, tested with javax.xml.ws.Endpoint#publish.

就是这样,用javax.xml.ws.Endpoint#publish.

回答by Ken Larson

For the default implementation put:

对于默认实现放置:

com.sun.xml.internal.ws.spi.ProviderImpl

inside /src/main/resources/META-INF/services/javax.xml.ws.spi.Provider

里面 /src/main/resources/META-INF/services/javax.xml.ws.spi.Provider

回答by EpicPandaForce

I tried the other and I just couldn't make it work at all, so to set CXF if it was not set to CXF, I just override the delegate inside the service.

我尝试了另一个,但我根本无法使其工作,因此如果未设置为 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();

回答by Partly Cloudy

The standard finding mechanisms don't seem to work nicely in OSGi (*).

标准查找机制在 OSGi (*) 中似乎不能很好地工作。

There are two ways I've gotten to work forcing the service to pick up the CXF implementation of javax.xml.ws.spi.Provider:

我有两种方法可以强制服务获取 CXF 实现javax.xml.ws.spi.Provider

  • the approach of setting delegateby reflection given in EpicPandaForce's answer to this question (https://stackoverflow.com/a/31892305/109079)

  • calling the lower-level JaxWsProxyFactoryBean; this seems to avoid all calls to the javax.xml.ws.spi.FactoryFinderincluded with Java which is the root of the problem

  • delegateEpicPandaForce 对此问题的回答中给出的反射设置方法( https://stackoverflow.com/a/31892305/109079)

  • 调用下层JaxWsProxyFactoryBean;这似乎避免了对javax.xml.ws.spi.FactoryFinderJava 包含的所有调用,这是问题的根源

Here is an example of the latter, for less intrepid coders who prefer not reflectively changing private fields:

以下是后者的示例,适用于不喜欢反思性地更改私有字段的不那么勇敢的编码人员:

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);

A couple of notes:

一些注意事项:

  • Passing a URLas above, rather than the simpler factory.setWsdlURL(String)may be needed if the WSDL is a resource on the classpath (avoid unresolvable bundle://...URLs for classpath items)

  • You may need additional bundles for features (such as addressing)

  • 如果 WSDL 是类路径上的资源,则URL如上所述传递 a ,而不是更简单的factory.setWsdlURL(String)可能需要(避免bundle://...类路径项的无法解析的URL)

  • 您可能需要额外的功能包(例如寻址)



(*) As for why the finding mechanisms don't work in most OSGi containers, check out this little bit of nasty in Oracle Java's FactoryFinder:

(*)至于为什么查找机制在大多数 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? Fishy indeed!

OSGi = 玻璃鱼?确实很腥!

回答by Monika Bozhinova

I had a similar problem. In my case I had to use org.apache.cxf.jaxws.spi.ProviderImplfor JAX-WS stuff (creating webservice endpoints etc.) and com.sun.xml.internal.ws.spi.ProviderImplfor publishing endpoints on com.sun.net.httpserver.HttpsServer.

我有一个类似的问题。在我的例子中,我不得不使用org.apache.cxf.jaxws.spi.ProviderImplJAX-WS 的东西(创建 webservice 端点等)和com.sun.xml.internal.ws.spi.ProviderImplcom.sun.net.httpserver.HttpsServer.

I managed to solve this by creating my own provider which extends javax.xml.ws.spi.Providerand using it instead of the default one.

我设法通过创建我自己的提供程序来解决这个问题,该提供程序扩展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;
}

}

Then simply create:

然后简单地创建:

/src/main/resources/META-INF/services/javax.xml.ws.spi.Provider

file (assuming you are using Maven) with the following contents:

文件(假设您使用的是 Maven),其中包含以下内容:

package.MyProvider