Làm cách nào để tránh yêu cầu chỉ định vị trí WSDL trong ứng dụng khách dịch vụ web do CXF hoặc JAX-WS tạo?


165

Khi tôi tạo một ứng dụng khách dịch vụ web bằng wsdl2java từ CXF (tạo ra một cái gì đó tương tự như wsimport), thông qua maven, các dịch vụ của tôi bắt đầu với các mã như thế này:

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

Con đường tuyệt đối mã hóa thực sự hút. Lớp được tạo sẽ không hoạt động trong bất kỳ máy tính nào khác ngoài máy tính của tôi.

Ý tưởng đầu tiên là đặt tệp WSDL (cộng với mọi thứ mà nó nhập, các WSDL và XSD khác) ở đâu đó trong tệp jar và phân loại tệp. Nhưng chúng tôi muốn tránh điều này. Vì tất cả những thứ đó được tạo ra bởi CXF và JAXB dựa trên WSDL và XSD, chúng tôi thấy không cần phải biết WSDL khi chạy.

Thuộc tính wsdlLocation nhằm ghi đè vị trí WSDL (ít nhất đây là những gì tôi đã đọc ở đâu đó) và giá trị mặc định của nó là "". Vì chúng tôi đang sử dụng maven, chúng tôi đã cố gắng đưa <wsdlLocation></wsdlLocation>vào cấu hình của CXF để cố gắng buộc trình tạo nguồn để trống wsdlLocation. Tuy nhiên, điều này chỉ đơn giản là làm cho nó bỏ qua thẻ XML vì nó trống. Chúng tôi đã thực hiện một hack đáng xấu hổ thực sự xấu xí, sử dụng <wsdlLocation>" + "</wsdlLocation>.

Điều này cũng thay đổi những nơi khác:

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

Vì vậy, câu hỏi của tôi là:

  1. Chúng ta có thực sự cần một vị trí WSDL ngay cả khi tất cả các lớp được tạo bởi CXF và JAXB không? Nếu đúng thì tại sao?

  2. Nếu chúng ta không thực sự cần vị trí WSDL, cách phù hợp và rõ ràng để làm cho CXF không tạo ra nó và tránh hoàn toàn nó là gì?

  3. Những tác dụng phụ xấu nào chúng ta có thể nhận được với bản hack đó? Chúng tôi vẫn không thể kiểm tra xem điều gì sẽ xảy ra, vì vậy nếu ai đó có thể nói trước, điều đó thật tuyệt.

Câu trả lời:


206

Cuối cùng tôi đã tìm ra câu trả lời đúng cho câu hỏi này ngày hôm nay.

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

Chú ý rằng tôi đã có tiền tố giá trị trong wsdlLocationvới classpath:. Điều này nói với plugin rằng wsdl sẽ nằm trên đường dẫn lớp thay vì một đường dẫn tuyệt đối. Sau đó, nó sẽ tạo mã tương tự như thế này:

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

Lưu ý rằng điều này chỉ hoạt động với phiên bản 2.4.1 hoặc mới hơn của plugin cxf-codegen.


8
Khi sử dụng JAX Maven Plugin thay vì CXF, bỏ qua classpath:trong <wsdlLocation...dòng.
Twilite

có ai phải đối mặt với một vấn đề không gian tên với mã được tạo bởi phương thức trên không?
Narendra Jaggi

Bạn có phải liệt kê từng wsdl riêng nếu bạn có nhiều? Có thể tránh điều đó?
pitseeker

21

Chúng tôi sử dụng

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

Nói cách khác, sử dụng một đường dẫn liên quan đến đường dẫn lớp.

Tôi tin rằng WSDL có thể cần thiết trong thời gian chạy để xác thực các tin nhắn trong thời gian soái ca / ​​không nguyên soái.


17

Đối với những người sử dụng org.jvnet.jax-ws-commons:jaxws-maven-pluginđể tạo ứng dụng khách từ WSDL tại thời điểm xây dựng:

  • Đặt WSDL ở đâu đó trong của bạn src/main/resources
  • Đừng không tiền tốwsdlLocation vớiclasspath:
  • Làm tiền tố wsdlLocationvới/

Thí dụ:

  • WSDL được lưu trữ trong /src/main/resources/foo/bar.wsdl
  • Cấu hình jaxws-maven-pluginvới <wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory><wsdlLocation>/foo/bar.wsdl</wsdlLocation>

tại sao không sử dụng tiền tố "wsdlLocation với classpath", tôi sử dụng nó và nó hoạt động
Mohammad Sadegh Rafiei

9

1) Trong một số trường hợp, có. Nếu WSDL chứa những thứ như Chính sách và điều đó chỉ đạo hành vi thời gian chạy, thì WSDL có thể được yêu cầu khi chạy. Cổ vật không được tạo ra cho những thứ liên quan đến chính sách và như vậy. Ngoài ra, trong một số trường hợp RPC / nghĩa đen tối nghĩa, không phải tất cả các không gian tên cần thiết đều được xuất ra trong mã được tạo (theo mỗi thông số). Vì vậy, wsdl sẽ là cần thiết cho họ. Trường hợp tối nghĩa mặc dù.

2) Tôi nghĩ rằng một cái gì đó như sẽ làm việc. Phiên bản nào của CXF? Nghe có vẻ như một lỗi. Bạn có thể thử một chuỗi trống trong đó (chỉ khoảng trắng). Không chắc chắn nếu điều đó làm việc hay không. Điều đó nói rằng, trong mã của bạn, bạn có thể sử dụng hàm tạo lấy URL WSDL và chỉ cần truyền null. Các wsdl sẽ không được sử dụng.

3) Chỉ là những hạn chế ở trên.


Đây là CXF 2.3.1 mới nhất. Phát hành chỉ 8 ngày trước. Vượt qua null là một ý tưởng tốt, tôi nên thấy câu trả lời rõ ràng này trước đây. Tôi vẫn sẽ thử các không gian.
Victor Stafusa

Không, không gian trống làm giống như không có gì. tức là: thẻ XML hoàn toàn bị bỏ qua.
Victor Stafusa

5

Tôi đã có thể tạo ra

static {
    WSDL_LOCATION = null;
}

bằng cách cấu hình tệp pom để có null cho 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
Giải pháp này không hiệu quả với tôi với CXF 3.1.0. gặp lỗi org.apache.cxf.tools.common.toolspec.parser.BadUsageException: Tùy chọn bất ngờ: -wsdlLocation
Chandru

4

Có thể là bạn có thể tránh sử dụng wsdl2java? Bạn có thể ngay lập tức sử dụng API CXF FrontEnd để gọi SOAP Webservice của bạn. Điều hấp dẫn duy nhất là bạn cần tạo SEI và VO trên đầu máy khách của mình. Đây là một mã mẫu.

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

    }
}

Bạn có thể xem hướng dẫn đầy đủ tại đây http://weblog4j.com/2012/05/01/developing-soap-web-service-USE-apache-cxf/


2
Các tệp WSDL cực kỳ phức tạp, vì vậy chúng tôi đã sử dụng tự động tạo như một cách để đảm bảo tính tương thích. Việc tự động tạo ra một số VO và SEI phức tạp không kém. Chúng tôi đã chọn sử dụng một tập hợp các đối tượng miền riêng biệt tách rời hoàn toàn với các đối tượng được tạo tự động, vì vậy chúng tôi không can thiệp vào quá trình tự phát cũng như bị hạn chế hoặc điều khiển bởi nó. Các VO được tạo tự động chỉ được sử dụng trong bối cảnh truyền thông dịch vụ và chúng tôi giữ chúng càng ngắn càng tốt. Nói cách khác, một trong những mối quan tâm của chúng tôi là tránh sự cần thiết phải tự viết mã và quản lý tất cả các VO.
Victor Stafusa

2
Tôi đồng ý với Victor, vì việc duy trì VO thủ công có thể gây lãng phí thời gian và rủi ro về sự khác biệt, ít nhiều có thể nhìn thấy và đủ điều kiện .. đó chính xác là mục đích của wsdl2java, đó là lý do tại sao nó hữu ích và an toàn!
Donatello

4

Cập nhật cho CXF 3.1.7

Trong trường hợp của tôi, tôi đặt các tệp WSDL vào src/main/resources và thêm đường dẫn này vào Srouces của mình trong Eclipse (Nhấp chuột phải vào Project-> Build Path -> Cấu hình đường dẫn xây dựng ...-> Nguồn [Tab] -> Thêm thư mục).

Đây là cách pomtập tin của tôi trông như thế nào và như có thể thấy là KHÔNG wsdlLocation cần tùy chọn:

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

Và đây là Dịch vụ được tạo. Như có thể thấy, URL được lấy từ ClassLoader chứ không phải từ Đường dẫn tệp tuyệt đối

@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> Tôi bao gồm tất cả các tệp .wsdl trong đường dẫn lớp thì làm cách nào tôi có thể chỉ định Vị trí wsdl để mỗi tệp .java được tạo tương ứng có đường dẫn .wsdl tương ứng? Cảm ơn trước. @Mazy
Khalid Shah

2

Nghiêm túc mà nói, câu trả lời hàng đầu không hiệu quả với tôi. đã thử cxf.version 2.4.1 và 3.0.10. và tạo đường dẫn tuyệt đối với wsdlLocation mỗi lần.

Giải pháp của tôi là sử dụng các wsdl2javalệnh trong apache-cxf-3.0.10\bin\ với-wsdlLocation classpath:wsdl/QueryService.wsdl .

Chi tiết:

    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

Giải pháp @Martin Devillers hoạt động tốt. Để hoàn thiện, cung cấp các bước dưới đây:

  1. Đặt wsdl của bạn vào thư mục tài nguyên như: src/main/resource
  2. Trong tệp pom, thêm cả wsdlDirectory và wsdlLocation (không bỏ lỡ / ở đầu wsdlLocation), như bên dưới. Trong khi wsdlDirectory được sử dụng để tạo mã và wsdlLocation được sử dụng trong thời gian chạy để tạo proxy động.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
  3. Sau đó, trong mã java của bạn (với hàm tạo không có đối số):

    MyPort myPort = new MyPortService().getMyPort();
  4. Đây là phần tạo mã đầy đủ trong tệp pom, với api lưu loát trong mã được tạo.

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.