【问题标题】:JAX-WS = When Apache CXF is installed it "steals" default JDK JAX-WS implementation, how to solve?JAX-WS = 安装 Apache CXF 时“窃取”默认的 JDK JAX-WS 实现,如何解决?
【发布时间】:2011-09-15 21:35:06
【问题描述】:

我有一个奇怪的问题。

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

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

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

非常感谢! 马库斯

【问题讨论】:

  • 老实说,我最感兴趣的是为什么 CXF 无法运行代码。 CXF 完全符合 JAX-WS,因此它应该是 in-jdk jax-ws 实现的替代品。错误报告?

标签: java web-services jax-ws cxf


【解决方案1】:

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

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

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

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

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

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

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

org.apache.cxf.jaxws.spi.ProviderImpl

就是这样,用javax.xml.ws.Endpoint#publish测试过。

【讨论】:

  • 您好 Tomasz,非常感谢您的慷慨和宝贵的帮助。这是非常赞赏!!! (以防万一您知道这一点:我可以使用 JDK 或 CXF 是否正确?所以没有选择告诉 CXF 保持我的一些类/包不受影响,并同时使用 somewho...)谢谢。
  • 您的第二个选项告诉您如何配置 JAX-WS 以使用 CXF,如果我希望 JAX-WS 不使用 CXF 怎么办? META-INF/javax.xml.ws.spi.Provider 文件和 cxf-rt-frontend-jaxws jar 都不在我的类路径上,所以我不知道如何防止 CXF 成为 JAX-WS 提供者
  • 我已经尝试让 CXF 在 WebLogic 11g 10.3.6 上运行几天了 - 这是关键。似乎 META-INF 中的 Provider 文件(在 EAR 模块中)成功了。我快要掉眼泪了。谢谢:-)
  • 覆盖 META-INF/services/javax.xml.ws.spi.Provider 并不总是适用于所有服务器,例如 Websphere,它会强制您自己实现。
【解决方案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();
    

    【讨论】:

    • 我尝试了所有答案,但对于我的应用程序来说,这是解决我的问题的唯一方法,谢谢 :)
    • (相同答案的相关问题:stackoverflow.com/a/31892206/2413303
    • Service.classjavax.xml.ws.Service
    【解决方案4】:

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

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

    • EpicPandaForce 对这个问题的回答 (https://stackoverflow.com/a/31892305/109079) 中给出的通过反射设置delegate 的方法

    • 调用低级JaxWsProxyFactoryBean;这似乎避免了对 Java 中包含的 javax.xml.ws.spi.FactoryFinder 的所有调用,这是问题的根源

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

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

    几点说明:

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

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


    (*) 至于为什么查找机制在大多数 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 = 玻璃鱼?确实很鱼!

    【讨论】:

      【解决方案5】:

      我遇到了类似的问题。在我的情况下,我必须使用 org.apache.cxf.jaxws.spi.ProviderImpl 来处理 JAX-WS 的东西(创建 Web 服务端点等),并使用 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-30
        • 1970-01-01
        相关资源
        最近更新 更多