Bagaimana menghindari kebutuhan untuk menentukan lokasi WSDL di klien layanan web yang dihasilkan CXF atau JAX-WS?

165

Ketika saya membuat klien layanan web menggunakan wsdl2java dari CXF (yang menghasilkan sesuatu yang mirip dengan wsimport), melalui maven, layanan saya dimulai dengan kode seperti ini:

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

Jalan absolut hardcoded benar-benar menyebalkan. Kelas yang dihasilkan tidak akan berfungsi di komputer lain selain komputer saya.

Gagasan pertama adalah meletakkan file WSDL (plus semua yang diimpornya, WSDL dan XSD lainnya) di suatu tempat di dalam file jar dan mengelompokkannya. Tapi kami ingin menghindari ini. Karena semua hal itu dihasilkan oleh CXF dan JAXB yang berbasis di WSDLs dan XSDs, kami melihat tidak ada gunanya mengetahui WSDL saat runtime.

Atribut wsdlLocation dimaksudkan untuk mengesampingkan lokasi WSDL (setidaknya ini yang saya baca di suatu tempat), dan nilai defaultnya adalah "". Karena kami menggunakan maven, kami mencoba memasukkan ke <wsdlLocation></wsdlLocation>dalam konfigurasi CXF untuk mencoba memaksa generator sumber untuk membiarkan wsdlLocation kosong. Namun, ini membuatnya mengabaikan tag XML karena kosong. Kami melakukan peretasan yang sangat memalukan, menggunakan <wsdlLocation>" + "</wsdlLocation>.

Ini mengubah tempat lain juga:

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

Jadi, pertanyaan saya adalah:

  1. Apakah kita benar-benar membutuhkan lokasi WSDL bahkan jika semua kelas dihasilkan oleh CXF dan JAXB? Jika ya, mengapa?

  2. Jika kita tidak benar-benar membutuhkan lokasi WSDL, apa cara yang tepat dan bersih untuk membuat CXF tidak menghasilkannya dan menghindarinya sepenuhnya?

  3. Apa efek samping buruk yang bisa kita peroleh dengan peretasan itu? Kami masih belum bisa mengujinya untuk melihat apa yang terjadi, jadi jika seseorang bisa mengatakan di muka, itu akan baik.

Victor Stafusa
sumber

Jawaban:

206

Saya akhirnya menemukan jawaban yang tepat untuk pertanyaan ini hari ini.

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

Perhatikan bahwa saya telah mengawali nilai wsdlLocationdengan classpath:. Ini memberitahu plugin bahwa wsdl akan berada di classpath bukan path absolut. Maka akan menghasilkan kode yang mirip dengan ini:

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

Perhatikan bahwa ini hanya berfungsi pada versi 2.4.1 atau yang lebih baru dari cxf-codegen-plugin.

Kyle
sumber
8
Bila menggunakan JAX Maven Plugin bukan CXF, menghilangkan classpath:di <wsdlLocation...baris.
Twilite
Adakah yang menghadapi masalah namespace dengan kode yang dihasilkan oleh metode di atas?
Narendra Jaggi
Apakah Anda harus membuat daftar setiap wsdl secara individual jika Anda memiliki beberapa? Apakah mungkin untuk menghindari itu?
pitseeker
21

Kita gunakan

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

Dengan kata lain, gunakan path relatif ke classpath.

Saya percaya WSDL mungkin diperlukan saat runtime untuk validasi pesan selama marshal / unmarshal.

BPS
sumber
17

Bagi mereka yang menggunakan org.jvnet.jax-ws-commons:jaxws-maven-pluginuntuk menghasilkan klien dari WSDL saat membangun:

  • Tempatkan WSDL di tempat Anda src/main/resources
  • Jangan tidak awalan wsdlLocationdenganclasspath:
  • Lakukan awalan wsdlLocationdengan/

Contoh:

  • WSDL disimpan di /src/main/resources/foo/bar.wsdl
  • Konfigurasikan jaxws-maven-plugindengan <wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>dan<wsdlLocation>/foo/bar.wsdl</wsdlLocation>
Martin Devillers
sumber
mengapa tidak menggunakan awalan "wsdlLocation with classpath", saya menggunakannya dan berfungsi
Mohammad Sadegh Rafiei
9

1) Dalam beberapa kasus, ya. Jika WSDL berisi hal-hal seperti Kebijakan dan yang mengarahkan perilaku runtime, maka WSDL mungkin diperlukan saat runtime. Artefak tidak dihasilkan untuk hal-hal terkait kebijakan dan semacamnya. Juga, dalam beberapa kasus RPC / Literal yang tidak jelas, tidak semua ruang nama yang diperlukan adalah output dalam kode yang dihasilkan (per spec). Dengan demikian, wsdl akan dibutuhkan untuk mereka. Kasus-kasus yang tidak jelas.

2) Saya pikir sesuatu seperti itu akan berhasil. Apa versi CXF? Itu terdengar seperti bug. Anda dapat mencoba string kosong di sana (hanya spasi). Tidak yakin apakah itu berhasil atau tidak. Yang mengatakan, dalam kode Anda, Anda dapat menggunakan konstruktor yang mengambil URL WSDL dan hanya lulus nol. Wsdl tidak akan digunakan.

3) Hanya keterbatasan di atas.

Daniel Kulp
sumber
Ini adalah CXF 2.3.1 terbaru. Dirilis hanya 8 hari yang lalu. Melewati nol adalah ide yang bagus, saya harus melihat jawaban yang jelas ini sebelumnya. Saya masih akan mencoba spasi.
Victor Stafusa
Tidak, ruang kosong tidak sama dengan tidak ada. yaitu: tag XML diabaikan sepenuhnya.
Victor Stafusa
5

Saya bisa menghasilkan

static {
    WSDL_LOCATION = null;
}

dengan mengkonfigurasi file pom untuk memiliki null untuk 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>
raisercostin
sumber
2
Solusi ini tidak berfungsi untuk saya dengan CXF 3.1.0. mendapat kesalahan org.apache.cxf.tools.common.toolspec.parser.BadUsageException: Opsi tak terduga: -wsdlLokasi
Chandru
4

Apakah mungkin Anda dapat menghindari penggunaan wsdl2java? Anda dapat langsung menggunakan API FrontFnd CXF untuk meminta layanan Web SOAP Anda. Satu-satunya tangkapan adalah bahwa Anda perlu membuat SEI dan VO Anda di sisi klien Anda. Berikut ini contoh kode.

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

    }
}

Anda dapat melihat tutorial lengkapnya di sini http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/

Niraj Singh
sumber
2
File WSDL sangat rumit, jadi kami menggunakan autogenerasi sebagai cara untuk memastikan kompatibilitas. Autogenerasi menciptakan beberapa VO dan SEI yang sangat rumit. Kami memilih untuk menggunakan satu set objek domain terpisah yang benar-benar dipisahkan dengan yang autogenerasi, jadi kami tidak mengganggu autogenerasi atau dibatasi atau didorong olehnya. VO yang dibuat secara otomatis hanya digunakan dalam konteks komunikasi layanan dan kami menyimpannya sesingkat mungkin. Dengan kata lain, salah satu perhatian kami adalah untuk menghindari perlunya kode secara manual dan mengelola semua VO.
Victor Stafusa
2
Saya setuju dengan Victor, karena mempertahankan VO secara manual mungkin membuang-buang waktu, dan risiko perbedaan, lebih atau kurang terlihat dan berkualitas .. itulah tujuan wsdl2java, itulah mengapa ini berguna dan aman!
Donatello
4

Pembaruan untuk CXF 3.1.7

Dalam kasus saya, saya meletakkan file WSDL src/main/resourcesdan menambahkan path ini ke Srouces saya di Eclipse (Klik kanan pada Project-> Build Path -> Configure Build Path ...-> Source [Tab] -> Add Folder).

Ini adalah bagaimana pomfile saya terlihat dan seperti yang terlihat tidak ada wsdlLocation opsi yang diperlukan:

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

Dan di sini adalah Layanan yang dihasilkan. Seperti dapat dilihat, URL didapat dari ClassLoader dan bukan dari Path File Absolute

@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;   
}
Simpang siur
sumber
<configuration> <sourceRoot>${basedir}/src/main/java/</sourceRoot> <wsdlRoot>${basedir}/src/main/resources/</wsdlRoot> <includes> <include>*.wsdl</include> </includes> </configuration> Saya menyertakan semua file .wsdl di jalur kelas lalu bagaimana cara menentukan Lokasi wsdl sehingga setiap file .java dapat dihasilkan sesuai dengan masing-masing jalur .wsdl? Terima kasih sebelumnya. @Mazy
Khalid Shah
2

Serius, jawaban teratas tidak berfungsi untuk saya. mencoba cxf.version 2.4.1 dan 3.0.10. dan menghasilkan path absolut dengan wsdlLokasi setiap kali.

Solusi saya adalah menggunakan wsdl2javaperintah di apache-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
jeiao
sumber
0

Solusi @Martin Devillers bekerja dengan baik. Untuk kelengkapan, berikan langkah-langkah di bawah ini:

  1. Masukkan wsdl Anda ke direktori sumber daya seperti: src/main/resource
  2. Dalam file pom, tambahkan wsdlDirectory dan wsdlLocation (jangan lewatkan / di awal wsdlLocation), seperti di bawah ini. Sementara wsdlDirectory digunakan untuk menghasilkan kode dan wsdlLocation digunakan saat runtime untuk membuat proxy dinamis.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
  3. Kemudian dalam kode java Anda (dengan konstruktor no-arg):

    MyPort myPort = new MyPortService().getMyPort();
  4. Ini adalah bagian pembuatan kode lengkap dalam file pom, dengan api fasih dalam kode yang dihasilkan.

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

Shafiul
sumber