JAX-WS Loading WSDL from jar

I'm writing a fat client that makes use of a SOAP service for some features (bug reporting etc.)

I've got JAX-WS working fine, but by default (in netbeans at least) it fetches the WSDL from the remote server every time the service is initialized. I expect this helps provide some versioning support etc., but it's not what I want.

I've added the wsdllocation arg to wsimport to point the generated classes to a local resource. The following snippet is the URL loading for the WSDL resource from ApplicationService.java.

baseUrl = net.example.ApplicationService.class.getResource(".");
url = new URL(baseUrl, "service.wsdl");

I'm pretty sure that should have no problems pointing to a resource stored inside a jar in the net/example/resources package, and the jar itself is constructed as expected. However the service will not load... specifically, I get a NullPointerException when I call ApplicationService.getPort();

Is this possible? or just a wild goose chase?


Yes this is most definitely possible as I have done it when creating clients through javax.xml.ws.EndpointReference, a WS-A related class. I have added a classpath reference to the WSDL to the WS-A EndPointReference and the Metro implementation of JAX-WS loaded it just fine. Whether loading the WSDL from the WS-A EndPointReference or from a file or http URL, your JAX-WS implementation should use the same WSDL parsing code as all you are doing is resolving URLs.

The best approach for you is probably to do something like the following:

URL wsdlUrl = MyClass.class.getResource(
            "/class/path/to/wsdl/yourWSDL.wsdl");

Service yourService= Service.create(
            wsdlUrl,
            ...);

Where ... represents the QName of a WSDL service inside of your WSDL. Now the important thing to remember is that your WSDL needs to be complete and valid. This means that if your WSDL imports XSD files or other WSDLs, the URLs must be correct. If you included your imported WSDL and XSDs in the same JAR as the WSDL file, you should use relative URLs for the imports and keep all of your imports in the same JAR file. The JAR URL handler does not treat the relative URLs as relative with respect to the classpath but rather to relative within the JAR file so you cannot have imports in your WSDL that run across JARs unless you implement a custom URL handler and your own prefix to do classpath based resolution of the imports. If your WSDL imports external resources, that is OK, but you are signing yourself up for maintenance issues if those resources ever move. Even using a static copy of the WSDL from your classpath is contrary to the spirit of WSDL, Web services, and JAX-WS, but there are times when it is necessary.

Finally, if you embed a static WSDL, I suggest that you at least make the service endpoint configurable for testing and deployment purposes. The code to reconfigure the endpoint of your Web service client is as follows:

  YourClientInterface client = yourService.getPort(
            new QName("...", "..."),
            YourClientInterface.class);
  BindingProvider bp = (BindingProvider) client;
  bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
                "http://localhost:8080/yourServiceEndpoint");

Atleast for the recent JAX-WS you don't need to do any schema catalogs or programmatic wsdl location setting IF you put the WSDL in the JAR and then set wsimport wsdlLocation to the relative resource path of the WSDL in the JAR. That is JAX-WS uses Java's builtin Class.getResource to load the WSDL.

If your using Maven its something like:

  <plugin>
    <groupId>org.jvnet.jax-ws-commons</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.3</version>
    <executions>
      <execution>
        <goals>
          <goal>wsimport</goal>
        </goals>
        <!-- Following configuration will invoke wsimport once for each wsdl. -->
        <configuration>
            <!--- VERY IMPORTANT THAT THE PATH START WITH '/' -->
    <wsdlLocation>/com/adamgent/ws/blah.wsdl</wsdlLocation>
    <wsdlDirectory>${basedir}/src/main/resources/com/adamgent/ws</wsdlDirectory>
    <wsdlFiles><wsdlFile>blah.wsdl</wsdlFile></wsdlFiles>
       </configuration>
      </execution>
    </executions>
  </plugin>

For the example above you would thus put the WSDL using Maven project layout here src/main/resources/com/adamgent/ws.

Make sure the WSDL gets in the JAR for Maven like:

<build>
      <resources>
        <resource>
          <directory>src/main/resources</directory>
        </resource>
      </resources> ....

Now your wsimport generated code and WSDL are in a self contained JAR. To use the service you do not have to set the WSDL location and is as easy as:

BlahService myService = new BlayService_Service().getBlahServicePort();

It should be trivial to map this over to ANT's wsimport.


Maybe a bit late, but I found a quite simple solution which worked to solve this problem, but this involved a change in the generated code of the Service class:

If the following line in the Service class

baseUrl = net.example.ApplicationService.class.getResource(".");

is changed to

baseUrl = net.example.ApplicationService.class.getResource("");

it works fine even with a WSDL that is packed within a JAR. Not sure about the exact supposed behaviour of getResource() in either of this cases, but I didn't experience any problems with this approach so far, on multiple OS and Java versions.


What you describe is a bug in JAX-WS: JAX_WS-888 - Wrong code for resolving the URL for a custom wsdlLocation.

It was fixed for V2.2, so just setting wsdlLocation, as you write, should work now.