JAX-WS从jar中加载WSDL

我正在写一个胖客户端,使用SOAP服务的一些function(错误报告等)

我有JAX-WS工作正常,但默认情况下(至less在NetBeans中)它每次服务初始化时从远程服务器获取WSDL。 我期望这有助于提供一些版本支持等,但这不是我想要的。

我已经将wsdllocation arg添加到wsimport,将生成的类指向本地资源。 以下片段是来自ApplicationService.java的WSDL资源的URL加载。

 baseUrl = net.example.ApplicationService.class.getResource("."); url = new URL(baseUrl, "service.wsdl"); 

我很确定,在net / example / resources包中,指向存储在jar中的资源应该没有问题,并且jar本身也是按照预期构build的。 然而,该服务将不会加载…具体来说,当我调用ApplicationService.getPort()时,我得到一个NullPointerException;

这可能吗? 还是只是一个疯狂的追逐?

是的,这是绝对有可能的,正如我在通过javax.xml.ws.EndpointReference(一个WS-A相关的类)创build客户端时所做的那样。 我已经添加了WSDL的类path引用到WS-A EndPointReference和JAX-WS的Metro实现加载它就好了。 无论从WS-A EndPointReference或文件或http URL加载WSDL,您的JAX-WS实现都应使用相同的WSDLparsing代码,因为您正在解决的所有问题都是parsingURL。

对你来说最好的办法可能是做如下的事情:

 URL wsdlUrl = MyClass.class.getResource( "/class/path/to/wsdl/yourWSDL.wsdl"); Service yourService= Service.create( wsdlUrl, ...); 

其中…代表WSDL内的WSDL服务的QName。 现在需要记住的重要一点是您的WSDL需要完整且有效。 这意味着如果您的WSDL导入XSD文件或其他WSDL,则URL必须是正确的。 如果将导入的WSDL和XSD与WSDL文件包含在同一个JAR中,则应该使用相对URL来导入,并将所有导入保留在同一个JAR文件中。 JAR URL处理程序不会将相对URL视为相对于类path的相对关系,而是相对于JAR文件中的相对关系,因此,除非您实现自定义URL处理程序和自己的前缀,否则不能在WSDL中跨JAR运行导入基于类path的导入parsing。 如果您的WSDL导入了外部资源,那没问题,但是如果这些资源移动了,您将自行维护维护问题。 即使在你的类path中使用WSDL的静态拷贝也违背了WSDL,Web服务和JAX-WS的精神,但是有时候这是必要的。

最后,如果您embedded了一个静态的WSDL,我build议您至less使服务端点可configuration用于testing和部署目的。 重新configurationWeb服务客户机端点的代码如下所示:

  YourClientInterface client = yourService.getPort( new QName("...", "..."), YourClientInterface.class); BindingProvider bp = (BindingProvider) client; bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://localhost:8080/yourServiceEndpoint"); 

至less对于最近的JAX-WS,您不需要执行任何模式目录或编程wsdl位置设置。 如果将WSDL放入JAR,然后将wsimport wsdlLocation设置为JAR中WSDL的相对资源path。 也就是说,JAX-WS使用Java的内置Class.getResource来加载WSDL。

如果你使用Maven它的东西是这样的:

  <plugin> <groupId>org.jvnet.jax-ws-commons</groupId> <artifactId>jaxws-maven-plugin</artifactId> <version>2.3</version> <executions> <execution> <goals> <goal>wsimport</goal> </goals> <!-- Following configuration will invoke wsimport once for each wsdl. --> <configuration> <!--- VERY IMPORTANT THAT THE PATH START WITH '/' --> <wsdlLocation>/com/adamgent/ws/blah.wsdl</wsdlLocation> <wsdlDirectory>${basedir}/src/main/resources/com/adamgent/ws</wsdlDirectory> <wsdlFiles><wsdlFile>blah.wsdl</wsdlFile></wsdlFiles> </configuration> </execution> </executions> </plugin> 

对于上面的示例,您将在此处将使用Maven项目布局的WSDL放置在src/main/resources/com/adamgent/ws

确保WSDL进入Maven的JAR,如下所示:

 <build> <resources> <resource> <directory>src/main/resources</directory> </resource> </resources> .... 

现在你的wsimport生成的代码和WSDL都在一个独立的JAR中。 要使用该服务,您不必设置WSDL位置,就像下面这样简单:

 BlahService myService = new BlayService_Service().getBlahServicePort(); 

把这个映射到ANT的wsimport应该是微不足道的。

也许有点迟,但是我发现了一个非常简单的解决scheme,它解决了这个问题,但是这涉及到Service类的生成代码的改变:

如果在Service类中有以下行

 baseUrl = net.example.ApplicationService.class.getResource("."); 

更改为

 baseUrl = net.example.ApplicationService.class.getResource(""); 

即使在JAR中打包的WSDL也能正常工作。 在这两种情况下都不确定getResource()的确切行为,但是到目前为止,在多个操作系统和Java版本中,我没有遇到这种方法的任何问题。

您所描述的是JAX-WS中的错误: JAX_WS-888 – parsing自定义wsdlLocation的URL的错误代码 。

这是固定的V2.2,所以只要设置wsdlLocation ,你写,现在应该工作。

如果你的类path有“。” 那么Class.getResource(“。”)将返回执行java命令的目录的URL。 否则,它将返回一个null。 相应地调整wsdlocation。

另一个答案是乌斯特新服务(wsdllocation,servicename); 获取服务对象。

这就是我解决问题的方法。

我偶然发现了同样的问题。 JAXWS生成的客户端代码使用MyService.class.getResource(".")技巧来加载wsdl文件…但是在testing之后,如果类文件位于filesytem目录中,似乎只能工作。 如果类文件在JAR中,则该调用将为URL返回null。

这听起来像是一个JDK中的错误,因为如果你像这样构build你的URL:

 final URL url = new URL( MyService.class.getResource( MyService.class.getSimpleName() + ".class"), "myservice.wsdl"); 

那么它也可以工作,如果类和wsdl捆绑在一个jar子里。

我想大多数人会实际上捆绑在一个jar子里!

在构build客户端jar之前,我replace了WSDL位置。

  1. 将WSDL复制到类目录。
  2. 使用classpath将Service类replace成WSDL。
  3. build立客户端存根。
  4. jar子存根。
 <copy todir="@{dest-dir}/@{dir-package}" verbose="@{verbose}"> <fileset dir="@{dir-wsdl}" includes="*.wsdl,*.xsd" /> </copy> <echo message="Replacing Service to point to correct WSDL path..." /> <replaceregexp match="new URL(.*)" replace='Class.class.getResource("@{name-wsdl}");' flags="gm"> <fileset dir="@{source-dest-dir}"> <include name="@{dir-package}/*Service.java" /> </fileset> </replaceregexp> <replaceregexp match="catch (.*)" replace='catch (Exception ex) {' flags="gm"> <fileset dir="@{source-dest-dir}"> <include name="@{dir-package}/*Service.java" /> </fileset> </replaceregexp> 

这是我的解决方法。

我从jar中解压WSDL,并把它写到jar附近的文件中:

 File wsdl = new File("../lib/service.wsdl"); InputStream source = getClass().getResource("resources/service.wsdl").openStream(); FileOutputStream out = new FileOutputStream(wsdl); byte[] buffer = new byte[512]; int read; while((read = source.read(buffer)) >= 0) { out.write(buffer, 0, read); } 

然后将服务类指向file:../lib/service.wsdl

这工作,但我会很感激,如果任何人都可以给我一个更优雅的解决scheme。

这是一个适合我的方法(特别是通过httphttps )。 Oracle JDK 1.8.0_51的JAX-WS使用Apache CXF 3.1.1创build的类。

请注意,远程WSDL只能在第一次调用时获得。 根据使用模式(长时间运行的程序),这可能是完全可以接受的。

基础:

  • 从远程主机下载WSDL并存储为文件: wget --output-document=wsdl_raw.xml $WSDL_URL
  • 您可能需要xmllint --format wsdl_raw.xml > wsdl.xml格式化xmllint --format wsdl_raw.xml > wsdl.xml以获得更好的格式
  • 使用命令行工具生成客户端类: ./cxf/bin/wsdl2java -d output/ -client -validate wsdl.xml并导入到您的项目

validationWSDL文件中是否存在httphttps的服务定义。 在我的情况下,提供商没有一个用于https (但确实接受httpsstream量),我不得不手动添加它。 WSDL中应该包含以下内容:

  <wsdl:service name="fooservice"> <wsdl:port binding="tns:fooserviceSoapBinding" name="FooBarWebServicePort"> <soap:address location="http://ws.example.com/a/b/FooBarWebService"/> </wsdl:port> </wsdl:service> <wsdl:service name="fooservice-secured"> <wsdl:port binding="tns:fooserviceSoapBinding" name="FooBarWebServicePort"> <soap:address location="https://ws.example.com/a/b/FooBarWebService"/> </wsdl:port> </wsdl:service> 

CXF应该生成一个实现javax.xml.ws.Service的类,例如Fooservice ,并带有适当的构造函数:

 public class Fooservice extends Service { public Fooservice(URL wsdlLocation) { super(wsdlLocation, SERVICE); } public Fooservice(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } public Fooservice() { super(WSDL_LOCATION, SERVICE); } ...etc... 

在你的代码的某个地方(在这里,一些Groovy更容易阅读),你初始化上面的Service实例,然后调用一个端口。 在这里,根据名为secure的标志,我们使用httpshttp

 static final String NAMESPACE = 'com.example.ws.ab' static final QName SERVICE_NAME_HTTP = new QName(NAMESPACE, 'fooservice') static final QName SERVICE_NAME_HTTPS = new QName(NAMESPACE, 'fooservice-secured') Fooservice wsService File wsdlfile = new File('/somewhere/on/disk/wsdl.xml') // If the file is missing there will be an exception at connect // time from sun.net.www.protocol.file.FileURLConnection.connect // It should be possible to denote a resource on the classpath // instead of a file-on-disk. Not sure how, maybe by adding a handler // for a 'resource:' URL scheme? URI wsdlLocationUri = java.nio.file.Paths(wsdlfile.getCanonicalPath()).toUri() if (secure) { wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTPS) } else { wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTP) } SomeServicePort port = wsService.getSomeServicePort() port.doStuff() 

另一种方法是将WSDL下载到与用于服务调用的连接分离的连接上(使用tcpdump -n -nn -s0 -A -i eth0 'tcp port 80'来观察stream量),只需执行以下操作:

 URI wsdlLocationUri if (secure) { wsdlLocationUri = new URI('https://ws.example.com/a/b/FooBarWebService?wsdl') } else { wsdlLocationUri = new URI('http://ws.example.com/a/b/FooBarWebService?wsdl') } Fooservice wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTP) SomeServicePort port = wsService.getSomeServicePort() port.doStuff() 

请注意,如果wsdlLocationUri指定了https ,则实际上会正确使用https ,尽pipewsService已使用SERVICE_NAME_HTTP进行了初始化。 (不知道为什么服务使用检索WSDL资源的scheme?)

这就是它。

要debugging连接,请传递:

 -Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true -Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=true 

到命令行上的JVM。 这会将信息从http连接代码写入stdout(不幸的是, java.util.logging !)。

我的解决scheme是修改生成的服务。 您必须更改标头注释中的wsdlLocation ,而实例块如下所示:

  static { URL url = null; url = com.ups.wsdl.xoltws.ship.v1.ShipService.class.getResource("Ship.wsdl"); SHIPSERVICE_WSDL_LOCATION = url; } 

我将wsdl文件放在ShipService类旁边的bin目录中

虽然你可以通过一些操作来实现它,但是我build议要这样做,并保持现在的样子。

Web Service端点提供者应该提供一个WSDL作为其合约的一部分。 您生成的代码应该从服务器本身的WSDL中提取出来。

在WebSphere上进行部署时,您可以从部署UI中将端点更改为其他端点。 其他应用程序服务器你可能需要找出供应商特定的绑定XML来做到这一点。

它只发生在初始化,所以对整个应用程序的影响应该可以忽略不计。