Last week I attended the SpringOne Congress in Antwerp. Just one or two days before the congress I wanted to create a proof of concept for a client connecting to a webservice. I have been using the proxy way (spring-remote), but I did not really like the way this works. Therefore I was looking for something else. I stumbled upon the Spring Webservices project. I did try this out before, but not for clients. I started working and it did not look very complicated. I must admit I needed the help from Arjen Poutsma himself to make it work, but now I have a very light weight solution. I like the approach very much, therefore I will briefly describe the solution I have created. You can of course find the sources online as well. Check the google code subversion repo for gridshore.
Overview of application
Build application using maven2
Web application build using spring webmvc
Webservice client created using spring-ws
Mapping between objects and xml using jaxb2 and spring-oxm
Maven 2 for building
This time not much details about the maven2 part. There are some things interesting though. Most important part is the creation of the jaxb generated classes using the maven2 jaxb2 plugin.
Using spring-webmv for web project
I do not think it is really interesting to talk a long time about this. There can be found a lot (better) resources online. I used the spring webmvc project to make the client i18n compatible, used the form controllers from spring to help with the error messages and validation stuff. For the webservices part the most interesting thing starts in the file service-applicationcontext.xml. This spring configurtion file contains the webservice client, the converters for jaxb (using generics to abstract the technique marshalling technique).
Webservice client created using spring-ws
Let's start by having a look at the configuration:
This configured class is the class that knows how to connect to the webservice. It uses a few values and classes to do it's work. The defaultUri is used to find the endpoint to connect to (we obtain it from a property file). Since we use JAXB2 to marshall and unmarshall the request we need the marshaller and unmarshaller. These (un)marshallers are provided by spring-ws as well. Actually they are provided by spring-oxm. The interface for the marshaller is the same as for the unmarshaller. The only thing you need to provide is the package where the jaxb files are generatd in. The other two references are converters used to convert the application specific components to JAXB2 components. More on this later as well. So the most important class is the Gateway class. Let's have a look at that now.
public class CongressRegistrationGateway extends WebServiceGatewaySupport implements CongressRegistrationDAO {
private final Converter
This is it, really. I show you the complete class, but in the end the most important works takes place in one line. That's the one in bold. Here we use the special webServiceTemplate class and ask it to marshall our request object and send it as a soap message to the configured endpoint. Ofcourse you can do a lot more, but for most situations this is all it takes. Do not forget to extend the WebServiceGatewaySupport class.
Mapping between objects and xml using jaxb2 and spring-oxm
Not much to tell here as well. I keep saying this, but that's because it is true. We have configured the marshaller and the unmarshaller which are provided by spring-oxm. We also call the right method from the template adapter called marshalSendAndReceive. I did create an interface and some implementations that enable the gateway to be ignorand to the type of marshalling and unmarshalling we use. This is my first try to do something with generics. There can be a better way, if you know one, please let me know as well . Let's start with the generic interface.
public interface Converter {
public E convert(T toBeConverted);
}
So we have an interface specifying we have a object as a parameter and an object as a return type. Then there is a method called convert that uses these generic types. At the moment I have two concrete implementations for creating the JAXB request and handling the JAXB Response. One of them is presented below.
public class CongressRegistrationResponseConverter implements Converter {
public String convert(CongressRegistrationResponse toBeConverted) {
String response = toBeConverted.getRegistrationCode();
return response;
}
}
As you can see we now have a concrete implementation for the generic interface. The returned object is a String and the parameter is a JAXB object called CongressRegistrationResponse. Now look back at the gateway code, check that there is no jaxb reference in that code.
Again check out the code at google code subversion repo for gridshore. You can already find the code for a server implementation there as well. The next blog will deal with the server. I am also working on a sample about the Rule engine called Drools. so stay tuned.
Hi, thanks for the great example. Can you tell me if the spring ws client configuration can be used to call some other non-spring web services? I can make your example work locally, but when I try to call a service defined by a wsdl supplied by a client I cannot get it to work. Does the server side need to be using the same saaj libraries or something?
with web services, client and server are (theoretically) completely decoupled. This means that none of the libraries must be shared by both parties. In the end, all communication is done by XML over HTTP.
It might be possible that the WSDL you client provides is not WS-I compliant, meaning that some implementations have problems generating the required XML.
What you could do to identify the problem is to use a proxy, such as tcpmon. Send your messages to the tcpmon application locally, which then forwards it to the actual endpoint. That way, you can see the actual XML being sent to the web service provider. It could give you a hint why your message isn't processed as expected.
Hi, thanks that got me a bit further. I used tcpmon and found out that I need to populate all fields in my jaxb obect. So once I did that and pulled my soap message out of tcpmon it runs successfully in SoapUI. But I still get an error in my Tomcat log and it does not seem to work from my Spring client set up. I am getting the following...
org.springframework.ws.soap.saaj.SaajSoapEnvelopeException: Could not access envelope: Unable to create envelope from given source because the root element is not named "Envelope";
It seems strange that the Soap message runs fine from SoapUI but not from my Spring client.
thanks,
Kieran.
Comments (3)
#1.1.1
Kieran O'Sullivan
on
2008-07-04 16:23
(Reply)
excellent example, thanks. I was using these technologies but barking up the wrong tree to some extent, so this has saved me lots of time.
a few code chunks are missing, particularly the generics stuff, but the Google Code repository version works fine, and is a great example of how to structure WS code.
Pretty! This was a really wonderful post. Many thanks for providing these
details.
Comment (1)
#5.2
Authentic Amulets
(Link)
on
2012-08-23 04:34
(Reply)
It didnt make sense to me in the first look. I could not believe that it will be this easy. At some point this post helped me a lot get things done. Thanks a lot !!!
Thank u so much for this depth explaining about how different elements gets connected t each other..for me the most important was webServiceTemplate ..Thank you and please keep up this good work.