如何避免在CXF或JAX-WS生成的Web服务客户端中指定WSDL位置?
当我使用CXF中的wsdl2java生成web服务客户端(通过maven生成类似于wsimport的东西)时,我的服务以这样的代码开始:
@WebServiceClient(name = "StatusManagement", wsdlLocation = "c:/some_absolute_path_to_a_wsdl_file.wsdl", targetNamespace = "http://tempuri.org/") public class StatusManagement extends Service { public final static URL WSDL_LOCATION; public final static QName SERVICE = new QName("http://tempuri.org/", "StatusManagement"); public final static QName WSHttpBindingIStatus = new QName("http://tempuri.org/", "WSHttpBinding_IStatus"); static { URL url = null; try { url = new URL("c:/some_absolute_path_to_a_wsdl_file.wsdl"); } catch (MalformedURLException e) { System.err.println("Can not initialize the default wsdl from c:/some_absolute_path_to_a_wsdl_file.wsdl"); // e.printStackTrace(); } WSDL_LOCATION = url; }
硬编码绝对path真的很糟糕。 生成的类将不能在除我以外的任何其他计算机上工作。
第一个想法是把WSDL文件(加上它导入的所有东西,其他的WSDL和XSD)放在jar文件的某个地方,并把它放在类path中。 但是我们想要避免这一点。 由于所有这些都是由基于WSDL和XSD的CXF和JAXB生成的,因此我们认为在运行时不需要知道WSDL。
wsdlLocation属性旨在覆盖WSDL的位置(至less这是我在某处),它的默认值是“”。 由于我们使用maven,我们试图在CXF的configuration中包含<wsdlLocation></wsdlLocation>
来尝试强制源生成器将wsdlLocation留空。 但是,这只是简单地忽略了XML标签,因为它是空的。 我们用<wsdlLocation>" + "</wsdlLocation>
做了一个非常难看的黑客攻击。
这也改变了其他地方:
@WebServiceClient(name = "StatusManagement", wsdlLocation = "" + "", targetNamespace = "http://tempuri.org/") public class StatusManagement extends Service { public final static URL WSDL_LOCATION; public final static QName SERVICE = new QName("http://tempuri.org/", "StatusManagement"); public final static QName WSHttpBindingIStatus = new QName("http://tempuri.org/", "WSHttpBinding_IStatus"); static { URL url = null; try { url = new URL("" + ""); } catch (MalformedURLException e) { System.err.println("Can not initialize the default wsdl from " + ""); // e.printStackTrace(); } WSDL_LOCATION = url; }
所以,我的问题是:
-
即使所有类都是由CXF和JAXB生成的,我们是否真的需要一个WSDL位置? 如果是,为什么?
-
如果我们确实不需要WSDL位置,那么使CXF不能生成它并完全避免它的正确和简洁的方法是什么?
-
我们可以用这个黑客得到什么坏的副作用? 我们仍然无法testing,看看会发生什么,所以如果有人能提前说,这将是很好的。
我终于明白了今天这个问题的正确答案。
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>${cxf.version}</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot> <wsdlOptions> <wsdlOption> <wsdl>${project.basedir}/src/main/resources/wsdl/FooService.wsdl</wsdl> <wsdlLocation>classpath:wsdl/FooService.wsdl</wsdlLocation> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin>
请注意,我已经用classpath:
在wsdlLocation
添加了前缀。 这告诉插件wsdl将在类path而不是绝对path。 然后它会生成类似这样的代码:
@WebServiceClient(name = "FooService", wsdlLocation = "classpath:wsdl/FooService.wsdl", targetNamespace = "http://org/example/foo") public class Foo_Service extends Service { public final static URL WSDL_LOCATION; public final static QName SERVICE = new QName("http://org/example/foo", "Foo"); public final static QName FooSOAPOverHTTP = new QName("http://org/example/foo", "Foo_SOAPOverHTTP"); static { URL url = Foo_Service.class.getClassLoader().getResource("wsdl/FooService.wsdl"); if (url == null) { java.util.logging.Logger.getLogger(Foo_Service.class.getName()) .log(java.util.logging.Level.INFO, "Can not initialize the default wsdl from {0}", "classpath:wsdl/FooService.wsdl"); } WSDL_LOCATION = url; }
我们用
wsdlLocation = "WEB-INF/wsdl/WSDL.wsdl"
换句话说,使用相对于类path的path。
我相信,在编组/解组期间,运行时可能需要WSDL来validation消息。
1)在某些情况下,是的。 如果WSDL包含诸如指导运行时行为的策略等,那么在运行时可能需要WSDL。 与策略有关的东西等不会产生工件。 另外,在一些不明确的RPC / Literal情况下,并不是所有需要的命名空间都是在生成的代码中输出的(每个规范)。 因此,他们需要wsdl。 晦涩的情况下。
2)我认为像这样的工作。 什么版本的CXF? 这听起来像一个错误。 你可以尝试一个空string(只是空格)。 不知道这是否有效。 也就是说,在你的代码中,你可以使用带有WSDL URL的构造函数,只传递null。 wsdl不会被使用。
3)只是以上的限制。
对于那些使用org.jvnet.jax-ws-commons:jaxws-maven-plugin
来在生成时从WSDL生成一个客户端:
- 把WSDL放在
src/main/resources
某个地方 - 不要用
classpath:
前缀wsdlLocation
classpath:
- 请在
wsdlLocation
加上/
例:
- WSDL存储在
/src/main/resources/foo/bar.wsdl
- 使用
<wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>
和<wsdlLocation>/foo/bar.wsdl</wsdlLocation>
configurationjaxws-maven-plugin
我能够产生
static { WSDL_LOCATION = null; }
通过configurationpom文件使wsdlurl为null:
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot> <wsdlOptions> <wsdlOption> <wsdl>${basedir}/src/main/resources/service.wsdl</wsdl> <extraargs> <extraarg>-client</extraarg> <extraarg>-wsdlLocation</extraarg> <wsdlurl /> </extraargs> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin>
是否有可能避免使用wsdl2java? 您可以直接使用CXF FrontEnd API来调用您的SOAP Web服务。 唯一的问题是你需要在客户端创build你的SEI和VO。 这是一个示例代码。
package com.aranin.weblog4j.client; import com.aranin.weblog4j.services.BookShelfService; import com.aranin.weblog4j.vo.BookVO; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; public class DemoClient { public static void main(String[] args){ String serviceUrl = "http://localhost:8080/weblog4jdemo/bookshelfservice"; JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(BookShelfService.class); factory.setAddress(serviceUrl); BookShelfService bookService = (BookShelfService) factory.create(); //insert book BookVO bookVO = new BookVO(); bookVO.setAuthor("Issac Asimov"); bookVO.setBookName("Foundation and Earth"); String result = bookService.insertBook(bookVO); System.out.println("result : " + result); bookVO = new BookVO(); bookVO.setAuthor("Issac Asimov"); bookVO.setBookName("Foundation and Empire"); result = bookService.insertBook(bookVO); System.out.println("result : " + result); bookVO = new BookVO(); bookVO.setAuthor("Arthur C Clarke"); bookVO.setBookName("Rama Revealed"); result = bookService.insertBook(bookVO); System.out.println("result : " + result); //retrieve book bookVO = bookService.getBook("Foundation and Earth"); System.out.println("book name : " + bookVO.getBookName()); System.out.println("book author : " + bookVO.getAuthor()); } }
你可以在这里看到完整的教程http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/
更新CXF 3.1.7
在我的情况下,我将WSDL文件放在src/main/resources
,并将这个path添加到Eclipse中的Srouces中(右键单击Project-> Build Path – > Configure Build Path …-> Source [Tab] – > Add Folder )。
这里是我的pom
文件的样子,可以看到有没有wsdlLocation
选项需要:
<plugin> <groupId>org.apache.cxf</groupId> <artifactId>cxf-codegen-plugin</artifactId> <version>${cxf.version}</version> <executions> <execution> <id>generate-sources</id> <phase>generate-sources</phase> <configuration> <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot> <wsdlOptions> <wsdlOption> <wsdl>classpath:wsdl/FOO_SERVICE.wsdl</wsdl> </wsdlOption> </wsdlOptions> </configuration> <goals> <goal>wsdl2java</goal> </goals> </execution> </executions> </plugin>
这里是生成的服务。 可以看出,URL是从ClassLoader而不是从绝对文件path获得的
@WebServiceClient(name = "EventService", wsdlLocation = "classpath:wsdl/FOO_SERVICE.wsdl", targetNamespace = "http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/") public class EventService extends Service { public final static URL WSDL_LOCATION; public final static QName SERVICE = new QName("http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/", "EventService"); public final static QName EventPort = new QName("http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/", "EventPort"); static { URL url = EventService.class.getClassLoader().getResource("wsdl/FOO_SERVICE.wsdl"); if (url == null) { java.util.logging.Logger.getLogger(EventService.class.getName()) .log(java.util.logging.Level.INFO, "Can not initialize the default wsdl from {0}", "classpath:wsdl/FOO_SERVICE.wsdl"); } WSDL_LOCATION = url; }
严重的是,最重要的答案是不为我工作。 试过cxf.version 2.4.1和3.0.10。 并且每次都用wsdlLocation生成绝对path。
我的解决schemewsdl2java命令在apache-cxf-3.0.10 \ bin \“-wsdlLocation classpath:wsdl / QueryService.wsdl”
详情:
wsdl2java -encoding utf-8 -p com.jeiao.boss.testQueryService -impl -wsdlLocation classpath:wsdl/testQueryService.wsdl http://127.0.0.1:9999/platf/testQueryService?wsdl