Skip to main content

locally packaged WSDL can't be found?

21 replies [Last post]
tomstrummer
Offline
Joined: 2006-06-30

Hi -- I have a generated WS client with a locally packaged WSDL, using the wsdlLocation annotation as demonstrated here:

https://metro.dev.java.net/guide/Developing_client_application_with_loca...

However, when I attempt to create a service instance, it can't find the WSDL location. Here's the entire generated initializer:

<br />
/**<br />
 * This class was generated by the JAX-WS RI.<br />
 * JAX-WS RI 2.1.3-b02-<br />
 * Generated source version: 2.1<br />
 *<br />
 */<br />
@WebServiceClient(name = "MarketQueryService", targetNamespace = "http://emkt.pjm.com/emkt/xml/wsdl", wsdlLocation = "./pjm-emkt-prod.wsdl")<br />
public class MarketQueryService<br />
    extends Service<br />
{</p>
<p>    private final static URL MARKETQUERYSERVICE_WSDL_LOCATION;<br />
    private final static Logger logger = Logger.getLogger(com.pjm.emkt.emkt.xml.wsdl.MarketQueryService.class.getName());</p>
<p>    static {<br />
        URL url = null;<br />
        try {<br />
            URL baseUrl;<br />
            baseUrl = com.pjm.emkt.emkt.xml.wsdl.MarketQueryService.class.getResource(".");<br />
            url = new URL(baseUrl, "./pjm-emkt-prod.wsdl");<br />
        } catch (MalformedURLException e) {<br />
            logger.warning("Failed to create URL for the wsdl Location: './pjm-emkt-prod.wsdl', retrying as a local file");<br />
            logger.warning(e.getMessage());<br />
        }<br />
        MARKETQUERYSERVICE_WSDL_LOCATION = url;<br />
    }<br />

The see the warning "Failed to create URL for the wsdl Location..." I've verified that the WSDL is in fact packaged in the JAR under /com/pjm/emkt/emkt/xml/wsdl. But what in fact is happening, I believe, is that

<br />
com.pjm.emkt.emkt.xml.wsdl.MarketQueryService.class.getResource(".")<br />

is returning null, then the next line:
<br />
new URL( baseUrl, "./pjm-emkt-prod.wsdl" );<br />

fails with a java.net.MalformedURLException: no protocol: ./pjm-emkt-prod.wsdl -- likely because the baseUrl is null, and between the two there is no protocol (whereas if baseUrl was a file:// path like I would expect, the pjm-emkt-prod.wsdl location would be found relative to that base URL).

Class.getResource says searching is defined by the underlying classloader... So I thought maybe somehow a custom classloader in Maven or JUnit could be messing that up. But if I run the same code in a java main method it fails the same.

Can someone please elicit the finer points of getResource for me? I should mention, this is wsimport- generated code, the only thing I've specified is the wsdlLocation! If anyone can point me to other correct uses of wsdlLocation maybe that would help. Ideas? (Java SE 1.6.0_06)

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
tomstrummer
Offline
Joined: 2006-06-30

I suppose I should have read your question a little more closely as well. META-INF just needs to be at the 'root' of your JAR, so you could put it right under your source folder, or if you're using Maven it would go under src/main/resources/(META-INF/). This puts it at the root of the classpath, so code can look up the file by calling someClassloader.getResource("/META-INF/jax-ws-catalog.xml")

Does that make sense? So your JAR should look like this:
[code]
my-client.jar
|
\- META-INF
| |
| \- jax-ws-catalog.xml
|
\- com
|
\- mycompany
|
\- SomeClass.class
[/code]

lockwood
Offline
Joined: 2009-01-23

Thanks for the quick reply. That is where I currently have the catalog file - in the META-INF/ folder on the root of the jar file.

It is very weird, when I put the catalog file there, once I try to connect to the service the app just stops responding. Without the catalog file I get an error the WSDL cannot be found.

lockwood
Offline
Joined: 2009-01-23

OK, I figured it out. The catalog file was fine and the WSDL was ok. My problem was the WSDL for the service had changed (other people are working on the service). I remembered to get the new WSDL but I forgot to get the new schema (xsd) file referred to by the WSDL. Once I got the new schema all worked well.

Thanks for the help and verifying I was doing things right!

lockwood
Offline
Joined: 2009-01-23

I am also trying to run a JAX-WS client as a Web Start application and am having problems with the WSDLlocation being resolved.

I have tried to create a catalog file as suggested but I am a little confused on where I put the catalog file (jax-ws-catalog.xml). A few posts suggested META-INF/jax-ws-catalog.xml but this is just a Web Start application (not a Web app) so where do I put the META-INF folder and how does the JRE know to find it?

tomstrummer
Offline
Joined: 2006-06-30

META-INF should be the default location, but if that does not seem to work, the wsdl parser can also accept a catalog location parameter. I forget what it is on the command line, but this is what the Maven configuration looks like:

[code]

org.codehaus.mojo
jaxws-maven-plugin

${basedir}/src/main/resources/META-INF/jax-ws-catalog.xml

pjm-emkt-${env}.wsdl

${wsdlLocation}


[/code]

At least I think that will work; it's been a while since I have worked on this project.

Clive Brettingham-Moore

I have been using the systemId to uri mapping with no problems (as in
your first case). I must admit that reading of the standard suggests
that uri resolution may be more correct, but system is what the jax-ws
examples use, so that is probably the safest starting point.
Probably the next step is to check that you have correct inputs - is the
generated service class definitely using this
(http://emkt.pjm.com/emkt/xml/wsdl) exact URL.
What is the exception? Presumably it gives a URL that is not being found
- if it the the abstract URL then at least you have got that far.
Then I'd check that the jar contains wsdl (catalog and wsdl should be in
the same jar) where it should be (I assume that ${env} is substituted in
your actual catalog file):
com/pjm/emkt/emkt/xml/wsdl/pjm-emkt-${env}.wsdl

If you are feeling ambitious you can get hold of an actual JAX-WS
resolution config in runtime via
com.sun.xml.ws.util.xml.XmlUtil.createDefaultCatalogResolver()
returns a org.xml.sax.EntityResolver (which might explain why systemIds
tend to be used).

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@metro.dev.java.net
For additional commands, e-mail: users-help@metro.dev.java.net

tomstrummer
Offline
Joined: 2006-06-30

Actually, I tried using the catalog in a JUnit test. My test asserts that both the catalog and WSDL are in their expected locations on the classpath before invoking the service class. This is what I've got for my generated service class:

[code]
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.1.3-b02-
* Generated source version: 2.1
*
*/
@WebServiceClient(name = "MarketQueryService", targetNamespace = "http://emkt.pjm.com/emkt/xml/wsdl", wsdlLocation = "http://emkt.pjm.com/emkt/xml/wsdl")
public class MarketQueryService
extends Service
{

private final static URL MARKETQUERYSERVICE_WSDL_LOCATION;
private final static Logger logger = Logger.getLogger(com.pjm.emkt.emkt.xml.wsdl.MarketQueryService.class.getName());

static {
URL url = null;
try {
URL baseUrl;
baseUrl = com.pjm.emkt.emkt.xml.wsdl.MarketQueryService.class.getResource(".");
url = new URL(baseUrl, "http://emkt.pjm.com/emkt/xml/wsdl");
} catch (MalformedURLException e) {
...
[/code]

And yes, the catalog file on my classpath in /META-INF/jax-ws-catalog.xml looks like this when the environment variables are filled in at build time:
[code]

uri="../com/pjm/emkt/emkt/xml/wsdl/pjm-emkt-prod.wsdl" />
alternateuri="/com/pjm/emkt/emkt/xml/wsdl/pjm-emkt-prod.wsdl" />

[/code]

So I'm expecting the wsdlLocation to get replaced by a relative classpath. Is that correct?

At runtime the service fails because the wsdlLocation URL happens to return a 401 -- meaning it's trying to hit the system URL rather than it being replaced by the correct path from the catalog.
[pre]
com.sun.xml.ws.wsdl.parser.InaccessibleWSDLException: 2 counts of InaccessibleWSDLException.

java.io.IOException: Server returned HTTP response code: 401 for URL: http://emkt.pjm.com/emkt/xml/wsdl
java.io.IOException: Server returned HTTP response code: 401 for URL: http://emkt.pjm.com/emkt/xml/wsdl?wsdl

at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(RuntimeWSDLParser.java:172)
[/pre]

Could you give a working example of a service that uses a catalog file?

Again, I know I can work around it several ways but my goal is to have the generated code do what it's supposed to w/o manual modification, and not have to specify a WSDL manually at runtime.

Thanks again for your help.

tomstrummer
Offline
Joined: 2006-06-30

Turns out the "system" URI has to be relative to the catalog file. Can't be an absolute classpath (although that is arguably 'relative' to the root). I thought I had tried this but apparently not. So since my catalog file is in /META-INF, the URI had to look like this:
[code]
uri="../com/pjm/emkt/emkt/xml/wsdl/pjm-emkt-${env}.wsdl" />
[/code]

Still more work than it should be if you ask me.. I can't imagine why standard classpath resolution is too much to ask for. I don't think I mentioned, I had previously opened an issue here (https://jax-ws.dev.java.net/issues/show_bug.cgi?id=627) with IMO a simple solution.

Again, thanks everyone for helping me through the patchy documentation. Hopefully others coming down this path will find this thread and have an easier time figuring it out than I did. :)

d_papworth
Offline
Joined: 2008-10-24

Tom,

I found this article useful for solving a similar problem I was having with WSDLs. I've written two unit tests that will identify when WSDL locations fail to resolve and when the locations do not match entries in jax-ws-catalog.xml.

[code]
import org.junit.Test;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

import java.net.URL;

import static com.sun.xml.ws.util.xml.XmlUtil.createDefaultCatalogResolver;
import static junit.framework.Assert.fail;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

public class MyService_ServiceTest {

@Test
public void testWSDLResolution() {
// Shouldn't throw exception if the WSDL location is able to be resolved
new MyService_Service();
}

@Test
public void testLocalWSDLResource() throws Exception {
// Ensure that WSDL location maps to a local resource
MyService_Service service = new MyService_Service();

// Replicate entity resolving in WSServiceDelegate/RuntimeWSDLParser
URL wsdlDocumentLocation = service.getWSDLDocumentLocation();
Source source = new StreamSource(wsdlDocumentLocation.toExternalForm());
EntityResolver resolver = createDefaultCatalogResolver();
try {
InputSource inputSource = resolver.resolveEntity(null, source.getSystemId());
assert(inputSource != null);
} catch (Exception e) {
fail("Should not have thrown " + e.getMessage());
}
}
}
[/code]

Obviously MyService_Service should be substituted for the service being tested.

Cheers,
Damien.

vinodsingh
Offline
Joined: 2004-04-16

Recently I also came across a requirement where I need to use locally packaged WSDL for creating the instance of the client and use the live URL during service method invocation. This worked well, I used it in following manner-

// during instantiation use the locally packaged WSDL
WebServiceClient ann = AddressService.class.getAnnotation(WebServiceClient.class); 
URL wsdlURL = this.getClass().getResource("/wsdl/AddressService.wsdl");

AddressService serviceObj = new AddressService(wsdlURL, new QName(ann.targetNamespace(), ann.name()));
Address service = serviceObj.getAddressPort();
BindingProvider bp = ((BindingProvider) service);
Map context = bp.getRequestContext();

// set the live URL of the service, which will be used during service method invocations
context.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "LIVE URL OF THE WEB SERVICE");
some more information is available here http://blog.vinodsingh.com/2008/12/locally-packaged-wsdl.html

agoblet
Offline
Joined: 2007-07-20

Hello,

Here is the code I use :

URL url = null;
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
url = classloader.getResource("WEB-INF/wsdl/mywsdl.wsdl");

regards,
arnaud

ritzmann
Offline
Joined: 2003-06-19

The only reliable way to read a resource within a Servlet is to go through ServletContext. I assume this is a servlet if you are reading resources from WEB-INF. Otherwise, Servlet containers may be very restrictive about what resources you may access.

agoblet
Offline
Joined: 2007-07-20

This isn't used in servelt but in webstarted app. The main Jar is organised with a WEB-INF folder.
regards

lockwood
Offline
Joined: 2009-01-23

I am also trying to run a JAX-WS client as a Web Start application and am having problems with the WSDLlocation being resolved.

I have tried to create a catalog file as suggested but I am a little confused on where I put the catalog file (jax-ws-catalog.xml). A few posts suggested META-INF/jax-ws-catalog.xml but this is just a Web Start application (not a Web app) so where do I put the META-INF folder and how does the JRE know to find it?

vinodsingh
Offline
Joined: 2004-04-16

Look at: http://blog.vinodsingh.com/2008/05/webservice-endpoint.html that is how I invoke web services in production.

Thanks,
Vinod

tomstrummer
Offline
Joined: 2006-06-30

Thank you Vinod...

I'm aware how to set the endpoint at runtime. That's what I need to do currently since the @wsdlLocation is useless. The default "file://..." wsdlLocation is almost completely non-portable as an absolute path, and a relative wsdlLocation seems to not work at all when it is actually packaged in a JAR.

I think I'm going to have to call this a bug unless someone can tell me that Metro example still works for them if it's JAR'd. If anyone else can confirm that it [i]doesn't[/i] work for them, that would be reassuring too.

Thanks.

Clive Brettingham-Moore

While the broken treatment for relative URLs in the generated service
class is a concern, and I'd have to agree a bug (personally, I would
have though searching for the class resource would be a better bet than
"."), it is only a fringe case (which is probably why no one noticed).
I thought the "normal" way to package local WSDL for JAX-WS is to set a
"formal" (typically absolute, may or may not be resolvable) URL for the
WSDL using the wsdllocation option to wsimport, and use
jax-ws-catalog.xml to make the formal URL resolve to the packaged resource.
But I see looking at the Metro documentation just now, it seems they
have decided this is "more work" that the relative URL approach... that
doesn't work (semantics of relative URL here AFAICS from the standard
are a bit uncertain anyway).

My advice it to use the absolute URL/catalog approach since this is
definitely portable and compliant with the JAX-WS standard (may want to
put in a bug for the relative case tho since if they have it in the docs
it should work).

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@metro.dev.java.net
For additional commands, e-mail: users-help@metro.dev.java.net

tomstrummer
Offline
Joined: 2006-06-30

Hi Clive --

Thanks for your help. I've started looking at catalog files and I may be on the right track but still can't get it to work. I created a META-INF/jax-ws-catalog.xml file on my classpath. First I tried this:

[code] uri="../com/pjm/emkt/emkt/xml/wsdl/pjm-emkt-${env}.wsdl" />
[/code]

which didin't work, but doesn't systemId infer that I'm using a DTD somewhere? I tried changing it to this:
[code] alternateuri="../com/pjm/emkt/emkt/xml/wsdl/pjm-emkt-${env}.wsdl" />
[/code]

... which also didn't work -- at least not in a testcase. Note that I changed the wsdlLocation to the "http://emkt.pjm...." URL, which should be replaced by the relative resource URL, right? I think I'm on the right track but still a bit lost. Did I mention this documentation is awful? :)

Thanks again.

maneshhere
Offline
Joined: 2008-08-04

Hell,

I have tried locally packaged wsdl and it worked out for me. I have never used ./a.wsdl. I was using something like ../wsdl/a.wsdl. Also, you could just try a.wsdl instead of ./a.wsdl. I am not sure if that would help, but you could give a try.

Thanks,
Manesh

tomstrummer
Offline
Joined: 2006-06-30

Doing a little more research, I took the example application that is attached to the Metro documentation (https://metro.dev.java.net/guide/portable-client-withwsdl.zip) and modified that.

I added a bit of debug output to the client class like so:
[code]
public class Client {

public static void main(String[] args) {
System.out.println( "Local directory (.) resource: " + MtomService.class.getResource(".") );
System.out.println( "Relative resource: " + Client.class.getResource("../Soap11MtomUtf8.svc.xml") );
System.out.println( "Absolute resource: " + MtomService.class.getResource("/Soap11MtomUtf8.svc.xml") );
//... WS call code
[/code]

And then rebuilt and JAR'd the code like so:
[pre]
$ ant client # rebuild the code
Buildfile: build.xml
....

$ cd build/classes/

$ jar -cf ../test.jar *

$ java -cp ../test.jar:$METRO_HOME/lib/webservices-rt.jar:$METRO_HOME/lib/metro-1.2/lib/webservices-api.jar client.Client
Local directory (.) resource: null
Relative resource: null
Absolute resource: jar:file:/home/tnichols/dev/sandbox/portable-client-withwsdl/test.jar!/Soap11MtomUtf8.svc.xml
Sep 15, 2008 11:16:46 AM client.MtomService
WARNING: Failed to create URL for the wsdl Location: '../Soap11MtomUtf8.svc.xml', retrying as a local file
Sep 15, 2008 11:16:46 AM client.MtomService
WARNING: no protocol: ../Soap11MtomUtf8.svc.xml
Sep 15, 2008 11:16:47 AM com.sun.xml.ws.mex.client.MetadataClient retrieveMetadata
WARNING: MEX0008:Failed to parse metadata returned from server at file:/home/tnichols/dev/sandbox/Soap11MtomUtf8.svc.xml using protocol SOAP_1_2. Continuing attempts.
Sep 15, 2008 11:16:47 AM com.sun.xml.ws.mex.client.MetadataClient retrieveMetadata
WARNING: MEX0008:Failed to parse metadata returned from server at file:/home/tnichols/dev/sandbox/Soap11MtomUtf8.svc.xml using protocol SOAP_1_1. Continuing attempts.
Sep 15, 2008 11:16:47 AM com.sun.xml.ws.mex.client.MetadataClient retrieveMetadata
WARNING: MEX0008:Failed to parse metadata returned from server at file:/home/tnichols/dev/sandbox/Soap11MtomUtf8.svc.xml/mex using protocol SOAP_1_2. Continuing attempts.
Sep 15, 2008 11:16:47 AM com.sun.xml.ws.mex.client.MetadataClient retrieveMetadata
WARNING: MEX0008:Failed to parse metadata returned from server at file:/home/tnichols/dev/sandbox/Soap11MtomUtf8.svc.xml/mex using protocol SOAP_1_1. Continuing attempts.
Exception in thread "main" javax.xml.ws.WebServiceException: Failed to access the WSDL at: file:/home/tnichols/dev/sandbox/Soap11MtomUtf8.svc.xml. It failed with:
/home/tnichols/dev/sandbox/Soap11MtomUtf8.svc.xml (No such file or directory).
at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(RuntimeWSDLParser.java:162)
at com.sun.xml.ws.wsdl.parser.RuntimeWSDLParser.parse(RuntimeWSDLParser.java:144)
at com.sun.xml.ws.client.WSServiceDelegate.parseWSDL(WSServiceDelegate.java:264)
at com.sun.xml.ws.client.WSServiceDelegate.(WSServiceDelegate.java:227)
at com.sun.xml.ws.client.WSServiceDelegate.(WSServiceDelegate.java:175)
at com.sun.xml.ws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:104)
at javax.xml.ws.Service.(Service.java:56)
at client.MtomService.(MtomService.java:46)
at client.Client.main(Unknown Source)
Caused by: java.io.FileNotFoundException: /home/tnichols/dev/sandbox/Soap11MtomUtf8.svc.xml (No such file or directory)
[/pre]

As you can see, the "local path" resource fails from within the JAR, as does a relative path.

So -- either
1. My environment is somehow very messed up...
2. The documentation is incorrect
3. There is an implementation bug here, in that using wsdlLocation to point to an embedded resource is essentially useless. To be fair, the generated code works if wsdlLocation uses any absolute URL (file:// or otherwise) but it seems the intent of the generated code (MyServiceClass.getResource(".").... ) was to allow for an embedded resource relative to the service class path. This obviously does not work. Someone please correct me if I'm off base. Attached is the generated JAR and the modified Client.java class.

Thanks.

weiresr
Offline
Joined: 2007-09-13

Hi all,

I've had the same problems recently when trying to package and use my WSDLs along with the generated client classes into a client jar for my Web Service.

I came to a different and very easy solution (without catalogs), which is (so far) working fine in different environments for me. Here it goes:

As stated before, my main problem also was the line with the
[code]
getResource(".");
[/code]
in the generated Service class, which always returns null if everything is packed into a jar.

If I however change this line into
[code]
getResource("");
[/code]
(i.e., use an empty string here), it's suddenly working fine.

So now I'm using the plain WSDL filename (without any path) as wsdlLocation in wsimport, put the WSDL into the same package as my generated classes, do an automatic replacement of the generated Service class source as shown above, recompile the classes and pack everything into a client jar - and everything is working fine for me.

I'm however not really sure if this is a clean solution, or if this may fail in certain environments, too (don't fully understand what getResource() is supposed to do for an empty input string). Anyway, for me this works like a charm [b]both in standalone and webapps[/b], using the standard constructor of the service class (and by manually changing the endpoint address property afterwards to define the actual target endpoint).

Any thoughts appreciated.

If you're interested, here is a snippet of my ant buildfile for doing all this:

[code]

sourcedestdir="${src.dir}" wsdl="${wsdl.dir}/${wsdl.file}"
wsdlLocation="${wsdl.file}" package="${target.package}" />









[/code]