这是对我有用的一个(特别是通过 http 和 https)。 Oracle JDK 1.8.0_51 的 JAX-WS 使用 Apache CXF 3.1.1 创建的类的案例。
请注意,在任何情况下,远程 WSDL 都只能在第一次调用时获得。根据使用模式(长时间运行的程序),这可能是完全可以接受的。
基础知识:
- 从远程主机下载 WSDL 并存储为文件:
wget --output-document=wsdl_raw.xml $WSDL_URL
- 您可能需要
xmllint --format wsdl_raw.xml > wsdl.xml 以获得更好的格式
- 使用the command line tool:
./cxf/bin/wsdl2java -d output/ -client -validate wsdl.xml 生成客户端类并导入到您的项目中
验证 http 和 https 的服务定义是否存在于 WSDL 文件中。就我而言,供应商没有 https 的供应商(但确实接受 https 流量),我不得不手动添加它。 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 的标志,我们使用https 或http:
static final String NAMESPACE = 'com.example.ws.a.b'
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' 观察流量)就是这样做:
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,尽管wsService 已经用SERVICE_NAME_HTTP 进行了初始化。 (不知道为什么 - 服务使用用于检索 WSDL 资源的方案?)
就是这样。
为了调试连接,通过:
-Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true
-Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=true
在命令行上到 JVM。这会将来自 http 连接代码的信息写入标准输出(不幸的是,不会写入 java.util.logging。Oracle,拜托!)。