JAX-WS客户端:访问本地WSDL的正确途径是什么?

问题是我需要从我提供的文件构build一个Web服务客户端。 我已经将这个文件存储在本地文件系统中,并且当我将WSDL文件保存在正确的文件系统文件夹中时,一切都很好。 当我将其部署到服务器或从文件系统文件夹中删除WSDL时,代理无法findWSDL并产生错误。 我search了网页,我发现了以下文章,但我无法使其工作:
JAX-WS从jar中加载WSDL
http://www.java.net/forum/topic/glassfish/metro-and-jaxb/client-jar-cant-find-local-wsdl-0
http://blog.vinodsingh.com/2008/12/locally-packaged-wsdl.html

我正在使用NetBeans 6.1(这是一个遗留的应用程序,我要用这个新的Web服务客户端进行更新)。 以下是JAX-WS代理类:

@WebServiceClient(name = "SOAService", targetNamespace = "http://soaservice.eci.ibm.com/", wsdlLocation = "file:/C:/local/path/to/wsdl/SOAService.wsdl") public class SOAService extends Service { private final static URL SOASERVICE_WSDL_LOCATION; private final static Logger logger = Logger.getLogger(com.ibm.eci.soaservice.SOAService.class.getName()); static { URL url = null; try { URL baseUrl; baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource("."); url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl"); } catch (MalformedURLException e) { logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file"); logger.warning(e.getMessage()); } SOASERVICE_WSDL_LOCATION = url; } public SOAService(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } public SOAService() { super(SOASERVICE_WSDL_LOCATION, new QName("http://soaservice.eci.ibm.com/", "SOAService")); } /** * @return * returns SOAServiceSoap */ @WebEndpoint(name = "SOAServiceSOAP") public SOAServiceSoap getSOAServiceSOAP() { return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class); } /** * @param features * A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values. * @return * returns SOAServiceSoap */ @WebEndpoint(name = "SOAServiceSOAP") public SOAServiceSoap getSOAServiceSOAP(WebServiceFeature... features) { return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class, features); } } 

这是我使用代理的代码:

  WebServiceClient annotation = SOAService.class.getAnnotation(WebServiceClient.class); // trying to replicate proxy settings URL baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource("");//note : proxy uses "." URL url = new URL(baseUrl, "/WEB-INF/wsdl/client/SOAService.wsdl"); //URL wsdlUrl = this.getClass().getResource("/META-INF/wsdl/SOAService.wsdl"); SOAService serviceObj = new SOAService(url, new QName(annotation.targetNamespace(), annotation.name())); proxy = serviceObj.getSOAServiceSOAP(); /* baseUrl; //classes\com\ibm\eci\soaservice //URL url = new URL(baseUrl, "../../../../wsdl/SOAService.wsdl"); proxy = new SOAService().getSOAServiceSOAP();*/ //updating service endpoint Map<String, Object> ctxt = ((BindingProvider)proxy ).getRequestContext(); ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192); ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, WebServiceUrl); 

NetBeans将WSDL的副本放在web-inf / wsdl / client / SOAService中 ,所以我不想将它添加到META-INF中 。 服务类在WEB-INF / classes / com / ibm / eci / soaservice /中 ,baseurlvariables包含文件系统的完整path(c:\ path \ to \ the \ project … \ soaservice)。 上面的代码引发了这个错误:

javax.xml.ws.WebServiceException:无法在以下位置访问WSDL:file:/WEB-INF/wsdl/client/SOAService.wsdl。 它失败了:\ WEB-INF \ wsdl \ client \ SOAService.wsdl(找不到path)

那么,首先,我应该更新代理类的wsdlocation吗? 那么如何告诉WEB-INF / classes / com / ibm / eci / soaservice中的SOAService类来search\ WEB-INF \ wsdl \ client \ SOAService.wsdl中的WSDL呢?

编辑 :我find了这个其他的链接 – http://jianmingli.com/wp/?cat=41 ,这就是说把WSDL放入类path。 我很惭愧地问:我如何把它放到Web应用程序类path中?

最好的select是使用jax-ws-catalog.xml

编译本地WSDL文件时,覆盖WSDL位置并将其设置为类似的内容

 HTTP://localhost/wsdl/SOAService.wsdl

不要担心,这只是一个URI而不是一个URL,这意味着您不必在该地址提供WSDL。
您可以通过将wsdlocation选项传递给wsdl来执行java编译器。

这样做会改变你的代理代码

 static { URL url = null; try { URL baseUrl; baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource("."); url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl"); } catch (MalformedURLException e) { logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file"); logger.warning(e.getMessage()); } SOASERVICE_WSDL_LOCATION = url; } 

 static { URL url = null; try { URL baseUrl; baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource("."); url = new URL(baseUrl, "http://localhost/wsdl/SOAService.wsdl"); } catch (MalformedURLException e) { logger.warning("Failed to create URL for the wsdl Location: 'http://localhost/wsdl/SOAService.wsdl', retrying as a local file"); logger.warning(e.getMessage()); } SOASERVICE_WSDL_LOCATION = url; } 

通知文件://在URL构造函数中更改为http://。

现在进入jax-ws-catalog.xml。 没有jax-ws-catalog.xml,jax-ws确实会尝试从位置加载WSDL

  HTTP://localhost/wsdl/SOAService.wsdl

并失败,因为没有这样的WSDL可用。

但是使用jax-ws-catalog.xml,无论何时试图访问WSDL @@@,您都可以将jax-wsredirect到本地打包的WSDL,

  HTTP://localhost/wsdl/SOAService.wsdl

这里是jax-ws-catalog.xml

 <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system"> <system systemId="http://localhost/wsdl/SOAService.wsdl" uri="wsdl/SOAService.wsdl"/> </catalog> 

你正在做的是告诉jax-ws,当它需要加载WSDL时

  HTTP://localhost/wsdl/SOAService.wsdl

它应该从本地pathwsdl / SOAService.wsdl加载它。

现在应该在哪里放置wsdl / SOAService.wsdl和jax-ws-catalog.xml? 这是百万美元的问题不是吗?
它应该在应用程序jar的META-INF目录下。

所以这样的事情

 ABCD.jar  
 | __ META-INF    
     | __ jax-ws-catalog.xml  
     | __ wsdl  
         | __ SOAService.wsdl  

这样,您甚至不必重写访问代理的客户端中的URL。 WSDL是从你的JAR中选取的,并且避免在代码中使用硬编码的文件系统path。

更多关于jax-ws-catalog.xml的信息http://jax-ws.java.net/nonav/2.1.2m1/docs/catalog-support.html

希望有所帮助

我们成功采用的另一种方法是使用wsimport(从Ant,作为Ant任务)生成WS客户端代理代码,并指定wsdlLocation属性。

 <wsimport debug="true" keep="true" verbose="false" target="2.1" sourcedestdir="${generated.client}" wsdl="${src}${wsdl.file}" wsdlLocation="${wsdl.file}"> </wsimport> 

由于我们为具有多个WSDL的项目运行此脚本,因此脚本将dynamicparsing$(wsdl.file)值,该值设置为/META-INF/wsdl/YourWebServiceName.wsdl相对于JavaSource位置(或/ src,取决于你如何设置项目)在构build过程中,WSDL和XSD文件被复制到这个位置并打包在JAR文件中(类似于上面Bhasakar描述的解决scheme)

 MyApp.jar |__META-INF |__wsdl |__YourWebServiceName.wsdl |__YourWebServiceName_schema1.xsd |__YourWebServiceName_schmea2.xsd 

注意:确保WSDL文件对所有导入的XSD使用相对的refrerence而不是http URL:

  <types> <xsd:schema> <xsd:import namespace="http://valueobject.common.services.xyz.com/" schemaLocation="YourWebService_schema1.xsd"/> </xsd:schema> <xsd:schema> <xsd:import namespace="http://exceptions.util.xyz.com/" schemaLocation="YourWebService_schema2.xsd"/> </xsd:schema> </types> 

生成的代码中,我们发现这个:

 /** * This class was generated by the JAX-WS RI. * JAX-WS RI 2.2-b05- * Generated source version: 2.1 * */ @WebServiceClient(name = "YourService", targetNamespace = "http://test.webservice.services.xyz.com/", wsdlLocation = "/META-INF/wsdl/YourService.wsdl") public class YourService_Service extends Service { private final static URL YOURWEBSERVICE_WSDL_LOCATION; private final static WebServiceException YOURWEBSERVICE_EXCEPTION; private final static QName YOURWEBSERVICE_QNAME = new QName("http://test.webservice.services.xyz.com/", "YourService"); static { YOURWEBSERVICE_WSDL_LOCATION = com.xyz.services.webservice.test.YourService_Service.class.getResource("/META-INF/wsdl/YourService.wsdl"); WebServiceException e = null; if (YOURWEBSERVICE_WSDL_LOCATION == null) { e = new WebServiceException("Cannot find '/META-INF/wsdl/YourService.wsdl' wsdl. Place the resource correctly in the classpath."); } YOURWEBSERVICE_EXCEPTION = e; } public YourService_Service() { super(__getWsdlLocation(), YOURWEBSERVICE_QNAME); } public YourService_Service(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } /** * * @return * returns YourService */ @WebEndpoint(name = "YourServicePort") public YourService getYourServicePort() { return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class); } /** * * @param features * A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy. Supported features not in the <code>features</code> parameter will have their default values. * @return * returns YourService */ @WebEndpoint(name = "YourServicePort") public YourService getYourServicePort(WebServiceFeature... features) { return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class, features); } private static URL __getWsdlLocation() { if (YOURWEBSERVICE_EXCEPTION!= null) { throw YOURWEBSERVICE_EXCEPTION; } return YOURWEBSERVICE_WSDL_LOCATION; } } 

也许这也可能有帮助。 这只是一个不使用“目录”方法的不同方法。

对于那些使用Spring的人来说,只需使用classpath-protocol引用任何classpath资源即可。 所以在wsdlLocation的情况下,这成为:

 <wsdlLocation>classpath:META-INF/webservice.wsdl</wsdlLocation> 

请注意,这不是标准的Java行为。 另见: http : //docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html

有这里描述的完全相同的问题。 不pipe我做了什么,按照上面的例子,改变我的WSDL文件的位置(在我们的例子中是从Web服务器),它仍然引用embedded在服务器进程的源代码树中的原始位置。

经过几个小时试图debugging这个,我注意到,exception总是从完全相同的行(在我的情况41)抛出。 最后,今天早上,我决定把我的客户端代码发送给我们的贸易伙伴,这样他们至less可以理解代码的外观,但也许是build立自己的代码。 令我震惊恐惧的是,我在客户端源代码树中发现了一堆与我的.java文件混合的类文件。 多么奇怪! 我怀疑这些是JAX-WS客户端构build器工具的副产品。

一旦我将这些愚蠢的.class文件剔除,并执行完整的清理和重build客户端代码,一切都完美无缺! Redonculous!

YMMV,安德鲁

感谢Bhaskar Karambelkar的回答,详细解释并解决了我的问题。 但是,我也想简单地说一个急着解决问题的人的答案

  1. 使您的wsdl本地位置参考为wsdlLocation= "http://localhost/wsdl/yourwsdlname.wsdl"
  2. 在src下创build一个META-INF文件夹。 把你的wsdl文件放在META-INF下的一个文件夹中,比如说META-INF / wsdl
  3. 在META-INF下创build一个xml文件jax-ws-catalog.xml,如下所示

    <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system"> <system systemId="http://localhost/wsdl/yourwsdlname.wsdl" uri="wsdl/yourwsdlname.wsdl" /> </catalog>

现在打包你的jar。 没有更多的参考本地目录,它的所有打包和引用