CXFまたはJAX-WSで生成されたWebサービスクライアントでWSDLの場所を指定する必要を回避する方法


165

CXFからwsdl2java(wsimportに似たものを生成)を使用してWebサービスクライアントを生成すると、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ファイルのどこかに置き、それをクラスパス化することです。しかし、これは避けたいです。これらすべては、WSDLとXSDに基づいてCXFとJAXBによって生成されたので、実行時に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. そのハックでどのような悪い副作用が発生する可能性がありますか?何が起こるかを確認するためにまだテストすることはできないので、誰かが前もって言うことができればそれは素晴らしいでしょう。

回答:


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
CXFの代わりにJAX Mavenプラグインを使用する場合classpath:は、<wsdlLocation...行を省略します。
Twilite

上記の方法で生成されたコードで名前空間の問題に直面している人はいますか?
Narendra Jaggi

複数ある場合、各wsdlを個別にリストする必要がありますか?それを回避することは可能ですか?
ピットシーカー

21

を使用しております

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

つまり、クラスパスを基準とした相対パスを使用します。

マーシャル/アンマーシャル中のメッセージの検証のために、実行時にWSDLが必要になる可能性があると思います。


17

org.jvnet.jax-ws-commons:jaxws-maven-pluginビルド時にWSDLからクライアントを生成するために使用している場合:

  • あなたのどこかにWSDLを置きます src/main/resources
  • 接頭辞を付けないでくださいwsdlLocationclasspath:
  • Doが接頭辞wsdlLocation/

例:

  • WSDLは次の場所に保存されます /src/main/resources/foo/bar.wsdl
  • 構成jaxws-maven-plugin<wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>し、<wsdlLocation>/foo/bar.wsdl</wsdlLocation>

接頭辞「wsdlLocation with classpath」を使用しないのはなぜですか、それを使用すると動作します
Mohammad Sadegh Rafiei

9

1)場合によっては、そうです。WSDLに、ポリシーなど、実行時の動作を指示するものが含まれている場合、実行時にWSDLが必要になることがあります。成果物は、ポリシー関連のものなどには生成されません。また、一部のあいまいなRPC /リテラル​​のケースでは、必要なすべての名前空間が生成されたコードに出力されるわけではありません(仕様ごと)。したがって、それらにはwsdlが必要です。あいまいなケースも。

2)私は何かがうまくいくと思った。CXFのバージョンは?それはバグのように聞こえます。空の文字列を試すことができます(スペースのみ)。それが機能するかどうかわからない。つまり、コードでは、WSDL URLを受け取り、nullを渡すだけのコンストラクターを使用できます。wsdlは使用されません。

3)上記の制限のみ。


最新のCXF 2.3.1です。ちょうど8日前にリリースされました。nullを渡すのは良い考えです。前にこの明白な答えを見るべきです。私はまだスペースを試します。
Victor Stafusa 2010

いいえ、空白は何もしないことと同じです。つまり、XMLタグは完全に無視されます。
Victor Stafusa 2010

5

生成することができました

static {
    WSDL_LOCATION = null;
}

wsdlurlにnullを持つようにpomファイルを構成する:

    <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では機能しませんでした。-wsdlLocation:予期しないオプション:エラーorg.apache.cxf.tools.common.toolspec.parser.BadUsageExceptionました
Chandru

4

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());

    }
}

ここで完全なチュートリアルを見ることができますhttp://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/


2
WSDLファイルは非常に複雑だったため、互換性を確保する方法として自動生成を使用しました。自動生成により、同じように非常に複雑なVOとSEIが作成されました。自動生成されたものと完全に分離された別のドメインオブジェクトのセットを使用することを選択したため、自動生成に干渉したり、自動生成によって制限されたり、駆動されたりすることはありませんでした。自動生成されたVOは、サービス通信のコンテキストでのみ使用され、可能な限り短期間で維持しました。言い換えると、私たちの懸念の1つは、すべてのVOを手動でコーディングして管理する必要を回避することです。
Victor Stafusa 2013

2
私はビクターに同意します。手動でVOを維持することは時間の浪費であり、多かれ少なかれ目に見える、そして資格のある差異のリスクである可能性があるからです。
Donatello

4

CXF 3.1.7のアップデート

私の場合、WSDLファイルをsrc/main/resources配置し、EclipseのSroucesにこのパスを追加しました(プロジェクト->ビルドパス->ビルドパスの構成...->ソース[タブ]-> [フォルダーの追加]を右クリック)。

これは私の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から取得され、Absolute File-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;   
}

<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. 以下は、生成されたコードにFluent APIを使用した、pomファイルの完全なコード生成部分です。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.