This is the _short_ version.
We write a few wsdls here. We’ve been exposing them with plain stock JAXWS built into java 6, along with JAX-WS Commons and regular spring. This has mostly been working fine. We even have some maven archetypes to generate the templates around this and it’s all well and good.
As long as your schemas and types are all in a single .wsdl file
You see, jax-ws commons is basically unmaintained. As you can see in the quickstart, you should just need this…
<wss:binding url="/sub">
<wss:service>
<ws:service bean="#myService" />
</wss:service>
</wss:binding>
<!-- this bean implements web service methods -->
<bean id="myService" class="foo.MyService" >
<property name="something" value="somevalue"/>
</bean> |
<wss:binding url="/sub">
<wss:service>
<ws:service bean="#myService" />
</wss:service>
</wss:binding>
<!-- this bean implements web service methods -->
<bean id="myService" class="foo.MyService" >
<property name="something" value="somevalue"/>
</bean>
And, that actually works. Except…. Even if you started from a wsdl, and then created a service that implemented the wsdl, the wsdl jax-ws commons will expose at runtime is generated dynamically. So none of that documentation and and xml annotations you slaved over for hours/days/weeks are actually visible anywhere.
Ok, No big deal! It’s still all standards based, we just lost our documentation. Welllll. Only if you added more annotations specifying the namespaces. If you just have the @WebService(serviceInterface = “the.generated.from.wsdl.Interface”) jax-ws commons will make you a webservice whose runtime wsdl will have a namespace created from your implementation package name! (Instead of the originally defined wsdl namespace)
So you can add more annotations, oh, one of them is wsdl location! sweet! oh. no. It can’t reference into a jar.
So you look at the wss: namespace based config, oh look, we can specify the wsdl there too! Oh. no. It can’t reference into a jar. Hmm, the schema’s broken. primaryWsdl is an attribute, but can’t be used as such…
You check the documentation again, and give it a go… Ok, primaryWsdl is a child element? Ok! this seems to work!
Then you look at your runtime wsdl…
<xsd:import schemaLocation="Core_v1.xsd" namespace="http://core.vf.is/dom/v1"/> |
<xsd:import schemaLocation="Core_v1.xsd" namespace="http://core.vf.is/dom/v1"/>
Hmm, how’s it going to see that file at runtime? Oops. you’ve just made an invalid wsdl. Back to the docs….. what docs? There are none. but suffice to say, there’s a magic key that does what you want… ws:metadata It’s defined as taking a list of schemas. When you get this right, it knows how to magically create runtime links to them. Of course, there’s a bug for this, but no-one’s working on it.
Except, as we mentioned, jax-ws commons is effectively unmaintained. There’s a bug in the schema that only allows one metadata element, instead of a list. So you can go and use the full expanded plain old spring config. But good lord that’s ugly. And how on earth would you have survived this far?
<wss:binding id="wscustomer" url="/services/customer1">
<wss:service>
<bean class="org.jvnet.jax_ws_commons.spring.SpringService">
<property name="bean" ref="servicesCustomer1"/>
<property name="impl" value="is.vf.test.ws.customers.v1.CustomersV1Impl"/>
<property name="primaryWsdl" value="customers_v1.wsdl"/>
<property name="serviceName">
<bean class="javax.xml.namespace.QName">
<constructor-arg index="0" value="http://core.vf.is/customers/jaxws/"/>
<constructor-arg index="1" value="customers_v1"/>
<constructor-arg index="2" value="tns"/>
</bean>
</property>
<property name="portName">
<bean class="javax.xml.namespace.QName">
<constructor-arg index="0" value="http://core.vf.is/customers/jaxws/"/>
<constructor-arg index="1" value="customers_v1SOAP"/>
<constructor-arg index="2" value="tns"/>
</bean>
</property>
<property name="metadata">
<list>
<value>Core_v1.xsd</value>
<value>Customers_v1.xsd</value>
<value>ProductCatalog_v1.xsd</value>
</list>
</property>
</bean>
</wss:service>
</wss:binding> |
<wss:binding id="wscustomer" url="/services/customer1">
<wss:service>
<bean class="org.jvnet.jax_ws_commons.spring.SpringService">
<property name="bean" ref="servicesCustomer1"/>
<property name="impl" value="is.vf.test.ws.customers.v1.CustomersV1Impl"/>
<property name="primaryWsdl" value="customers_v1.wsdl"/>
<property name="serviceName">
<bean class="javax.xml.namespace.QName">
<constructor-arg index="0" value="http://core.vf.is/customers/jaxws/"/>
<constructor-arg index="1" value="customers_v1"/>
<constructor-arg index="2" value="tns"/>
</bean>
</property>
<property name="portName">
<bean class="javax.xml.namespace.QName">
<constructor-arg index="0" value="http://core.vf.is/customers/jaxws/"/>
<constructor-arg index="1" value="customers_v1SOAP"/>
<constructor-arg index="2" value="tns"/>
</bean>
</property>
<property name="metadata">
<list>
<value>Core_v1.xsd</value>
<value>Customers_v1.xsd</value>
<value>ProductCatalog_v1.xsd</value>
</list>
</property>
</bean>
</wss:service>
</wss:binding>
There’s patches that aren’t applied, bugs not fixed, it’s a zoo. But yeah, you can make it work with the very manual and repetitive raw spring bean config.
But where did webmethods come into this?
Well, basically, it just fails abominably when it gets one of these wsdls that references unreachable schemas. And if you were using the autogenerated ones, and weren’t super careful, you ended up with types in webmethods that weren’t actually the same type.
Did I mention that when you import a wsdl into webmethods, it creates a doc type for every type, even if the same type has already been imported via another wsdl? It gives you a warning for each duplicate type, but fortunately, at run time, it seems acknowledge that the two types are actually the same thing. Thank god.
Enough of this shit.
Want to do the same thing with CXF?
<jaxws:endpoint id="wsCustomer" implementor="#servicesCustomer1" wsdlLocation="customers_v1.wsdl" address="/customer1"
xmlns:kz="http://core.vf.is/customers/cxf/" serviceName="kz:customers_v1"
endpointName="kz:customers_v1SOAP"/> |
<jaxws:endpoint id="wsCustomer" implementor="#servicesCustomer1" wsdlLocation="customers_v1.wsdl" address="/customer1"
xmlns:kz="http://core.vf.is/customers/cxf/" serviceName="kz:customers_v1"
endpointName="kz:customers_v1SOAP"/>
You still have to put in the serviceName and enpointName, even though they just copied from the wsdl itself but hey, it works, it’s way more intuitive, and it’s less typing.
(Don’t even get me started on the quality of the generated types from CXF vs JAXWS-Commons.)
bleh.