segunda-feira, 15 de julho de 2013

Simple Document/Wrapped SOAP JAX-WS WebServices Tutorial with Maven+Tomcat+Failsafe

Technology credits:
Apache Maven & Failsafe
Apache Tomcat



Disclosure

Before you start reading further: Keep in mind that this is a work in progress, and although i'm a "Systems&Informatics Engineer" i consider myself a novice programmer. If you have any question you don't see answered here, please leave your comment on the section below to help me improve this article.
Also, you should be aware that this post was a result of a lot of work. If you like it, please feel free to show your appreciation by offering me a cup of coffee through paypal and/or add value to this article by sharing your knowledge.

Objectives of this tutorial:

  • To establish a standard "archetype" for Document/Wrapped SOAP JAX-WS WebServices development
  • To demonstrate how a Maven project can build-deploy-test a Document/Wrapped SOAP WebService
Pre-requisites:
  • You have knowledge on Plain Old Java Objects (POJO's) development.
  • You understand the concept of WebService, it's benefits and are able to decide when and how it should be used.
  • You understand the diference between a RESTful WebService and a SOAP WebService
  • You understand the concept of "server side container" and it's purpose.
  • You have downloaded and installed the Maven build software, or you are using Maven through the m2e Eclipse plug-in
The sources for this project are available here. Just unzip it and build it using 'mvn clean verify' (on the cmd line or 'clean verify' targets using the m2e Eclipse plugin)

Conventions over Configuration

First of all, as we will be using the Maven WAR plugin we should be aware of the project's file structure that is required by this plugin (as well as other conventions required by Maven which i won't discuss here)
Don't mind the red 'X' over the pom.xml (the m2e was having a bad day).
As you can see from the picture above, all you need to build and deploy a WebService to Tomcat are this 5 files. Be aware that the Maven WAR plugin requires you to have the web.xml and sun-jaxws.xml at that specific location so that they can be included in the deployment to Tomcat. We will talk about each file in the following sections.

What should the pom.xml contain?

The pom.xml is the Maven build file and contains all the required configurations to build, deploy and test the project. In this tutorial, it only contains the configurations that are absolutely essential for this project to pass the tests. I will talk here about the essentials of the configuration that prepare the WAR for proper Tomcat deployment and the Failsafe plug-in.

Get ready for WAR

So... What do we need to get started? First of all let's start by calling some friends to the WAR :P We will need some "dependencies" to resolve our classpath needs:
This are JAX-WS RT related dependencies: It's basically a placeholders library that enables the Maven compiler to build the WAR. You can use non-placeholders libraries as long as you build the WAR using the <scope>provided</scope>, otherwise Tomcat will fail to load the WAR using his internal JAX-WS RT library. This dependency will download a lot of other related dependencies to your project.

Next, we will need to generate a lot of "WebServices related artifacts" based on the Annotations (like @WebService and @WebMethod) present in our WebService class such as WSDL files and other Java classes to handle the WebServices requests:
Noticed the <sei>ext.domain.pkg.MyService</sei>? This is the way we tell the plug-in to process that class and generate the artifacts. I wish there was a way to make this plugin behave like JUnit so that you wouldn't need to configure each and every WebService...
Also: Did you noticed the commented Codehaus MOJO plug-in reference? This will also work if uncommented, but will show you a "deprecated warning" at build time.

The WAR builder shall be configured to exclude the web.xml file: To avoid warnings and because a web.xml can already be present at a Tomcat production server.

Now that the WAR is built we need to deploy it to a Tomcat container. From what i know, there are maven plugins that enable you to deploy to Jetty, JBoss and also Grizzly(a client side J2EE stack), to do that, just change the following section with the appropriate plugin configuration.
On the configuration above we are saying that the server shall start before the integration-test phase and stop right after the integration-test phase. Because of that you shall be aware that invoking the Maven targets 'pre-integration-test' and 'integration-test' will leave you with a running instance of the Tomcat 7 server. The next Maven build will regain control over the embedded Tomcat if required (shutting it down if you call the post-integration-test target).

Now that we have our working server, we need to run the integration tests. For that purpose we shall configure the Failsafe plugin:
This will run all our tests started with IT*.java or ended in *IT.java or *ITCase.java. This classes are just like standard JUnit tests, only the filename pattern is different. It should be attached to the integration-test phase as well to the verify phase in order to check if some error has ocurred and fail the build.

The WebService

The web service i used is very simple, just as an hello world service you can see anywhere else. Here is the code:

The WebService configuration files

I acknowledge that it takes quite a configuration effort to put the WebService "up and running" but this is the last step. Take it easy :)
We need two things: To configure the web.xml and the sun-jaxws.xml files.

First things first! The web.xml: What are we saying here?
First of all, a WebService is not a Servlet. A servlet is the basic "entrance point" for any J2EE application. It's like a Main class for J2SE. Because of that, we must tell Tomcat which Servlet do we want him to invoke at what address. As we didn't develop any, we must use one from another provider :) That is were the com.sun.xml.ws.transport.http.servlet.WSServlet comes along to save our back. Next, we tell him to start that Servlet when someone calls http://****/MyService
Ok, now Tomcat will start the WSServlet, but how will the WSServlet know what WebService to run at that address? We must configure that in the sun-jaxws.xml file:
Here we tell com.sun.xml.ws.transport.http.servlet.WSServletContextListener the class name of our implementation and the url where it should be available. Notice that the url-parameter must be the same as the servlet-mapping url-pattern so that the Servlet can deploy an Endpoint at http://******/MyService

Manually test the WebService deployment

By running 'mvn pre-integration-test', you shall now be able to point your browser at http://localhost:8080/MyService and see the following:
You can now go drink a cup of coffe, as you have sucessfuly deployed your very simple WebService using Maven and Tomcat :D Hurray!

Integration test programmaticly

As good software developers that we all are :D we should have developed the WebService Integration Tests first :D But as i stated earlier, i'm a n00b :P and left the tests for last. Good thing i'm not on a schedule (or the tests wont ever be done).

For testing i used Failsafe, the Surefire counterpart that executes on Maven's integration-test phase.
As the test "script" is a bit more "tricky" (as i didn't wanted it to be even more trikier using pre-generated classes from WSDL, as you would normally use for the general development of WebService clients, since this is a pretty simple WebService), i will explain it step-by-step.

To test this service we need to send a SOAP message like the following to our Endpoint:
How can we do it? (you ask)
We can save the code above to a file named justPayload.xml and save it in src/test/resources/justPayload.xml as stated here, and load it using: The other option (which i prefer) is to build the document in-memory using a XML DocumentBuilder like so: This enables us to have full control over the XML document generation and the possibility to change the WebService method argument programmaticly (if we need to test the same method with several values).

After you've decided which kind of XML loading you prefer, we need to send the SOAP request to our Endpoint located at http://localhost:8080/MyService. To do that we can use the following:

That's it! Just run your 'mvn clean verify' and you should be able to "succed your build" with 0 failures.

Happy Coding!