我正在编写一个胖客户端,它使用SOAP服务来处理某些功能(错误报告等)。
我的JAX-WS工作正常,但默认情况下(至少在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本身是按预期构造的。但是服务不会加载...具体来说,当我调用ApplicationService.getPort()时,我得到一个NullPointerException;
这可能吗?还是只是一场疯狂的追逐?
答案 0 :(得分:48)
是的,这绝对是可能的,因为我在通过javax.xml.ws.EndpointReference(一个与WS-A相关的类)创建客户端时已经这样做了。我已经将WSDL的类路径引用添加到WS-A EndPointReference,并且JAX-WS的Metro实现加载它就好了。无论是从WS-A EndPointReference加载WSDL还是从文件或http URL加载WSDL,您的JAX-WS实现都应使用相同的WSDL解析代码,因为您所做的只是解析URL。
最适合您的方法可能是执行以下操作:
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视为相对于类路径的相对URL,而是将其视为JAR文件中的相对URL,因此除非您实现自定义URL处理程序和您自己的前缀,否则您不能在WSDL中具有跨JAR运行的导入基于类路径的导入解析。如果您的WSDL导入外部资源,那就没问题,但如果这些资源移动,您就是在为自己注册维护问题。即使从类路径中使用WSDL的静态副本也违背了WSDL,Web服务和JAX-WS的精神,但有时候它是必要的。
最后,如果您嵌入了静态WSDL,我建议您至少使服务端点可配置以进行测试和部署。重新配置Web服务客户端的端点的代码如下:
YourClientInterface client = yourService.getPort(
new QName("...", "..."),
YourClientInterface.class);
BindingProvider bp = (BindingProvider) client;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://localhost:8080/yourServiceEndpoint");
答案 1 :(得分:14)
最近的JAX-WS您不需要执行任何架构目录或编程的wsdl位置设置 IF 您将WSDL放入JAR,然后将wsimport wsdlLocation
设置为JAR中WSDL的相对资源路径。那就是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项目布局src/main/resources/com/adamgent/ws
放置WSDL。
确保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应该是微不足道的。
答案 2 :(得分:6)
也许有点晚了,但我找到了一个非常简单的解决方案来解决这个问题,但这涉及到Service类生成代码的更改:
如果Service类中的以下行
baseUrl = net.example.ApplicationService.class.getResource(".");
更改为
baseUrl = net.example.ApplicationService.class.getResource("");
即使使用JAR中打包的WSDL也能正常工作。在这两种情况下都不确定getResource()的确切假设行为,但到目前为止,我在多个操作系统和Java版本上没有遇到任何问题。
答案 3 :(得分:5)
您所描述的是JAX-WS中的错误:JAX_WS-888 - Wrong code for resolving the URL for a custom wsdlLocation。
它已针对V2.2修复,因此在编写时只需设置wsdlLocation
即可。
答案 4 :(得分:3)
如果您的类路径有“。”在其中,Class.getResource(“。”)将返回执行java命令的目录的URL。否则,它将返回null。相应地调整wsdllocation。
答案 5 :(得分:3)
另一个答案是使用
new Service(wsdllocation, servicename );
获取服务对象。
这就是我解决问题的方法。
答案 6 :(得分:2)
MyService.class.getResource(".")
技巧来加载wsdl文件...但是在测试之后,如果类文件位于filesytem上的目录中,这似乎只能起作用。如果类文件在JAR中,则此调用将为URL返回null。
这听起来像JDK中的一个错误,因为如果您构建这样的URL:
final URL url = new URL( MyService.class.getResource( MyService.class.getSimpleName() + ".class"), "myservice.wsdl");
然后,如果将类和wsdl捆绑在一个jar中,它也可以工作。
我猜大多数人实际上会捆绑在一个罐子里!
答案 7 :(得分:2)
我在构建客户端jar之前替换了WSDL位置。
<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>
答案 8 :(得分:1)
这是我的hack-y解决方法。
我从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
。
这很有效,但如果有人能告诉我一个更优雅的解决方案,我会很感激。
答案 9 :(得分:1)
这是一个适合我的方式(特别是通过 http 和 https )。 Oracle JDK 1.8.0_51的JAX-WS使用由Apache CXF创建的类3.1.1。
请注意,在任何情况下,只能在第一次调用时获取远程WSDL。根据使用模式(长时间运行的程序),这可能是完全可以接受的。
基础知识:
wget --output-document=wsdl_raw.xml $WSDL_URL
xmllint --format wsdl_raw.xml > wsdl.xml
获得良好的格式./cxf/bin/wsdl2java -d output/ -client -validate wsdl.xml
并导入到项目中验证WSDL文件中是否存在 http 和 https 的服务定义。在我的情况下,提供商没有 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
-Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true
-Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=true
。 (不确定原因 - 服务是否使用用于检索WSDL资源的方案?)
就是这样。
要调试连接,请传递:
java.util.logging
命令行上的JVM。这会将http连接代码中的信息写入stdout(遗憾的是不会@register.simple_tag(takes_context=True)
def current(context, url_name):
print(context)
print(context.get('request'))
current_path = context.get('request').path
return 'active' if current_path.startswith(url_name) else ''
>>> [{'True': True, 'False': False, 'None': None}, {'csrf_token': <django.utils.functional.lazy.<locals>.__proxy__ object at 0x7f61d288b860>, 'links': OrderedDict([('index', 'home'), ('products', 'prodotti'), ('contact_us', 'contattaci')])}, {'forloop': {'counter0': 2, 'revcounter': 1, 'revcounter0': 0, 'counter': 3, 'parentloop': {}, 'last': True, 'first': False}}, {'name': 'contattaci', 'key': 'contact_us'}]
>>> None
。请Oracle!)。
答案 10 :(得分:0)
我的解决方案是修改生成的服务。你必须在标题注释中更改 wsdlLocation ,并且instantion块看起来像这样:
static {
URL url = null;
url = com.ups.wsdl.xoltws.ship.v1.ShipService.class.getResource("Ship.wsdl");
SHIPSERVICE_WSDL_LOCATION = url;
}
我将wsdl文件放在ShipService类
旁边的bin目录中答案 11 :(得分:0)
虽然你可以让它与一些操作一起使用,但我建议不这样做,并保持你现在的方式。
Web服务端点提供程序应提供WSDL作为其合同的一部分。您生成的代码应该从服务器本身提取WSDL。
在WebSphere上部署时,您可以将端点从部署UI更改为其他端点。您可能需要找到供应商特定的绑定XML来执行此操作的其他应用程序服务器。
它只在初始化时发生,因此对整个应用程序的影响应该可以忽略不计。
答案 12 :(得分:0)
无需复杂化任何内容, 只需使用jar类加载器
ClassLoader cl = SomeServiceImplService.class.getClassLoader();
SERVICE_WSDL_LOCATION = cl.getResource("META-INF/wsdls/service.wsdl");
尝试一下!