[SWS-281] add support for imported xsd in static wsdl as well as xsd's that import other xsd's Created: 06/Feb/08  Updated: 01/May/08  Resolved: 01/May/08

Status: Closed
Project: Spring Web Services
Component/s: Core
Affects Version/s: None
Fix Version/s: 1.5.1

Type: New Feature Priority: Major
Reporter: Mark LaFond Assignee: Arjen Poutsma
Resolution: Fixed Votes: 8
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Spring WS 1.0.2


Attachments: Zip Archive springws.zip    
Issue Links:
Duplicate
is duplicated by SWS-250 Enable MessageDispatcherServlet to s... Closed
Related
is related to SWS-179 Create and use some wrapper around ja... Closed
is related to SWS-271 Request to inline included XSD's in a... Closed
Supersede
is superseded by SWS-346 HandlerAdapter for XsdSchemas Closed

 Description   

Currently, Spring WS does not seem to have support for serving up xsd's that are imported into static wsdl. There is also no support for xsd's that import other xsd's. Both situations are considered best practices by schema and wsdl designers.

The schemaLocation attribute should be transformed in the same way that the location attribute is transformed for wsdl's today to include the request specific server info depending on where the application is deployed.

Usage Examples:

wsdl (with imported xsd)
request
http://localhost:7001/Services/8.02/Example?wsdl

response (partial)
<wsdl:definitions name="Example" targetNamespace="http://services">
<wsdl:types>
<xs:schema>
<xs:import namespace="http://customer/types" schemaLocation="http://localhost:7001/Services/8.02/Example?Customer.xsd"/>
</xs:schema>
</wsdl:types>

xsd (with imported xsd)
request
http://localhost:7001/Services/8.02/Example?Customer.xsd

response (partial)
<xs:schema targetNamespace="http://customer/types">
<xs:import namespace="http://order/types" schemaLocation="http://localhost:7001/Services/8.02/Example?Order.xsd"/>



 Comments   
Comment by Mark LaFond [ 06/Feb/08 ]

I've attached a .zip that contains source code for a full working example. Please see the readme.txt for details.

I followed the pattern already in use by the WsdlDefinition code. Although it seems a little clunky to define .xsd's individually in the spring config file it does allow for the resource location to be completely configurable. In my case, I'm actually using Xml Beans to create a separate .jar via Maven so I can simply get the xsd's and wsdl's off the classpath instead of duplicating them within the .war project in maven.

Comment by Arjen Poutsma [ 27/Feb/08 ]

Hey Mark,

The XsdDefinition interface in the zip looks interesting, I am working on putting it into 1.5. Not sure whether the schema translation feature will be added, because there is also SWS-271 to think about. My current thoughts to solve this are as follows:

  • Inline all imported XSDs into the WSDL, getting rid of the schemaLocation of the import tag. .NET 3.0 uses a similar trick.
  • inline all included XSDs into the including XSD, getting rid of the include altogether.

In the end, you still have the advantage of separate schema files, but they are served as one WSDL. I am interested in hearing your thoughts on this solution.

Before I can add the code in the attached zip to the code base, I need your formal approval to releasing this code under the Apache License version 2. I know that you added the license tags to the Java files already, so I suppose you have no issue with it, but I do need that approval.

A comment in this issue will do.

Thanks

Comment by Ray Harrison [ 06/Mar/08 ]

Hi Arjen,

Mr. LaFond is soaking up the sun in Hawaii this week but should be able to provide his approval next week. Here at Comcast, we are happily using 1.02 and 1.03 variants using Mark's additions and would welcome the solution you propose or a mixture of the two.

Cheers
Ray Harrison

Comment by Arjen Poutsma [ 08/Mar/08 ]

I've implemented the inlining approach, and it seems to work well.

Comment by Arjen Poutsma [ 08/Mar/08 ]

Closing issues in 1.5 RC1.

Comment by Mark LaFond [ 17/Mar/08 ]

I'm not sure that inlining the schemas is the best solution. I'll hold my comments on trying to mimic what .NET is doing as a good thing Please keep in mind, my solution focuses on statically defined wsdl, not dynamically generated wsdl. In general, static wsdl doesn't seem to be supported nearly as well in spring-ws as is the dynamically generated approach. This is very true when the static wsdl imports schemas and those schemas import other schemas. This is considered a best practice by many in the SOA community as it promotes reuse of types.

I think that spring-ws will be more successful if it supports static wsdl as well as it does dynamic wsdl. Even though spring-ws doesn't necessarily need the static wsdl in order to function, it should still provide standard ways to access it (via ?wsdl query parm on the url) and standard ways to resolve all location specific elements and attributes embedded within.

My thoughts are that the static wsdl and associated schema files are essential parts of defining the Web services contract and that they should be preserved in the form that they were created with as few modifications as possible. In our case, we tried to follow best practices by defining types in separate schema files and then import those schemas into the statically defined wsdl and other schemas. This leads to better reusability of types. The only modification to the wsdl and xsd my solution provided was to resolve the location specific aspects, mostly URL's. Inlining creates one big huge unreadable, unmaintainable wsdl in our case. If I were to look at that from an outsiders point of view, I would come to the conclusion that the wsdl designer didn't know what he was doing. Popular tools like SoapUI can easily pull in all of the schemas using my solution, it just walks through the wsdl and gathers all of the nested schemas (and schemas that import other schemas), pretty standard stuff.

So while inlining the schemas is functionally equivalent to importing them, it's not very good at all from a design standpoint. You're obviously free to do what you want since spring-ws is your framework but for now we are going to have to keep our modifications in place in order to use spring-ws in a way that is more natural to us (and most likely others). I would like to see these modificiations be included in spring-ws so that others may benefit but that's kind of up to you. FWIW, Weblogic provides similiar functionality in regards to static wsdl and nested schemas.

The other thing I'd like to point out is that most projects of any size will most likely have separate .jars for their XMLBeans (or equivalent XML to Java binding) classes. In our case, we did not want to have to drop the .xsd into both the XMLBeans maven project AND the .war project. Instead, it was fairly easy to simply make a classpath reference in the spring config for the schemas.

Comment by Mark LaFond [ 17/Mar/08 ]

Or, if you want to allow the user to decide how they want to view it you could add a query parm on the URL such as:

  • ?wsdl&inline=true
  • ?Order.xsd&inline=true

That's probably a little more flexible that hardwiring it into the spring config.

Comment by Arjen Poutsma [ 17/Mar/08 ]

Ok, I've reopened the issue and will take a look at the other enhancements you made for 1.5 final/ the next RC.

Comment by Erik-Berndt Scheper [ 17/Mar/08 ]

I second Mark's opinion, that xsd's in a static wsdl generally need not be inlined, but should be made available as is. We are also using complex xsd structures and including / importing them makes allows us to keep each xsd concise and readable.

Maybe this could be resolved in a similar way as Axis2 supports this, like so:
*publish the static wsdl as http://some.url/appcontext/services/myservice?wsdl
including a modified location of each included / imported xsd

*publish each xsd recursively resolved as
http://some.url/appcontext/services/myservice?xsd=xsd0
http://some.url/appcontext/services/myservice?xsd=xsd1
including a modified location of each included / imported xsd

  • and a startup error in the case a referenced xsd cannot be resolved.
Comment by Arjen Poutsma [ 17/Mar/08 ]

I think Mark's solution actually works like you said, with the ?xsd queries. The one problem I have is that in Spring-WS, there is no concept of a Service; there are only endpoints. Multiple endpoints can make up a service, of course. So how would you relate one of the potentially many Wsdl11Definition beans to a service? It's relatively easier to relate the XSDs to the WSDL, allowing http://some.url/appcontext/services/myservice?xsd=xsd0, because they are referenced.

One thing to note is that the current solution only imports and includes XSDs in the WSDL served at runtime. You still have files which you can edit separately, just like you had before. And, as SWS-271 indicates, some clients actually do have issues with included and imported XSDs.

Comment by Arjen Poutsma [ 19/Mar/08 ]

I'd really like to put this in 1.5, but I'm seeing some issues, and time is running out. I might have to postpone this to a later release.

@Mark

I am looking into the XsdDefinitionHandlerAdapter: is there any particular reason why you used a different algorithm in the transformSchemaLocation() method than the existing one in WsdlDefinitionHandlerAdapter#transformLocation() ?

I mean, using your example in the readme, can't we just expose Customer.xsd as http://localhost:7001/Services/8.02/Customer.xsd rather than http://localhost:7001/Services/8.02/Example?Customer.xsd ? This seems more consistent with the WSDL approach that we already have.

@Erik-Berndt
I am not sure how Axis2' solution works, but I do know that imports/includes get tricky, especially when considering circular references. Using your example: what if http://some.url/appcontext/services/myservice?xsd=xsd0 imported xsd1, which imports xsd0? Where would those schemas be made available?

I'd rather have more explicit approach, with individual SimpleXsdSchema bean definitions such as Mark proposed, rather than to try to figure out things automagically .

Comment by Erik-Berndt Scheper [ 19/Mar/08 ]

When I read your comments, I realized that the Axis2 implementation is indeed not appropriate here, because there is no concept of a service but only of endpoints and wsdl definitions. I can think of two solutions that are more in line with the current SimpleWsdl11Definition implementation, which is actually quite nice.

First of all, the idea is to add an optional property xsdLocator to the SimpleWsdl11Definition, which can be used to link the xsd's to a statically defined wsdl.

The default implementation of this XsdLocator, let's say the SimpleXsdLocator, should have two properties, xsdLocation and xsdLocations of type Resource and Resource[], so that it is possible to locate multiple xsd's using constructs like 'classpath:/resources/xsd/*/.xsd'

Then we need a way to publish them so that the xsd's can be loaded from a URL. This could be achieved in two ways:
1. Add an extra servlet mapping to web.xml (*.xsd) and publish them in a similar way to the SimpleWsdl1Definition already does. But this leads to a complication: the static wsdl and (possibly) xsd's need to be modified so that each xsd can recursively be resolved correctly. The most simple way seems to skip all path prefixes and therefore require each xsd name to be unique.
This would lead to the following published wsdl and xsd's
http://some.url/appcontext/myservice.wsdl returning the wsdl definition (resolved by the SimpleWsdl11Definition)
http://some.url/appcontext/xsdA.xsd returning an xsd (resolved by the SimpleXsdLocator)
http://some.url/appcontext/xsdB.xsd returning an xsd (resolved by the SimpleXsdLocator)

2. But if this uniqueness of xsd names is required then it might be much easier to resolve the xsd's using the servlet mapping that is already in place (that of the SimpleWsdl11Definition), and let the SimpleWsdl11Definition publish them instead. This would lead to the following published wsdl and xsd's
http://some.url/appcontext//myservice.wsdl returning the wsdl definition
http://some.url/appcontext//myservice.wsdl?xsd=xsdA.xsd returning an xsd resolved using delegation to the SimpleXsdLocator
http://some.url/appcontext//myservice.wsdl?xsd=xsdB.xsd returning another xsd also resolved by delegating to the SimpleXsdLocator

The advantage of this alternative is that the user does not need an extra servlet mapping (for *.xsd) and (maybe) that there is a direct link to xsd's used by a specific wsdl.

Comment by Arjen Poutsma [ 27/Mar/08 ]

Pushing this past 1.5.0 as we need some input from Mark.

Comment by Ray Harrison [ 27/Mar/08 ]

Hi Arjen,
We can work for a future release. Mark is buried in getting some releases out and hasn't had much time to devote to this particular item. We will probably always keep our own addition to allow for the ability to use the query parameter for referencing the WSDL. Comcast has a rather large and complex organizational structure and we need to be able to play nicely with how other teams expect to interact with our web services.

That said, exposing the XSD via the standard Spring-WS as you suggest is perfectly workable from my perspective.

Thanks,
Ray

Comment by Arjen Poutsma [ 01/May/08 ]

Closing this, as the work is at least partially done. I've created a separate issue (SWS-346) for the handler adapter, which you can vote for if you need it.

Generated at Mon Dec 11 00:24:39 UTC 2017 using JIRA 6.4.14#64029-sha1:ae256fe0fbb912241490ff1cecfb323ea0905ca5.