如何避免需要在CXF或JAX-WS生成的Web服务客户端中指定WSDL位置?


165

当我从CXF使用wsdl2java生成Web服务客户端(生成类似于wsimport的东西)时,通过maven,我的服务从以下代码开始:

@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;
    }

硬编码的绝对路径确实很烂。生成的类无法在我的其他计算机上使用。

第一个想法是将WSDL文件(加上它导入的所有内容,其他WSDL和XSD)放在jar文件中,并对其进行类路径。但是我们要避免这种情况。由于所有这些都是由CXF和JAXB基于WSDL和XSD生成的,因此我们不需要在运行时知道WSDL。

wsdlLocation属性用于覆盖WSDL位置(至少这是我在某处读取的内容),并且其默认值为“”。由于我们正在使用Maven,因此我们尝试包含<wsdlLocation></wsdlLocation>在CXF的配置中,以尝试强制源生成器将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;
    }

因此,我的问题是:

  1. 即使所有类都是由CXF和JAXB生成的,我们是否真的需要WSDL位置?如果是,为什么?

  2. 如果我们真的不需要WSDL位置,那么什么是使CXF不生成并完全避免它的正确而干净的方法?

  3. 使用该hack会带来哪些不良影响?我们仍然无法测试是否会发生这种情况,因此,如果有人可以提前说,那就太好了。

Answers:


206

我终于在今天找到了对这个问题的正确答案。

<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>

请注意,我已在中添加值的前缀 wsdlLocationclasspath:。这告诉插件,wsdl将位于类路径上,而不是绝对路径上。然后它将生成类似于以下代码:

@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;
    }

请注意,这仅适用于2.4.1版或更高版本的cxf-codegen-plugin。


8
当使用JAX Maven插件而不是CXF时,请classpath:在该<wsdlLocation...行中省略。
2014年

是否有人面对上述方法生成的代码的命名空间问题?
Narendra Jaggi,

如果您有多个wsdl,是否必须分别列出它们?有可能避免这种情况吗?
pitseeker

21

我们用

wsdlLocation = "WEB-INF/wsdl/WSDL.wsdl"

换句话说,使用相对于类路径的路径。

我认为可能需要在运行时使用WSDL来在编组/解组期间验证消息。


17

对于那些org.jvnet.jax-ws-commons:jaxws-maven-plugin在构建时使用WSDL生成客户端的用户:

  • 将WSDL放在您的某处 src/main/resources
  • 不要加前缀wsdlLocationclasspath:
  • wsdlLocation加上前缀/

例:

  • WSDL存​​储在 /src/main/resources/foo/bar.wsdl
  • jaxws-maven-plugin使用<wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>和配置<wsdlLocation>/foo/bar.wsdl</wsdlLocation>

为什么不使用前缀“带有类路径的wsdlLocation”,我使用它并且可以正常工作
Mohammad Sadegh Rafiei

9

1)在某些情况下,是的。如果WSDL包含诸如Policy之类的内容并指导运行时行为,则在运行时可能需要WSDL。不会为与政策相关的事物等生成工件。同样,在某些晦涩的RPC /文字情况下,并非所有需要的名称空间都在生成的代码中输出(按规范)。因此,他们将需要wsdl。虽然晦涩难懂。

2)我认为类似的方法会起作用。什么版本的CXF?听起来像个错误。您可以在此处尝试一个空字符串(仅空格)。不确定是否可行。就是说,在您的代码中,您可以使用采用WSDL URL并仅传递null的构造函数。不会使用wsdl。

3)以上限制。


它是最新的CXF 2.3.1。仅在8天前发布。传递null是一个好主意,我之前应该看到这个明显的答案。我仍然会尝试空格。
维克多·斯塔夫萨

不,空格等于空。即:XML标记被完全忽略。
维克多·斯塔夫萨

5

我能够产生

static {
    WSDL_LOCATION = null;
}

通过将pom文件配置为wsdlurl为空:

    <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>

2
对于CXF 3.1.0,此解决方案不适用于我。收到错误org.apache.cxf.tools.common.toolspec.parser.BadUsageException:意外的选项:-wsdlLocation
Chandru

4

是否有可能避免使用wsdl2java?您可以立即使用CXF FrontEnd API来调用您的SOAP Web服务。唯一的问题是您需要在客户端上创建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/


2
WSDL文件非常复杂,因此我们使用自动生成来确保兼容性。自发电产生了一些同样非常复杂的VO和SEI。我们选择使用完全独立于自动生成对象的一组独立的域对象,因此我们既不干扰自动生成,也不受其限制或驱动。自动生成的VO仅在服务通信的上下文中使用,我们将它们保留得尽可能短。换句话说,我们的关注之一是避免需要手动编码和管理所有VO。
维克多·斯塔夫萨

2
我同意Victor的观点,因为手动维护VO可能会浪费时间,并且可能会或多或少可见和合格地存在差异的风险。这正是wsdl2java的目的,这就是为什么它有用且安全的原因!
Donatello 2014年

4

CXF 3.1.7更新

就我而言,我将WSDL文件放入其中src/main/resources,并将此路径添加到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而不是绝对文件路径获取的

@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;   
}

<configuration> <sourceRoot>${basedir}/src/main/java/</sourceRoot> <wsdlRoot>${basedir}/src/main/resources/</wsdlRoot> <includes> <include>*.wsdl</include> </includes> </configuration> 我将所有.wsdl文件都包含在类路径中,那么如何指定wsdl位置,因此每个生成的.java文件都可以包含各自的.wsdl路径?提前致谢。@Mazy
Khalid Shah,

2

严重的是,最佳答案对我不起作用。尝试了cxf.version 2.4.1和3.0.10。并每次都使用wsdlLocation生成绝对路径。

我的解决方案是wsdl2javaapache-cxf-3.0.10\bin\ with中使用命令-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

0

@Martin Devillers解决方案效果很好。为了完整起见,请提供以下步骤:

  1. 将您的wsdl放入资源目录,例如: src/main/resource
  2. 在pom文件中,添加wsdlDirectory和wsdlLocation(在wsdlLocation的开头不要错过/),如下所示。wsdlDirectory用于生成代码,而wsdlLocation用于在运行时创建动态代理。

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
  3. 然后在您的Java代码中(使用无参数构造函数):

    MyPort myPort = new MyPortService().getMyPort();
  4. 这是pom文件中完整的代码生成部分,并在生成的代码中带有流利的api。

    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.5</version>
    
    <dependencies>
        <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-fluent-api</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-tools</artifactId>
            <version>2.3.0</version>
        </dependency>
    </dependencies>
    
    <executions>
        <execution>
            <id>wsdl-to-java-generator</id>
            <goals>
                <goal>wsimport</goal>
            </goals>
            <configuration>
                <xjcArgs>
                    <xjcArg>-Xfluent-api</xjcArg>
                </xjcArgs>
                <keep>true</keep>
                <wsdlDirectory>src/main/resources/package</wsdlDirectory>
                <wsdlLocation>/package/my.wsdl</wsdlLocation>
                <sourceDestDir>${project.build.directory}/generated-sources/annotations/jaxb</sourceDestDir>
                <packageName>full.package.here</packageName>
            </configuration>
        </execution>
    </executions>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.