I will omit the web service contract design phase and assume we are developing SOAP web service and WSDL (or XSD schema) is already available. The best way (in my opinion) to generate Java model from WSDL is to use JAXB2 specification implementation. There are several ways to get it done:
- using xjc compiler
- using Maven 2.x/3.x plugin (I lean towards this one and use it all the time)
1 2 3 4 5 6 7 8 9 10 11 12 | < plugin > < groupid >org.jvnet.jaxb2.maven2</ groupid > < artifactid >maven-jaxb2-plugin</ artifactid > < version >0.7.5</ version > < configuration > < schemalanguage >WSDL</ schemalanguage > < schemadirectory >src/main/resources</ schemadirectory > < schemaincludes > < include >*/*.wsdl</ include > </ schemaincludes > </ configuration > </ plugin > |
Ok, so we have our Java classes (with JAXB2 annotations), generated from our WSDL. As picture describes, we have UserProfileService web service with UserProfile operations which accepts UserProfileRequest as input and returns UserProfileResponse. In case of exception, UserProfileFault has been returned.
Let's do some routine work in order to configure Spring application context with Spring Web Services specific beans: message factory, marshallers and endpoint processors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <!--xml version="1.0" encoding="UTF-8"?--> < beans xmlns = "http://www.springframework.org/schema/beans" xmlns:context = "http://www.springframework.org/schema/context" xmlns:ws = "http://www.springframework.org/schema/web-services" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation=" < ws:annotation-driven ></ ws:annotation-driven > < ws:static-wsdl id = "UserProfile" location = "classpath:UserProfile.wsdl" ></ ws:static-wsdl > < bean class = "org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping" ></ bean > < bean id = "jaxbPayloadMethodProcessor" class = "org.springframework.ws.server.endpoint.adapter.method.jaxb.JaxbElementPayloadMethodProcessor" ></ bean > < bean id = "defaultMethodEndpointAdapter" class = "org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter" > < property name = "methodArgumentResolvers" > < list > < ref bean = "jaxbPayloadMethodProcessor" ></ ref > </ list > </ property > < property name = "methodReturnValueHandlers" > < list > < ref bean = "jaxbPayloadMethodProcessor" ></ ref > </ list > </ property > </ bean > < bean id = "messageFactory" class = "org.springframework.ws.soap.saaj.SaajSoapMessageFactory" ></ bean > </ beans > |
And here is how our web service endpoint looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package org.example; import javax.xml.bind.JAXBElement; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ws.server.endpoint.annotation.Endpoint; import org.springframework.ws.server.endpoint.annotation.RequestPayload; import org.springframework.ws.server.endpoint.annotation.ResponsePayload; import org.springframework.ws.soap.server.endpoint.annotation.SoapAction; @Endpoint public class UserProfileEndpoint { @SoapAction ( "UserProfile" ) public @ResponsePayload JAXBElement< UserProfileResponse > getUserProfile( @RequestPayload JAXBElement< UserProfileRequest > request ) { ... } } |
- @Endpoint annotation tells Spring Web Services that we have web service endpoint
- @SoapAction annotation (on the methods) tells Spring Web Services that method in question is responsible to handle particular SOAP action
- @ResponsePayload and @RequestPayload annotations tell Spring Web Services to extract payload from SOAP message, deserialize it to Java classes (using JAXB2 binding), and match input/output parameters against declared handlers (methods)
That's pretty much it to make things work! Spring Web Services takes care about all boiler-plate code and allows to concentrate on what really matters - implementation.
So, web service is up and running. Aside from very basic features, Spring Web Services allows to configure response/request validation (against XSD schema). To do that, we need just to add validation interceptor to the application context:
1 2 3 4 5 6 7 | < ws:interceptors > < bean id = "validatingInterceptor" class = "org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor" > < property name = "schema" value = "classpath:UserProfile.xsd" ></ property > < property name = "validateRequest" value = "true" ></ property > < property name = "validateResponse" value = "true" ></ property > </ bean > </ ws:interceptors > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package org.example; import org.springframework.stereotype.Component; import org.springframework.ws.context.MessageContext; import org.springframework.ws.soap.SoapFault; import org.springframework.ws.soap.server.endpoint.SimpleSoapExceptionResolver; @Component public class UserProfileEndpointExceptionResolver extends SimpleSoapExceptionResolver { public UserProfileEndpointExceptionResolver() { super (); // Let this handler precede all other handlers super .setOrder( HIGHEST_PRECEDENCE ); } @Override protected void customizeFault( MessageContext messageContext, Object endpoint, Exception ex, SoapFault fault ) { // Customize here your SOAP fault with some details } } |
3 comments:
Hi Andriy,
Thanks alot for this information. I Found it extremely useful.
Now I need to be able to set the security headers(username Token) in the MockWebServiceClient.
As the web service interceptor(XwsSecurityInterceptor) checks for the username token.
please help.
thanks
jaskaran
(jaskaran81@yahoo.com)
hi i was able to fix my issue with help from:-
http://stackoverflow.com/questions/2274378/add-soapheader-to-org-springframework-ws-webservicemessage
Hi Karan,
Thank you a lot for sharing such a very useful information!
Post a Comment