Friday, December 31, 2010

Spring WS - MVC: Implementing a Client Tutorial

In this tutorial we will create a Spring WS web service client using the latest 2.0.0 RC2 build. We will integrate this client with a Spring MVC 3 application and provide a form where users can submit data. Our application is a front-end for a subscription web service, which we created in the tutorial Spring WS 2 and Spring 3 MVC Integration Tutorial. It's highly recommend to read that first because that is our web service.

We will assume that we only know the WSDL of the web service to simulate a real client development. To generate the proxy classes, we will use the built-in wsimport tool from the JDK. To access the web service, we will use Spring's WebServiceTemplate.

What is Spring Web Services (WS)?
Spring Web Services (Spring-WS) is a product of the Spring community focused on creating document-driven Web services. Spring Web Services aims to facilitate contract-first SOAP service development, allowing for the creation of flexible web services using one of the many ways to manipulate XML payloads. The product is based on Spring itself, which means you can use the Spring concepts such as dependency injection as an integral part of your Web service.

Source: Spring WS Reference 2.0
What is WebServiceTemplate?
The WebServiceTemplate is the core class for client-side Web service access in Spring-WS. It contains methods for sending Source objects, and receiving response messages as either Source or Result. Additionally, it can marshal objects to XML before sending them across a transport, and unmarshal any response XML into an object again.

Source: Spring WS Reference Chapter 6
What is wsimport?
The wsimport tool generates JAX-WS portable artifacts, such as:
  • Service Endpoint Interface (SEI)
  • Service
  • Exception class mapped from wsdl:fault (if any)
  • Async Reponse Bean derived from response wsdl:message (if any)
  • JAXB generated value types (mapped java classes from schema types)
These artifacts can be packaged in a WAR file with the WSDL and schema documents along with the endpoint implementation to be deployed

Source: Java™ API for XML Web Services (JAX-WS) 2.0
Let's start by downloading and examining the WSDL document from the web service which is accessible in the following URL:
http://localhost:8081/spring-ws-standalone/krams/ws/subscription.wsdl
subscription.wsdl

This document has one request subscriptionRequest and one response subscriptionResponse.

The subscriptionRequest accepts the following format:

The subscriptionResponse is what we will receive in return:

The service name is SubscriptionPortService. The port type is SubscriptionPort. If you need help in reviewing the basics of a WSDL document, please visit the following tutorial from w3schools.com WSDL Tutorial.

We got our WSDL document. Next we generate the Java proxy classes or precisely the JAX-WS portable artifacts

We will use wsimport to generate the artifacts. Open a terminal and type the following:
wsimport
And this is what you should see:

We need to keep the generated Java sources and specify the target directory. To do that we will include the -keep and -d options. Make sure you update the directory accordingly.

Here's the command to generate the classes

Type that in a terminal and hit enter. You should see the following:
parsing WSDL...


generating code...
Go to the directory where the file is saved. You should see a nest of folders. These folders correspond to the namespace of the WSDL document. Also, for each .class file, there's a coresponding .java file. That's the source code.

Here's an actual screenshot from my machine:

We have our WSDL. We have our artifacts. Notice we haven't done any Java or Spring development yet. These are just preparations.

Let's start the actual development. I'm using Eclipse and the m2eclipse plugin, so my instructions would be IDE-centric and Maven-centric as well.

If you're using Eclipse:
1. Create a new Dynamic Web project.

2. Create the top-level package, ie. org.krams.tutorial

3. Create a sub package, i.e org.krams.tutorial.oxm

4. Remember the files that we generated earlier? Copy all the .java files and paste them in the sub-package.

If you have the m2eclipse plugin:
1. Create a new Maven project.

2. Select the maven-archetype-webapp

3. Create a sub package, i.e org.krams.tutorial.oxm

4. Remember the files that we generated earlier? Copy all the .java files and paste them in the sub-package.

The source code for this tutorial is a Maven project which you can download the end of this tutorial.

Here's how the project looks like in my IDE:

We can now start developing our Spring MVC application.

We begin by declaring the primary controller.

MainController

This controller declares two mappings:
/main - for showing the subscribe page
/main/subscribe - for processing the subscription
Notice we have injected an instance of a WebServiceTemplate

Inside the doSubscribe() method, we process the subscription by delegating to the WebServiceTemplate

The WebServiceTemplate is able to marshall Java objects to XML and unmarshall from XML to Java objects again. In layman's term, marshall and unmarshall are another word for converting back and forth to different formats. If you need a thorough guide of the WebServiceTemplate, please check the official reference guide at SpringSource - Chapter 6. Using Spring Web Services on the Client

Once we get a valid response, we add it to the model:

If there are validation errors, the client will throw a SoapFaultClientException so we need to catch that:

SubscriptionResponse and SubscriptionRequest are JAX artifacts that were generated from the WSDL earlier. So instead of manipulating XML, we deal with Java objects.
A quick look at the contents of these two artifacts shows us the following:

SubscriptionRequest

SubscriptionResponse

Notice how the XML elements had been mapped to equivalent Java fields.

Our controller requires a single JSP page for displaying the form.

subscribepage.jsp

Here's the actual screenshot:

If there's a validation error, we see the following:

If successful, we get the following instead:

What's left are the required XML configurations.

To enable Spring MVC we need to add it in the web.xml

web.xml

Take note of the URL pattern. When accessing any pages in our MVC application, the host name must be appended with
/krams
In the web.xml we declared a servlet-name spring. By convention, we must declare a spring-servlet.xml as well.

spring-servlet.xml

By convention, we must declare an applicationContext.xml as well.

applicationContext.xml

This XML config declares three beans to activate the Spring 3 MVC programming model. There's also an extra bean that imports Spring-WS related configuration.

spring-ws.xml

For a detailed description of this configuration, please check the official Spring Reference: Chapter 6. Using Spring Web Services on the Client

That's it. We're done with our Spring WS web service client. We've managed to integrate a Spring MVC application for the submission of subscriptions, and utilized the convenient WebServiceTemplate.

To test the client and the web service provider, you need two instances of Tomcat or Jetty : one for the web service and one for the client. Make sure to set the ports accordingly. For the provider, I chose port 8081. For the client, I chose 8080. If you change the ports, you must update the spring-ws.xml configuration of the provider and the client as well. In case you forgot, the web service provider is in my other tutorial Spring WS 2 and Spring 3 MVC Integration Tutorial

To access the Subscribe Page, use the following URL:
http://localhost:8080/spring-ws-client/krams/main
Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring-ws-2-0-0-rc2-tutorial/

You can download the project as a Maven build. Look for the spring-ws-client.zip in the Download sections.

You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run

If you want to learn more about Spring MVC and integration with other technologies, feel free to read my other tutorials in the Tutorials section.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring WS - MVC: Implementing a Client Tutorial ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

18 comments:

  1. Thank you for your hard work, you made it very easy to learn the new annotations for Spring-WS 2.0!

    ReplyDelete
  2. Very nice article, very easy to understand and well set up. I have one question, and that is where does the payloadLoggingInterceptor log the requests to? I don't see them coming out in the console.

    ReplyDelete
  3. Question from a WS newbee. Your package structure in java is org.krams.tutorial but your structure in your wsdl is com.blogspot.kram915 . I was under the impression they should be the same. I can deploy your service and get the wsdl. Can you straighten me out?

    ReplyDelete
  4. Awesome!Thanks a lot mate for this such a detailed and nicely written tutorial.

    ReplyDelete
  5. Hi,
    Thanks for this nice tutorial.
    Is there a way you can write a tutorial with Spring-ws client with attachment (MTOM)?
    Thanks

    ReplyDelete
    Replies
    1. @Emmanuel, I'm not sure if I can do it soon :-) I'm glad you liked the tutorial

      Delete
  6. Hi!

    Very clear, thank you very much! I was so confused after reading the chapter 6 of Spring Reference! Thank for make it clear!

    If I have to call a WS within a secured conection (https), would I have to change something?

    Thank you very much!

    ReplyDelete
  7. Getting ClassCastException at line

    SubscriptionResponse response = (SubscriptionResponse) webServiceTemplate.marshalSendAndReceive(request);

    java.lang.ClassCastException: javax.xml.bind.JAXBElement cannot be cast to

    ReplyDelete
    Replies
    1. Maybe you have incompatible or old jar versions? Have you tried doing a Maven clean command? Also, I suggest using pastebin when pasting the exception so that we can see all of it.

      Delete
    2. I am using this version of spring-ws


      org.springframework.ws
      spring-ws-core
      2.1.2.RELEASE

      Delete
  8. Please follow the link - http://pastebin.com/238vQD5g

    I did a workaround to resolve this issue by casting with JAXBElement like this.

    JAXBElement response = (JAXBElement) webServiceTemplate.marshalSendAndReceive(request);
    SetScopeOutcomeResponseType responseType = response.getValue();

    I guess the Issue relates to ws-import as it was not generating @XmlRootElement for my generated Java Classes and I have to manually write that Element on top of the Class after Maven generates it.

    ReplyDelete
  9. I can't access http://localhost:8081/spring-ws-standalone/krams/ws/subscription.wsdl. For generating Client side Java code - Link could not be found error.
    I am getting HTTP Status 404 - /spring-ws-standalone/krams/ws/subscription.wsdl even if i use 8080 port Number.
    Can you please help me for accessing WSDl file.

    ReplyDelete
  10. Thanks for the nice article!

    I've followed your example, but my WSDL and generated classes are in another module. I've imported it through standard maven dependency, but somehow the JaxBMarshaller can't find the classes. The error is:

    javax.xml.bind.JAXBException: class XX nor any of its super class is known to this context.

    Do you have any idea how to fix this?

    --Frank Tan

    ReplyDelete
  11. in my case, the response retruns a list of object and the object attributes are always coming as null. Although the server side code is processing the request properly and returning the correct response. But the controller after the webServiceTemplate.marshalSendAndReceive the values are setting as null. I am not able to debug what is going wrong .Can anyone please help.

    Thanks a lot.

    ReplyDelete
  12. I have read your blog its very attractive and impressive. I like it your blog.

    Spring online training Spring online training Spring Hibernate online training Spring Hibernate online training Java online training

    spring training in chennai spring hibernate training in chennai

    ReplyDelete
  13. Very helpful suggestions that help in the optimizing website.
    thank for sharing the link.
    goldenslot

    ReplyDelete