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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4455195/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 17:46:02  来源:igfitidea点击:

How to avoid the need to specify the WSDL location in a CXF or JAX-WS generated webservice client?

javawsdlcxfwsdl2java

提问by Victor Stafusa

When I generate a webservice client using wsdl2java from CXF (which generates something similar to wsimport), via maven, my services starts with codes like this:

当我使用 CXF 中的 wsdl2java(生成类似于 wsimport 的东西)生成 webservice 客户端时,通过 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;
    }

The hardcoded absolute path really sucks. The generated class won't work in any other computer other than mine.

硬编码的绝对路径真的很糟糕。生成的类不能在我的以外的任何其他计算机上工作。

The first idea is to put the WSDL file (plus everything it imports, other WSDLs and XSDs) somewhere in a jar-file and classpath it. But we want to avoid this. Since all that thing was generated by CXF and JAXB based in the WSDLs and XSDs, we see no point in needing to know the WSDL at runtime.

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

The wsdlLocation attribute is intended to override the WSDL location (at least this is what i readed somewhere), and it default value is "". Since we are using maven, we tried to include <wsdlLocation></wsdlLocation>inside the configuration of CXF to try to force the source generator to leave the wsdlLocation blank. However, this simply makes it ignore the XML tag because it is empty. We did a really ugly shameful hack, using <wsdlLocation>" + "</wsdlLocation>.

wsdlLocation 属性旨在覆盖 WSDL 位置(至少这是我在某处读到的),它的默认值为“”。由于我们使用的是 maven,因此我们尝试<wsdlLocation></wsdlLocation>在 CXF 的配置中包含以尝试强制源生成器将 wsdlLocation 留空。但是,这只会使它忽略 XML 标记,因为它是空的。我们做了一个非常丑陋可耻的 hack,使用<wsdlLocation>" + "</wsdlLocation>.

This changes other places too:

这也会改变其他地方:

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

So, my questions are:

所以,我的问题是:

  1. Does we really need a WSDL location even if all the classes were generated by CXF and JAXB? If yes, why?

  2. If we do not really need the WSDL location, what is the proper and clean way to make CXF not generate it and avoiding it entirely?

  3. What bad side effects we could get with that hack? We still can't test that to see what happens, so if someone could say in advance, it would be nice.

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

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

  3. 我们可以通过该黑客获得哪些不良副作用?我们仍然无法测试以了解会发生什么,因此如果有人可以提前说,那就太好了。

采纳答案by Kyle

I finally figured out the right answer to this question today.

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

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

Notice that I have prefixed the value in wsdlLocationwith classpath:. This tells the plugin that the wsdl will be on the classpath instead of an absolute path. Then it will generate code similar to this:

请注意,我已经前缀的价值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;
    }

Note that this only works with version 2.4.1 or newer of the cxf-codegen-plugin.

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

回答by Daniel Kulp

1) In some cases, yes. If the WSDL contains things like Policies and such that direct the runtime behavior, then the WSDL may be required at runtime. Artifacts are not generated for policy related things and such. Also, in some obscure RPC/Literal cases, not all the namespaces that are needed are output in the generated code (per spec). Thus, the wsdl would be needed for them. Obscure cases though.

1) 在某些情况下,是的。如果 WSDL 包含诸如 Policies 之类的东西来指导运行时行为,那么在运行时可能需要 WSDL。不会为与政策相关的事物等生成工件。此外,在一些晦涩难懂的 RPC/Literal 情况下,并非所有需要的命名空间都在生成的代码中输出(根据规范)。因此,他们需要 wsdl。晦涩的案例虽然。

2) I thought something like would work. What version of CXF? That sounds like a bug. You can try an empty string in there (just spaces). Not sure if that works or not. That said, in your code, you can use the constructor that takes the WSDL URL and just pass null. The wsdl wouldn't be used.

2)我认为类似的东西会起作用。什么版本的CXF?这听起来像一个错误。你可以在那里尝试一个空字符串(只是空格)。不确定这是否有效。也就是说,在您的代码中,您可以使用接受 WSDL URL 并只传递 null 的构造函数。不会使用 wsdl。

3) Just the limitations above.

3)只是上面的限制。

回答by BPS

We use

我们用

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

In other words, use a path relative to the classpath.

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

I believe the WSDL may be needed at runtime for validation of messages during marshal/unmarshal.

我相信在运行时可能需要 WSDL 以在编组/解组期间验证消息。

回答by raisercostin

I was able to generate

我能够生成

static {
    WSDL_LOCATION = null;
}

by configuring pom file to have a null for wsdlurl:

通过将 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>

回答by Niraj Singh

Is it possible that you can avoid using wsdl2java? You can straight away use CXF FrontEnd APIs to invoke your SOAP Webservice. The only catch is that you need to create your SEI and VOs on your client end. Here is a sample code.

是否有可能避免使用 wsdl2java?您可以立即使用 CXF 前端 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());

    }
}

You can see the full tutorial here http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/

你可以在这里看到完整的教程http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/

回答by Martin Devillers

For those using org.jvnet.jax-ws-commons:jaxws-maven-pluginto generate a client from WSDL at build-time:

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

  • Place the WSDL somewhere in your src/main/resources
  • Do notprefix the wsdlLocationwith classpath:
  • Do prefix the wsdlLocationwith /
  • 将 WSDL 放在您的某个位置 src/main/resources
  • 不要前缀wsdlLocationclasspath:
  • 做前缀wsdlLocationwith/

Example:

例子:

  • WSDL is stored in /src/main/resources/foo/bar.wsdl
  • Configure jaxws-maven-pluginwith <wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>and <wsdlLocation>/foo/bar.wsdl</wsdlLocation>
  • WSDL 存储在 /src/main/resources/foo/bar.wsdl
  • 配置jaxws-maven-plugin<wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory><wsdlLocation>/foo/bar.wsdl</wsdlLocation>

回答by jeiao

Seriously, the top answer is not working for me. tried cxf.version 2.4.1 and 3.0.10. and generate absolute path with wsdlLocation every times.

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

My solution is to use the wsdl2javacommand in the apache-cxf-3.0.10\bin\with -wsdlLocation classpath:wsdl/QueryService.wsdl.

我的解决方案是wsdl2javaapache-cxf-3.0.10\bin\with 中使用该命令-wsdlLocation classpath:wsdl/QueryService.wsdl

Detail:

细节:

    wsdl2java -encoding utf-8 -p com.jeiao.boss.testQueryService -impl -wsdlLocation classpath:wsdl/testQueryService.wsdl http://127.0.0.1:9999/platf/testQueryService?wsdl

回答by Mazy

Update for CXF 3.1.7

CXF 3.1.7 更新

In my case I put the WSDL files in src/main/resourcesand added this path to my Srouces in Eclipse (Right Click on Project-> Build Path -> Configure Build Path...-> Source[Tab] -> Add Folder).

就我而言,我将 WSDL 文件放入src/main/resources并添加到 Eclipse 中的 Srouces 路径(右键单击项目-> 构建路径-> 配置构建路径...-> 源 [Tab] -> 添加文件夹)。

Here is how my pomfile looks like and as can be seen there is NO wsdlLocationoption needed:

这是我的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>

And here is the generated Service. As can be seen the URL is get from ClassLoader and not from the Absolute File-Path

这是生成的服务。可以看出 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;   
}

回答by Shafiul

@Martin Devillers solution works fine. For completeness, providing the steps below:

@Martin Devillers 解决方案工作正常。为完整起见,提供以下步骤:

  1. Put your wsdl to resource directory like : src/main/resource
  2. In pom file, add both wsdlDirectory and wsdlLocation(don't miss / at the beginning of wsdlLocation), like below. While wsdlDirectory is used to generate code and wsdlLocation is used at runtime to create dynamic proxy.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
    
  3. Then in your java code(with no-arg constructor):

    MyPort myPort = new MyPortService().getMyPort();
    
  4. Here is the full code generation part in pom file, with fluent api in generated code.

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

  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>