Máy khách JAX-WS: đường dẫn chính xác để truy cập WSDL cục bộ là gì?


86

Vấn đề là tôi cần tạo một ứng dụng khách dịch vụ web từ một tệp mà tôi đã được cung cấp. Tôi đã lưu trữ tệp này trên hệ thống tệp cục bộ và trong khi tôi giữ tệp WSDL trong đúng thư mục hệ thống tệp, mọi thứ đều ổn. Khi tôi triển khai nó đến một máy chủ hoặc xóa WSDL khỏi thư mục hệ thống tệp, proxy không thể tìm thấy WSDL và xảy ra lỗi. Tôi đã tìm kiếm trên web và tìm thấy các bài đăng sau đây nhưng tôi không thể làm cho nó hoạt động:
JAX-WS Đang tải WSDL từ jar
http://www.java.net/forum/topic/glassfish/metro -and-jaxb / client-jar-cant-find-local-wsdl-0
http://blog.vinodsingh.com/2008/12/locally-packaged-wsdl.html

Tôi đang sử dụng NetBeans 6.1 (đây là ứng dụng cũ mà tôi đã cập nhật với ứng dụng khách dịch vụ web mới này). Dưới đây là lớp proxy JAX-WS:

    @WebServiceClient(name = "SOAService", targetNamespace = "http://soaservice.eci.ibm.com/", wsdlLocation = "file:/C:/local/path/to/wsdl/SOAService.wsdl")
public class SOAService
    extends Service
{

    private final static URL SOASERVICE_WSDL_LOCATION;
    private final static Logger logger = Logger.getLogger(com.ibm.eci.soaservice.SOAService.class.getName());

    static {
        URL url = null;
        try {
            URL baseUrl;
            baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
            url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl");
        } catch (MalformedURLException e) {
            logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file");
            logger.warning(e.getMessage());
        }
        SOASERVICE_WSDL_LOCATION = url;
    }

    public SOAService(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    public SOAService() {
        super(SOASERVICE_WSDL_LOCATION, new QName("http://soaservice.eci.ibm.com/", "SOAService"));
    }

    /**
     * @return
     *     returns SOAServiceSoap
     */
    @WebEndpoint(name = "SOAServiceSOAP")
    public SOAServiceSoap getSOAServiceSOAP() {
        return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class);
    }

    /**
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns SOAServiceSoap
     */
    @WebEndpoint(name = "SOAServiceSOAP")
    public SOAServiceSoap getSOAServiceSOAP(WebServiceFeature... features) {
        return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class, features);
    }

}


Đây là mã của tôi để sử dụng proxy:

   WebServiceClient annotation = SOAService.class.getAnnotation(WebServiceClient.class);
   // trying to replicate proxy settings
   URL baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource("");//note : proxy uses "."
   URL url = new URL(baseUrl, "/WEB-INF/wsdl/client/SOAService.wsdl");
   //URL wsdlUrl = this.getClass().getResource("/META-INF/wsdl/SOAService.wsdl"); 
   SOAService serviceObj = new SOAService(url, new QName(annotation.targetNamespace(), annotation.name()));
   proxy = serviceObj.getSOAServiceSOAP();
   /* baseUrl;

   //classes\com\ibm\eci\soaservice
   //URL url = new URL(baseUrl, "../../../../wsdl/SOAService.wsdl");

   proxy = new SOAService().getSOAServiceSOAP();*/
   //updating service endpoint 
   Map<String, Object> ctxt = ((BindingProvider)proxy ).getRequestContext();
   ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);
   ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, WebServiceUrl);

NetBeans đã đưa một bản sao của WSDL vào web-inf / wsdl / client / SOAService , vì vậy tôi cũng không muốn thêm nó vào META-INF . Các lớp dịch vụ nằm trong WEB-INF / class / com / ibm / eci / soaservice / và biến baseurl chứa đường dẫn đầy đủ của hệ thống tệp đến nó (c: \ path \ to \ the \ project ... \ soaservice). Đoạn mã trên gây ra lỗi:

javax.xml.ws.WebServiceException: Không thể truy cập WSDL tại: tệp: /WEB-INF/wsdl/client/SOAService.wsdl. Nó không thành công với: \ WEB-INF \ wsdl \ client \ SOAService.wsdl (không thể tìm thấy đường dẫn)

Vì vậy, trước hết, tôi có nên cập nhật wsdllocation của lớp proxy không? Sau đó, làm cách nào để yêu cầu lớp SOAService trong WEB-INF / class / com / ibm / eci / soaservice tìm kiếm WSDL trong \ WEB-INF \ wsdl \ client \ SOAService.wsdl?

ĐÃ CHỈNH SỬA : Tôi đã tìm thấy liên kết khác này - http://jianmingli.com/wp/?cat=41 , có nghĩa là đưa WSDL vào classpath. Tôi xấu hổ khi hỏi: làm cách nào để đưa nó vào đường dẫn ứng dụng web?


Câu hỏi tương tự: JAX-WS tải WSDL từ jar
sleske

Câu trả lời:


117

Tùy chọn tốt nhất là sử dụng jax-ws-catalog.xml

Khi bạn biên dịch tệp WSDL cục bộ, hãy ghi đè vị trí WSDL và đặt nó thành một cái gì đó như

http: //localhost/wsdl/SOAService.wsdl

Đừng lo lắng đây chỉ là URI chứ không phải URL, nghĩa là bạn không cần phải có sẵn WSDL tại địa chỉ đó.
Bạn có thể thực hiện việc này bằng cách chuyển tùy chọn wsdllocation vào trình biên dịch wsdl sang java.

Làm như vậy sẽ thay đổi mã proxy của bạn từ

static {
    URL url = null;
    try {
        URL baseUrl;
        baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
        url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl");
    } catch (MalformedURLException e) {
        logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file");
        logger.warning(e.getMessage());
    }
    SOASERVICE_WSDL_LOCATION = url;
}

đến

static {
    URL url = null;
    try {
        URL baseUrl;
        baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
        url = new URL(baseUrl, "http://localhost/wsdl/SOAService.wsdl");
    } catch (MalformedURLException e) {
        logger.warning("Failed to create URL for the wsdl Location: 'http://localhost/wsdl/SOAService.wsdl', retrying as a local file");
        logger.warning(e.getMessage());
    }
    SOASERVICE_WSDL_LOCATION = url;
}

Tệp thông báo: // đã thay đổi thành http: // trong hàm tạo URL.

Bây giờ có trong jax-ws-catalog.xml. Nếu không có jax-ws-catalog.xml jax-ws thực sự sẽ cố gắng tải WSDL từ vị trí

http: //localhost/wsdl/SOAService.wsdl
và không thành công, vì sẽ không có WSDL như vậy.

Nhưng với jax-ws-catalog.xml, bạn có thể chuyển hướng jax-ws đến một WSDL được đóng gói cục bộ bất cứ khi nào nó cố gắng truy cập WSDL @

http: //localhost/wsdl/SOAService.wsdl
.

Đây là jax-ws-catalog.xml

<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
        <system systemId="http://localhost/wsdl/SOAService.wsdl"
                uri="wsdl/SOAService.wsdl"/>
    </catalog>

Những gì bạn đang làm là nói với jax-ws rằng bất cứ khi nào nó cần tải WSDL từ

http: //localhost/wsdl/SOAService.wsdl
, nó sẽ tải nó từ đường dẫn cục bộ wsdl / SOAService.wsdl.

Bây giờ bạn nên đặt wsdl / SOAService.wsdl và jax-ws-catalog.xml ở đâu? Đó là câu hỏi triệu đô phải không?
Nó phải nằm trong thư mục META-INF của jar ứng dụng của bạn.

vì vậy một cái gì đó như thế này

ABCD.jar  
| __ META-INF    
    | __ jax-ws-catalog.xml  
    | __ wsdl  
        | __ SOAService.wsdl  

Bằng cách này, bạn thậm chí không phải ghi đè URL trong máy khách truy cập proxy của mình. WSDL được chọn từ bên trong JAR của bạn và bạn tránh phải có các đường dẫn hệ thống tệp được mã hóa cứng trong mã của mình.

Thông tin thêm về jax-ws-catalog.xml http://jax-ws.java.net/nonav/2.1.2m1/docs/catalog-support.html

Hy vọng điều đó sẽ giúp


được rồi, tôi không thể giải quyết vấn đề theo cách này trong ứng dụng web: tôi đã cố gắng đưa wsdl vào bên trong web-inf mà không thành công, có thể là do tôi thiếu kiến ​​thức. Dù sao thì nó cũng hoạt động với một cái lọ, vì vậy tôi sẽ tạo một thư viện trình bao bọc, vì nó đáng lẽ phải được thực hiện ngay từ đầu. Cảm ơn bạn đã hỗ trợ

Tôi đã có thể sử dụng câu trả lời này thành công và tôi tin rằng đây là một giải pháp tốt hơn tất cả các giải pháp thay thế khác mà các bài báo và hướng dẫn khác ghi lại. Vì vậy, đối với tôi đây là thực hành tốt nhất. Tôi chỉ thắc mắc tại sao giải pháp này không được ghi lại trong các bài báo và hướng dẫn chính thức khác về chủ đề JAX-WS.
Rahul Khimasia 14/1218

19

Một cách tiếp cận khác mà chúng tôi đã thực hiện thành công là tạo mã proxy máy khách WS bằng cách sử dụng wsimport (từ Ant, như một tác vụ Ant) và chỉ định thuộc tính wsdlLocation.

<wsimport debug="true" keep="true" verbose="false" target="2.1" sourcedestdir="${generated.client}" wsdl="${src}${wsdl.file}" wsdlLocation="${wsdl.file}">
</wsimport>

Vì chúng tôi chạy điều này cho một dự án có nhiều WSDL, tập lệnh sẽ giải quyết động giá trị $ (wsdl.file} được thiết lập thành /META-INF/wsdl/YourWebServiceName.wsdl liên quan đến vị trí JavaSource (hoặc / src, tùy thuộc vào cách bạn thiết lập dự án của mình). Trong quá trình xây dựng, các tệp WSDL và XSDs được sao chép vào vị trí này và được đóng gói trong tệp JAR. (tương tự như giải pháp được Bhasakar mô tả ở trên)

MyApp.jar
|__META-INF
   |__wsdl
      |__YourWebServiceName.wsdl
      |__YourWebServiceName_schema1.xsd
      |__YourWebServiceName_schmea2.xsd

Lưu ý: đảm bảo rằng các tệp WSDL đang sử dụng các đường dẫn tương đối cho bất kỳ XSD đã nhập nào chứ không phải URL http:

  <types>
    <xsd:schema>
      <xsd:import namespace="http://valueobject.common.services.xyz.com/" schemaLocation="YourWebService_schema1.xsd"/>
    </xsd:schema>
    <xsd:schema>
      <xsd:import namespace="http://exceptions.util.xyz.com/" schemaLocation="YourWebService_schema2.xsd"/>
    </xsd:schema>
  </types>

Trong mã đã tạo , chúng tôi tìm thấy điều này:

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.2-b05-
 * Generated source version: 2.1
 * 
 */
@WebServiceClient(name = "YourService", targetNamespace = "http://test.webservice.services.xyz.com/", wsdlLocation = "/META-INF/wsdl/YourService.wsdl")
public class YourService_Service
    extends Service
{

    private final static URL YOURWEBSERVICE_WSDL_LOCATION;
    private final static WebServiceException YOURWEBSERVICE_EXCEPTION;
    private final static QName YOURWEBSERVICE_QNAME = new QName("http://test.webservice.services.xyz.com/", "YourService");

    static {
        YOURWEBSERVICE_WSDL_LOCATION = com.xyz.services.webservice.test.YourService_Service.class.getResource("/META-INF/wsdl/YourService.wsdl");
        WebServiceException e = null;
        if (YOURWEBSERVICE_WSDL_LOCATION == null) {
            e = new WebServiceException("Cannot find '/META-INF/wsdl/YourService.wsdl' wsdl. Place the resource correctly in the classpath.");
        }
        YOURWEBSERVICE_EXCEPTION = e;
    }

    public YourService_Service() {
        super(__getWsdlLocation(), YOURWEBSERVICE_QNAME);
    }

    public YourService_Service(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    /**
     * 
     * @return
     *     returns YourService
     */
    @WebEndpoint(name = "YourServicePort")
    public YourService getYourServicePort() {
        return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class);
    }

    /**
     * 
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns YourService
     */
    @WebEndpoint(name = "YourServicePort")
    public YourService getYourServicePort(WebServiceFeature... features) {
        return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class, features);
    }

    private static URL __getWsdlLocation() {
        if (YOURWEBSERVICE_EXCEPTION!= null) {
            throw YOURWEBSERVICE_EXCEPTION;
        }
        return YOURWEBSERVICE_WSDL_LOCATION;
    }

}

Có lẽ điều này cũng có thể giúp ích. Nó chỉ là một cách tiếp cận khác mà không sử dụng cách tiếp cận "danh mục".


Tôi thích cách tiếp cận này ... nhưng tại sao lại là thư mục META-INF?
IcedDante

1
Xin lưu ý rằng điều này yêu cầu sử dụng JAX-WS RI 2.2, không phải 2.1 đi kèm với JDK 6 theo mặc định
ᄂ ᄀ

4

Cảm ơn rất nhiều vì câu trả lời của Bhaskar Karambelkar giải thích chi tiết và khắc phục sự cố của tôi. Nhưng tôi cũng muốn diễn đạt lại câu trả lời trong ba bước đơn giản cho những người đang vội vàng sửa chữa

  1. Thực hiện tham chiếu vị trí địa phương wsdl của bạn dưới dạng wsdlLocation= "http://localhost/wsdl/yourwsdlname.wsdl"
  2. Tạo một thư mục META-INF ngay dưới src. Đặt / s tệp wsdl của bạn vào một thư mục dưới META-INF, nói META-INF / wsdl
  3. Tạo tệp xml jax-ws-catalog.xml theo META-INF như bên dưới

    <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system"> <system systemId="http://localhost/wsdl/yourwsdlname.wsdl" uri="wsdl/yourwsdlname.wsdl" /> </catalog>

Bây giờ đóng gói lọ của bạn. Không còn tham chiếu đến thư mục cục bộ, tất cả đều được đóng gói và tham chiếu trong


4

Đối với những người vẫn đang tìm kiếm giải pháp ở đây, giải pháp dễ nhất là sử dụng <wsdlLocation>mà không cần thay đổi bất kỳ mã nào. Các bước làm việc được đưa ra 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, hãy thêm cả wsdlDirectory và wsdlLocation (đừ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 no-arg):

    MyPort myPort = new MyPortService().getMyPort();
    
  4. Để hoàn thiện, tôi cung cấp ở đây phần tạo mã đầy đủ, với api thông thạo 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>
    


2

Đối với những người bạn sử dụng Spring, bạn có thể chỉ cần tham chiếu bất kỳ tài nguyên classpath nào bằng cách sử dụng giao thức classpath. Vì vậy, trong trường hợp của wsdlLocation, điều này sẽ trở thành:

<wsdlLocation>classpath:META-INF/webservice.wsdl</wsdlLocation>

Lưu ý rằng đó không phải là hành vi Java tiêu chuẩn. Xem thêm: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html


0

chính xác cùng một vấn đề được mô tả ở đây. Bất kể tôi đã làm gì, theo các ví dụ trên, để thay đổi vị trí tệp WSDL của tôi (trong trường hợp của chúng tôi là từ máy chủ web), nó vẫn tham chiếu đến vị trí ban đầu được nhúng trong cây nguồn của quy trình máy chủ.

Sau NHIỀU giờ cố gắng gỡ lỗi này, tôi nhận thấy rằng Ngoại lệ luôn được ném từ cùng một dòng chính xác (trong trường hợp của tôi là 41). Cuối cùng vào sáng nay, tôi quyết định chỉ gửi mã khách hàng nguồn của mình cho đối tác thương mại của chúng tôi để họ ít nhất có thể hiểu mã trông như thế nào, nhưng có lẽ là xây dựng mã của riêng họ. Trước sự sửng sốtkinh hoàng của tôi, tôi đã tìm thấy một loạt các tệp lớp được trộn lẫn với các tệp .java của tôi trong cây nguồn khách hàng của tôi. Thật kỳ lạ !! Tôi nghi ngờ đây là một sản phẩm phụ của công cụ tạo máy khách JAX-WS.

Sau khi tôi tập hợp các tệp .class ngớ ngẩn đó và thực hiện việc xây dựng lại mã máy khách hoàn toàn sạch sẽ, mọi thứ hoạt động hoàn hảo !! Đỏ tươi !!

YMMV, Andrew

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.