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

179,305

Solution 1

One other approach that we have taken successfully is to generate the WS client proxy code using wsimport (from Ant, as an Ant task) and specify the wsdlLocation attribute.

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

Since we run this for a project w/ multiple WSDLs, the script resolves the $(wsdl.file} value dynamically which is set up to be /META-INF/wsdl/YourWebServiceName.wsdl relative to the JavaSource location (or /src, depending on how you have your project set up). During the build proess, the WSDL and XSDs files are copied to this location and packaged in the JAR file. (similar to the solution described by Bhasakar above)

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

Note: make sure the WSDL files are using relative refrerences to any imported XSDs and not http URLs:

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

In the generated code, we find this:

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

}

Perhaps this might help too. It's just a different approach that does not use the "catalog" approach.

Solution 2

For those who are still coming for solution here, the easiest solution would be to use <wsdlLocation>, without changing any code. Working steps are given below:

  1. Put your wsdl to resource directory like : src/main/resource
  2. In pom file, add both wsdlDirectory and wsdlLocation(don't miss / at the beginning of wsdlLocation), like below. While wsdlDirectory is used to generate code and wsdlLocation is used at runtime to create dynamic proxy.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
    
  3. Then in your java code(with no-arg constructor):

    MyPort myPort = new MyPortService().getMyPort();
    
  4. For completeness, I am providing here full code generation part, with fluent api in generated code.

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

Solution 3

Thanks a ton for Bhaskar Karambelkar's answer which explains in detail and fixed my issue. But also I would like to re phrase the answer in three simple steps for someone who is in a hurry to fix

  1. Make your wsdl local location reference as wsdlLocation= "http://localhost/wsdl/yourwsdlname.wsdl"
  2. Create a META-INF folder right under the src. Put your wsdl file/s in a folder under META-INF, say META-INF/wsdl
  3. Create an xml file jax-ws-catalog.xml under META-INF as below

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

Now package your jar. No more reference to the local directory, it's all packaged and referenced within

Solution 4

For those of you using Spring, you can simply reference any classpath-resource using the classpath-protocol. So in case of the wsdlLocation, this becomes:

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

Note that is not standard Java behavior. See also: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html

Share:
179,305
Admin
Author by

Admin

Updated on July 08, 2022

Comments

  • Admin
    Admin almost 2 years

    The problem is I need to build a web service client from a file I'm been provided. I've stored this file on the local file system and, while I keep the WSDL file in the correct file system folder, everything is fine. When I deploy it to a server or remove the WSDL from the file system folder the proxy can't find the WSDL and rises an error. I've searched the web and I've found the following posts yet I'm not been able to make it work:
    JAX-WS Loading WSDL from 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

    I'm using NetBeans 6.1 (this is a legacy application I've to update with this new web service client). Below is the JAX-WS proxy class :

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


    This is my code to use the 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 put a copy of the WSDL in web-inf/wsdl/client/SOAService, so I don't want to add it to META-INF too. Service classes are in WEB-INF/classes/com/ibm/eci/soaservice/ and baseurl variable contains the filesystem full path to it (c:\path\to\the\project...\soaservice ). The above code raises the error:

    javax.xml.ws.WebServiceException: Failed to access the WSDL at: file:/WEB-INF/wsdl/client/SOAService.wsdl. It failed with: \WEB-INF\wsdl\client\SOAService.wsdl (cannot find the path)

    So, first of all, shall I update the wsdllocation of the proxy class? Then how do I tell the SOAService class in WEB-INF/classes/com/ibm/eci/soaservice to search for the WSDL in \WEB-INF\wsdl\client\SOAService.wsdl?

    EDITED: I've found this other link - http://jianmingli.com/wp/?cat=41, which say to put the WSDL into the classpath. I'm ashamed to ask: how do I put it into the web application classpath?

  • IcedDante
    IcedDante almost 10 years
    I like this approach... but why the META-INF directory?
  • ᄂ ᄀ
    ᄂ ᄀ over 9 years
    Please note that this requires the use of JAX-WS RI 2.2, not 2.1 that comes with JDK 6 by default