簡體   English   中英

JAX-WS 客戶端:訪問本地 WSDL 的正確路徑是什么?

[英]JAX-WS client : what's the correct path to access the local WSDL?

問題是我需要從提供的文件中構建 Web 服務客戶端。 我已將此文件存儲在本地文件系統上,並且雖然我將 WSDL 文件保存在正確的文件系統文件夾中,但一切都很好。 當我將它部署到服務器或從文件系統文件夾中刪除 WSDL 時,代理找不到 WSDL 並出現錯誤。 我在網上搜索過,我發現了以下帖子,但我無法使它工作:
JAX-WS 從 jar 加載 WSDL
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

我正在使用 NetBeans 6.1(這是一個遺留應用程序,我必須使用這個新的 Web 服務客戶端進行更新)。 下面是 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);
    }

}


這是我使用代理的代碼:

   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 將 WSDL 的副本放在web-inf/wsdl/client/SOAService中,所以我也不想將它添加到META-INF中。 服務類位於WEB-INF/classes/com/ibm/eci/soaservice/中,baseurl 變量包含它的文件系統完整路徑(c:\path\to\the\project...\soaservice)。 上面的代碼引發了錯誤:

javax.xml.ws.WebServiceException:無法訪問位於:file:/WEB-INF/wsdl/client/SOAService.wsdl 的 WSDL。 它失敗了:\WEB-INF\wsdl\client\SOAService.wsdl(找不到路徑)

那么,首先,我應該更新代理類的 wsdllocation 嗎? 那么如何告訴 WEB-INF/classes/com/ibm/eci/soaservice 中的 SOAService 類在 \WEB-INF\wsdl\client\SOAService.wsdl 中搜索 WSDL?

編輯:我找到了另一個鏈接 - http://jianmingli.com/wp/?cat=41 ,它說將 WSDL 放入類路徑中。 不好意思問:怎么把它放到web應用的classpath中?

最好的選擇是使用 jax-ws-catalog.xml

當您編譯本地 WSDL 文件時,覆蓋 WSDL 位置並將其設置為類似

http://localhost/wsdl/SOAService.wsdl

不用擔心,這只是一個 URI 而不是 URL,這意味着您不必在該地址上提供 WSDL。
您可以通過將 wsdllocation 選項傳遞給 wsdl to java 編譯器來做到這一點。

這樣做會將您的代理代碼從

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

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

請注意,在 URL 構造函數中,file:// 更改為 http://。

現在出現在 jax-ws-catalog.xml 中。 如果沒有 jax-ws-catalog.xml jax-ws 確實會嘗試從該位置加載 WSDL

http://localhost/wsdl/SOAService.wsdl
並且失敗,因為沒有這樣的 WSDL 可用。

但是使用 jax-ws-catalog.xml,您可以在 jax-ws 嘗試訪問 WSDL 時將其重定向到本地打包的 WSDL @

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

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

您正在做的是告訴 jax-ws 何時需要從

ABCD.jar  
|__ META-INF    
    |__ jax-ws-catalog.xml  
    |__ wsdl  
        |__ SOAService.wsdl
,它應該從本地路徑 wsdl/SOAService.wsdl 加載它。

現在應該將 wsdl/SOAService.wsdl 和 jax-ws-catalog.xml 放在哪里? 那是百萬美元的問題,不是嗎?
它應該位於應用程序 jar 的 META-INF 目錄中。

所以像這樣

 ABCD.jar  
 |__ 元信息    
     |__ jax-ws-catalog.xml  
     |__ wsdl  
         |__ SOAService.wsdl  

這樣,您甚至不必覆蓋訪問代理的客戶端中的 URL。 WSDL 是從您的 JAR 中提取的,您可以避免在代碼中使用硬編碼的文件系統路徑。

有關 jax-ws-catalog.xml 的更多信息http://jax-ws.java.net/nonav/2.1.2m1/docs/catalog-support.html

希望有幫助

我們成功采用的另一種方法是使用 wsimport(來自 Ant,作為 Ant 任務)生成 WS 客戶端代理代碼並指定 wsdlLocation 屬性。

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

由於我們為帶有多個 WSDL 的項目運行此腳本,因此腳本會動態解析 $(wsdl.file} 值,該值設置為相對於 JavaSource 位置(或 /src,取決於您如何設置項目)。在構建過程中,將 WSDL 和 XSD 文件復制到此位置並打包在 JAR 文件中。(類似於上面 Bhasakar 描述的解決方案)

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

注意:確保 WSDL 文件使用對任何導入的 XSD 的相對引用,而不是 http URL:

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

生成的代碼中,我們發現:

/**
 * 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;
    }

}

也許這也可能有所幫助。 這只是一種不使用“目錄”方法的不同方法。

對於那些仍然在這里尋求解決方案的人,最簡單的解決方案是使用<wsdlLocation> ,而無需更改任何代碼。 工作步驟如下:

  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. 為了完整起見,我在這里提供完整的代碼生成部分,在生成的代碼中使用流利的 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>

非常感謝 Bhaskar Karambelkar 的回答,它詳細解釋並解決了我的問題。 但我也想用三個簡單的步驟為急於修復的人重新表述答案

  1. 將您的 wsdl 本地位置引用設為wsdlLocation= "http://localhost/wsdl/yourwsdlname.wsdl"
  2. 在 src 下創建一個 META-INF 文件夾。 將您的 wsdl 文件/s 放在 META-INF 下的文件夾中,例如 META-INF/wsdl
  3. 在 META-INF 下創建一個 xml 文件 jax-ws-catalog.xml 如下

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

現在打包你的罐子。 不再引用本地目錄,全部打包並在其中引用

對於那些使用 Spring 的人,您可以使用 classpath-protocol 簡單地引用任何 classpath-resource。 因此,對於 wsdlLocation,這將變為:

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

請注意,這不是標准的 Java 行為。 另請參閱: http ://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html

對於那些使用多個 wsdl 文件的人:

pom.xml

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>2.6</version>
<executions>
    <execution>
        <phase>generate-sources</phase>
        <goals>
            <goal>wsimport</goal>
        </goals>
        <configuration>
            <bindingDirectory>${basedir}/src/main/resources/jaxws</bindingDirectory>
            <bindingFiles>
                <bindingFile>binding.xjb</bindingFile>
            </bindingFiles>
            <wsdlDirectory>${basedir}/src/main/resources/wsdl</wsdlDirectory>
            <wsdlFiles>
                <wsdlFile>VN_PCSApplicationManagementService_v21.xml</wsdlFile>
                <wsdlFile>VN_PCSApplicationOfferManagementService_v7.xml</wsdlFile>
                <wsdlFile>VN_PCSOnlineDebtService_v2.xml</wsdlFile>
            </wsdlFiles>
        </configuration>
    </execution>
</executions>

創建服務客戶端時:

@Bean
public ApplicationOfferManagementWSV7 getAppOfferWS() {
    String wsdlLocation = OnlineDebtWSv2Soap11QSService.class.getAnnotation(WebServiceClient.class).wsdlLocation().replaceFirst(".+wsdl/", "/wsdl/");
    URL url = this.getClass().getResource(wsdlLocation);
    ApplicationOfferManagementWSV7 applicationManagementWSV21 = new ApplicationOfferManagementWSV7Soap11QSService(url)
            .getApplicationOfferManagementWSV7Soap11QSPort();

    BindingProvider binding = (BindingProvider) applicationManagementWSV21;
    binding.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
    binding.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
    binding.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, offerEndpoint);

    return applicationManagementWSV21;
}

在我的情況下,我必須從 .classpath 文件中刪除“src”條目的(排除 =“META-INF”)然后在代理類中將 wsdl 路徑從 /META-INF 更改為 META-INF

有與此處描述的完全相同的問題。 不管我做了什么,按照上面的例子,改變我的 WSDL 文件的位置(在我們的例子中來自一個 Web 服務器),它仍然引用嵌入在服務器進程的源樹中的原始位置。

嘗試調試了很多小時后,我注意到異常總是從完全相同的行中拋出(在我的情況下為 41)。 最后今天早上,我決定將我的源客戶端代碼發送給我們的貿易伙伴,這樣他們至少可以了解代碼的外觀,但也許可以構建自己的代碼。 令我震驚恐懼的是,我在客戶端源代碼樹中發現一堆類文件與我的 .java 文件混合在一起。 多么奇怪! 我懷疑這些是 JAX-WS 客戶端構建器工具的副產品。

一旦我刪除了那些愚蠢的 .class 文件並對客戶端代碼進行了徹底的清理和重建,一切正常! 重蹈覆轍!!

YMMV,安德魯

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM