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!

sexta-feira, 22 de fevereiro de 2013

Deadlock

So, "What is a Deadlock?" you might ask. If you already know what a Deadlock is skip the next paragraph.

Imagine a bowl of soup with one spoon and two persons. Each person must hold the bowl and the spoon in order to eat the soup. One person grabs the bowl, the other person grabs the spoon. They both wait for each other for either the spoon or the bowl to take a "bite" on the soup. This situation represents a "Deadlock".

Earlier this week i've discovered a "Deadlock" situation on my MySQL 5.5.28 database. I've been noticing spurious "deadlocks" for a long time since the affected database became live. I've checked my software several times and i was certain that nothing i was doing could trigger a deadlock, not multithreading issues, not multiprocessing issues, not even multi-client issues since each client accessed exclusive ranges of data on the same table, so i thought how could this be?

After lots of research i've discovered a page that said that MySQL is not obliged to always return a result without a "deadlock" and that we should always "retry" to fetch the required information. Since i don't consider myself an expert in this area of expertise i decided to give it a go and developed a retry cycle to "attack" the deadlocking table.

After a month of normal operations on the live database another deadlock was found, this time in another table and on an insert operation. I went berzerk. How can this be? If the record does not exist on the database how can it be on deadlock!? If there is no record, no one else could be trying to access it!

I digged deeper on this second deadlock and discovered that the insert operation on that table had an "Insert Trigger" that requested an update operation on the previouly discovered deadlocking table (let's call it A). A forensic analysis to the MySQL database revealed that at the time of the Deadlock there was 2 clients trying to access Table A. Client 1 was accessing the table in a SELECT...FOR UPDATE and Client 2 in the INSERT operation to table B. Bear in mind that Client 1 is trying to select a range of data that is already mutually exclusive from the range of data that is going to be updated by the INSERT TRIGGER on Client 2, so conceptualy there are no reasons for deadlocking there.

So? What the hell is going on? I've read some foruns and here is my explanation:

  • The first SELECT from Client 1 on table A is selecting a range of data that will be discarded. I'm sorry oracle, but trying to lock rows that i don't need doesn't seem very efficient to begin with.
  • Although the INSERT operation on table B (that TRIGGERS an UPDATE on table A) is being executed as an atomic transaction the resources required for that transaction to happen are not being atomically requested, thus leaving a chance for this deadlock to happen.

Because of this two events Client 2 would hold a shared lock on Table A in order to INSERT on Table B while Client 1 would request the same row (to be rapidly discarded) on Table A which would then be needed by Client 2 to execute the UPDATE TRIGGER on Table A leaving the two requests on a deadlock.

What a mess!

I'm now using MySQL 5.5.30 and i didn't changed to 5.6.10 because that database version failed my system's database tests.

That is another mystery that i will try to solve. Stay tuned.