views:

1008

answers:

4

Preface

We are providing customers with our service API.

Each customer has own subdomain (e.g. sergii.ourwebsite.com) and own WSDL URL, it looks like http://sergii.ourwebsite.com/api/bsapi.cfc?wsdl

Also, all the websites (including API, of course) using the same codebase.


Problem

Say, two applications on same CF-server. This can easily happen, because some of customer websites are hosted on our servers.

Both trying to use own API WSDL, say:

http://sergii.ourwebsite.com/api/bsapi.cfc?wsdl
http://galashyn.ourwebsite.com/api/bsapi.cfc?wsdl

And here come the problems.

When second website tries to register the web-service, CF throws an error:

Name: https://galashyn.ourwebsite.com/api/bsapi.cfc?wsdl. WSDL: https://galashyn.ourwebsite.com/api/bsapi.cfc?wsdl. org.apache.axis.wsdl.toJava.DuplicateFileException: Duplicate file name: /opt/coldfusion8/stubs/WS1985941973/api/Bsapi.java. Hint: you may have mapped two namespaces with elements of the same name to the same package name. It is recommended that you use a web browser to retrieve and examine the requested WSDL document to ensure it is correct. If the requested WSDL document cannot be retrieved or is dynamically generated, it is likely that the target web service has programming errors.

Problem is that both of them are using same WSDL namespace, built from CFC path:

<wsdl:definitions targetNamespace="http://api"&gt;


Current solution

The only working solution for us is using the CFC aliases, like:

http://galashyn.ourwebsite.com/api/v1n1/bsapi.cfc?wsdl
http://galashyn.ourwebsite.com/api/v1n1/bsapi.cfc?wsdl

Each this CFC extends the parent like this:

<cfcomponent output="false" extends="api.bsapi">
<!--- this component used to extend base api version 1.x --->
</cfcomponent>

They produce different namespaces, which can be used without problems -- own namespace for each application:

<wsdl:definitions targetNamespace="http://v1n1.api"&gt;
<wsdl:definitions targetNamespace="http://v1n2.api"&gt;


This is pretty dumb workaround, but it works for now.


Other solution would be to use the single API sub-domain and identifying the customers by some key (we are already using them for security purposes), but it has serious negative problems for us because of some legacy code.


Please note that I don't know Java, so many specific advices are not so clear for me.

Google shows that this problem exists for years, but I can't find the smart solution.

So maybe here?

+1  A: 

Do all the customers use the same WSDL? Then place the WSDL at a common URL for all customers.

I also think you need to find out exactly what the error message means. I don't see where it has anything to do with the URL being used. If it had mentioned the URL of another customer, I'd have understood.


Part of this may be due to my lack of understanding of how CF works. In particular, what is this "registering web services" that triggers the problem when two customers do it?

Did you realize that the URL in the <soap:address/> element in the WSDL is only a hint? In many clients, it can be overridden. In a .NET client, just set the Url property of the proxy class. This should allow you to have a single WSDL at a single location, and yet have your customers each reference the proper subdomain, assuming there's some way to convey that information to them. For instance, if there is a way for you to know which customer is making the call, then perhaps you could receive calls on one URL and then redirect to the proper URL, or else use a SOAP Routing infrastructure to route to the correct one.

I hope you are not changing the namespaces from one customer to another. Namespaces have nothing to do with URLs, even if they happen to look like URLs.

John Saunders
I can not "place the WSDL at a common URL for all customers" for now and I've explained why: because I have to use the sub-domains.If you know the way to put the WSDL at one URL and make service requests to another -- please tell me.
Sergii
"to find out exactly what the error message means" -- this is the reason of creating the question, right? It does not tell about the URL, but chain URL => namespace => problem with stub is pretty obvious.
Sergii
Hint about the <soap:address/> is useful, thanks. Will try to combine it with the Adam's proposition.
Sergii
A: 

There is a namespace attribute for the cfcomponent tag. You should be able to use this, along with cgi.host_name (? I'm at home, without docs) to specify a namespace that matches the subdomain being queried.

Something like:

<cfcomponent namespace="http://#cgi.host_name#/api/v1n1/bsapi.cfc"&gt;
Ben Doom
But this would be incorrect if it turns out all the WSDL are meant to be the same.
John Saunders
This is good idea and I've thinked of it too. But as I understand, there can not be used dynamic values in cfcomponent tag attributes. Am I wrong?
Sergii
I've not tried to use dynamic values there. Try it and see. If that does not work, you may have to unify the services at one single URL, which would also solve John's objection.
Ben Doom
+2  A: 

I can not "place the WSDL at a common URL for all customers" for now and I've explained why: because I have to use the sub-domains. If you know the way to put the WSDL at one URL and make service requests to another -- please tell me.

A WSDL is just an XML document that describes the web service. You can write (customize) it using CFML. For instance:

http://subdomain.domain.com/api/wsdl.cfm?api=bsapi&amp;customer=subdomain

Then just copy the WSDL generated by CF, and use it as a template for your custom WSDL page. Replace the parts of the WSDL that are subdomain-specific and return the XML document. Be mindful of whitespace (perhaps see CFSilent, CFSetting), and consider using CFHeader to set the mime type to "text/xml".

Adam Tuttle
This approach works for us pretty good. Thanks Adam!
Sergii
Glad I could help. :)
Adam Tuttle
A: 

Have caught this error message when tried to CFINVOKE webservice from localhost by test script located on same localhost (but in different folder). Sounds 'buggish' for me.

Rodion Bykov