<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6836009845114221640</id><updated>2012-02-16T08:43:43.827-08:00</updated><category term='maven'/><category term='clover'/><category term='bamboo'/><title type='text'>rand(alex)</title><subtitle type='html'>My random notes</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://alex.vanboxel.be/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default'/><link rel='alternate' type='text/html' href='http://alex.vanboxel.be/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Alex Van Boxel</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-jqaZc9ZsFp0/AAAAAAAAAAI/AAAAAAAAAO0/UF5osgU9z90/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>5</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6836009845114221640.post-964420526042974177</id><published>2012-01-23T13:43:00.000-08:00</published><updated>2012-01-24T15:51:06.960-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bamboo'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><category scheme='http://www.blogger.com/atom/ns#' term='clover'/><title type='text'>Aggregated code coverage using Maven, Clover and Bamboo</title><content type='html'>Finally we got it working... we wanted to know what our total test coverage was on our product with all the effort we did over the last year by adding new tests. It wasn't easy because once you leave the path of simple unit testing you come in the terrain of multi-server setups. With multiple server it's not easy anymore too collect all your metrics and get a descent report. But we pulled it off. So here's our story.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-Ze674f9S5rQ/TxvvdvjCUlI/AAAAAAAAAbw/VZ0u3DSPC3I/s1600/clover_plan.PNG" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="200" src="http://2.bp.blogspot.com/-Ze674f9S5rQ/TxvvdvjCUlI/AAAAAAAAAbw/VZ0u3DSPC3I/s200/clover_plan.PNG" width="173" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;We're using the complete tool chain of Atlassian, so this story involves their tools. In this case Clover (the code coverage tool), Bamboo (Continues Integration server) and Maven (as out build tool). Let's start with Clover. Clover is quite a clever coverage tool. It integrates in your build tool to create a special version of your product. It changes the source by injecting code to do  the instrumentation. At the same time it will build a database with a collection of all the methods that are instrumented. With the special build of your product and the database it will collect metrics while your tests are running. So with a single Maven command line we had a complete coverage report of our unit tests.&lt;br /&gt;&lt;br /&gt;But the tight integration with our build tool comes at a price. That price is not knowing what magic is going on behind the screen. And you need to know how everything fits together whenever you want to get more out of your tools. And that's what we wanted... Let's start at the beginning and start building our instrumented server.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-large;"&gt;Building the instrumented server&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://1.bp.blogspot.com/-adnWdj5OBrY/TxvxtzCrTII/AAAAAAAAAb4/DfKC0rWu6Mk/s1600/clover_sharedartifacts.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="142" src="http://1.bp.blogspot.com/-adnWdj5OBrY/TxvxtzCrTII/AAAAAAAAAb4/DfKC0rWu6Mk/s320/clover_sharedartifacts.PNG" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;As I said, the Clover plugin for Maven does a lot a magic and after a normal Clover maven build you get a nice report of what the code coverage is of your unit tests. Getting this into Bamboo (our integration server) is easy. Start a source code checkout, do the clover maven build as you would on the commandline and publish the report as an artifact. Set this up first before you go any further. This will be the basis for our next phase and will already provide some usefull insights.Then it's time to think about what we need if we want to get metrics on our server running on different machines. Lets have a look at the components.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Our instrumented product. Thats the easy part, your instrumented server will be located at the usual place in your maven project after a build. Just make sure that you don't deploy this special version to your local or remote repository, so limit yourself to package (this still runs the unit tests).&lt;/li&gt;&lt;li&gt;This special build needs the clover.jar to be present on the application server. In our jboss server it's enought that it's present in the lib folder. Make sure to look at the server logs when you deploy your ears. If you find clover related NoSuchMethod exception the jar is in the wrong place.&lt;/li&gt;&lt;li&gt;Last thing we need to get on the server is the clover.db, without this database you will not get eny metrics.&lt;/li&gt;&lt;li&gt;The last component we need to worry about is how to get the instrumentation metrics in a consistent manner so we can get it easily from the remote server back to our Bamboo agent for later processing.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Looking at the list, the biggest problem is the clover.db, by default each maven module within a maven project has it's own database. This would be a pain if we wanted to distribute those to a different server. Luckily we can force clover to build a single database by providing a path to the database. Although this had the strange effect that the report was generated in an unstable location (it's actually the last module in the maven reactor). But you can force the report path as well. Here is an example of the modification in our parent pom:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&lt;properties&gt;&lt;br /&gt;    &lt;clover.version&gt;3.1.0&lt;/clover.version&gt;&lt;br /&gt;    &lt;clover.licenselocation&gt;/opt/shared/license/clover.license&lt;/clover.licenselocation&gt;&lt;br /&gt;    &lt;clover.databasepath&gt;/opt/work/clover/example&lt;/clover.databasepath&gt;&lt;br /&gt;    &lt;clover.reportpath&gt;${clover.databasePath}&lt;/clover.reportpath&gt;&lt;br /&gt;    &lt;clover.enabled&gt;false&lt;/clover.enabled&gt;&lt;br /&gt;&lt;/properties&gt;&lt;br /&gt;...&lt;br /&gt;&lt;build&gt;&lt;plugins&gt;&lt;br /&gt;&lt;plugin&gt;&lt;br /&gt;    &lt;groupid&gt;com.atlassian.maven.plugins&lt;/groupid&gt;&lt;br /&gt;    &lt;artifactid&gt;maven-clover2-plugin&lt;/artifactid&gt;&lt;br /&gt;    &lt;version&gt;${clover.version}&lt;/version&gt;&lt;br /&gt;    &lt;configuration&gt;&lt;br /&gt;        &lt;licenselocation&gt;${clover.licenseLocation}&lt;/licenselocation&gt;&lt;br /&gt;        &lt;cloverdatabase&gt;${clover.dbPath}/db/clover.db&lt;/cloverdatabase&gt;&lt;br /&gt;        &lt;historydir&gt;${clover.dbPath}/history&lt;/historydir&gt;&lt;br /&gt;        &lt;includetestsourceroots&gt;false&lt;/includetestsourceroots&gt;&lt;br /&gt;        &lt;excludes&gt;&lt;br /&gt;            &lt;exclude&gt;com\example\**\*&lt;/exclude&gt;&lt;br /&gt;        &lt;/excludes&gt;&lt;br /&gt;   &lt;/configuration&gt;&lt;br /&gt;&lt;/plugin&gt;&lt;br /&gt;&lt;/plugins&gt;&lt;/build&gt;&lt;br /&gt;...&lt;br /&gt;&lt;reporting&gt;&lt;plugins&gt;&lt;br /&gt;&lt;plugin&gt;&lt;br /&gt;    &lt;groupid&gt;com.atlassian.maven.plugins&lt;/groupid&gt;&lt;br /&gt;        &lt;artifactid&gt;maven-clover2-plugin&lt;/artifactid&gt;&lt;br /&gt;        &lt;configuration&gt;&lt;br /&gt;            &lt;licenselocation&gt;${clover.licenseLocation}&lt;/licenselocation&gt;&lt;br /&gt;            &lt;cloverdatabase&gt;${clover.databasePath}/db/clover.db&lt;/cloverdatabase&gt;&lt;br /&gt;            &lt;historydir&gt;${clover.databasePath}/history&lt;/historydir&gt;&lt;br /&gt;            &lt;outputdirectory&gt;${clover.reportPath}/clover-report&lt;/outputdirectory&gt;&lt;br /&gt;        &lt;excludes&gt;&lt;br /&gt;            &lt;exclude&gt;com\example\**\*&lt;/exclude&gt;&lt;br /&gt;        &lt;/excludes&gt;&lt;br /&gt;   &lt;/configuration&gt;&lt;br /&gt;&lt;/plugin&gt;&lt;br /&gt;&lt;/plugins&gt;&lt;/reporting&gt;&lt;/pre&gt;&lt;br /&gt;The most importing thing to note here is the database path. This is a shared location we create that is available on each Bamboo agent. The user that the agents are running with need to have access to that location, so don't forget to set the correct access rights. This location is outside of the normal Bamboo working directory because we're going to manage it ourselves. It's that working location that we are going to replicate on our test servers as well. &lt;br /&gt;&lt;br /&gt;To create the first job of our master plan, we started out with our normal maven clover build. The build, with our adaptations, already produces the database, the instrumented server and the metrics of the unit test. Now we only need to add scripts to manage our central location. A cool feature of Bamboo is it's task infrastructure. A job can comprise out of different tasks, as jobs can run in parallel, tasks will run sequential within a job. A lot of task types are provided or you write your own. One of the tasks that's used here is the inline script task. Very useful for prototyping your CI build and you still can decide to later put the script in your source repository (Bamboo is able to do multiple source checkouts within one job) as the checkout is modeled as a task. Our pre-clover maven build prepares our shared location, basically cleaning up leftovers and recreating the structure we need.&lt;br /&gt;&lt;pre class="brush:bash"&gt;rm -rf /opt/work/clover/example/db&lt;br /&gt;mkdir -p /opt/work/clover/example/db&lt;br /&gt;rm -f clover-db&lt;br /&gt;ln  -s /opt/work/clover/example/db clover-db&lt;/pre&gt;&lt;br /&gt;Next task in our "Build Instrumented Server" job is running maven. Always start cleaning everything, followed by clover2:setup which will build the database and modify the source code. Then do a package, that will build the server, run the unit tests and output the metrics. Finally the clover2:clover will generate the report of the unit test part.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;clean clover2:setup package clover2:clover -Dclover.reportPath=${bamboo.build.working.directory}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The nice side effect off having the single clover db is that all the metrics are saved at that location as well. That makes it easy to create the post execute script. The script is another inline Bamboo task that removes the database and tar's the data in a single file.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:bash"&gt;# Remove old data&lt;br /&gt;rm -f clover-instr.tar.gz&lt;br /&gt;rm -rf clover-instr&lt;br /&gt;# Copy from symbolic link, and delete clover.db&lt;br /&gt;cp -RL clover-db clover-instr&lt;br /&gt;rm -f clover-instr/clover.db&lt;br /&gt;# Archive and compress&lt;br /&gt;tar cvzf clover-instr.tar.gz clover-instr/&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Finally we publish the clover database, instrumented ears and the metrics as a shared artifact that will be used in a later stage.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-large;"&gt;Running the integration tests&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-RXnFPVqR6R4/Txvx5aK7rJI/AAAAAAAAAcA/jE9d8EHchj4/s1600/clover_tasks.PNG" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="197" src="http://3.bp.blogspot.com/-RXnFPVqR6R4/Txvx5aK7rJI/AAAAAAAAAcA/jE9d8EHchj4/s320/clover_tasks.PNG" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Stage two in our plan is running the different test suites. The different stages are jobs grouped together in a single stage. That makes it possible to run the tests concurrently on different agents. Building the job for the integration test can get a bit tricky. Not only are their 2 machines that come into play, now we need the use the instrumented server and the clover database as well.&lt;br /&gt;&lt;br /&gt;As the clover.db and the instrumented server are published in the previous stage they are available to all the next stages. We only need to specify the artifacts we are interested in the location where we like to have them. When we got our artifacts we can use them in our task. Here is the list of tasks we have in one of our jobs:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Checkout the server. Needed for generating the scoped report after the run.&lt;/li&gt;&lt;li&gt;Checkout the test suite, in a sub-directory so it doesn't conflict with the server.&lt;/li&gt;&lt;li&gt;Inline script to cleanup old data, create a mirror of our working directory on the remote server, install the instrumented server and the clover.db on the remote server.&lt;/li&gt;&lt;li&gt;Maven build that runs the integration run.&lt;/li&gt;&lt;li&gt;Inline script to get the metrics back from our remote server to our agents.&lt;/li&gt;&lt;li&gt;Maven build that uses the metric and our server source code checkout to generate a report (scope to these tests).&lt;/li&gt;&lt;li&gt;Script to remove the clover.db from our working directory and pack the metrics data in a tar (just like in our server build).&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Quite a number of tasks. Lets show one of them in detail. The inline script we're showing here is the script that prepares everything and sends everything to the remote server.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:bash"&gt;# Remove all old Clover data&lt;br /&gt;rm -rf /opt/work/clover/example/db&lt;br /&gt;mkdir -p /opt/work/clover/example/db&lt;br /&gt;&lt;br /&gt;# Recreate symbolic links&lt;br /&gt;rm -f clover-db&lt;br /&gt;ln  -s /opt/work/clover/example/db clover-db&lt;br /&gt;&lt;br /&gt;# Unpack clover database&lt;br /&gt;cp ext/clover-db/* clover-db&lt;br /&gt;&lt;br /&gt;# Prepare and send instrumented server&lt;br /&gt;rm -rf deploy&lt;br /&gt;mkdir -p deploy&lt;br /&gt;find ./ext/server -iname 'example*.ear' -exec cp \{\} deploy \;&lt;br /&gt;tar c deploy  | ssh -i /opt/secure/qa-fat.private root@qa-fat tar x -C /opt/example/server/default/&lt;br /&gt;&lt;br /&gt;# copy clover db&lt;br /&gt;ssh -i /opt/secure/qa-fat.private root@qa-fat rm -rf /opt/work/clover/example/db&lt;br /&gt;ssh -i /opt/secure/qa-fat.private root@qa-fat mkdir -p /opt/work/clover/example/db&lt;br /&gt;scp -i /opt/secure/qa-fat.private /opt/work/clover/example/db/clover.db root@qa-fat:/opt/work/clover/example/db/&lt;/pre&gt;&lt;br /&gt;The rest of the scripts are trivial, just collect the data from the remote server and continue. Don't forget to setup your remote server by adding the clover.jar to the libraries of your app server and add a VM parameter to tell clover where to find the database &lt;code&gt;-Dclover.initstring.basedir = /opt/work/clover/example/db&lt;/code&gt;. The other test suites have similar setups, so we're thinking about adding the scripts to a small git repo, so they can be reused in different jobs, with an extra checkout.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-large;"&gt;Creating the&amp;nbsp;aggregated&amp;nbsp;report&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-8taLKDY0yaw/TxvyAFERmFI/AAAAAAAAAcI/74dbXxArq2k/s1600/clover_artifacts.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="198" src="http://4.bp.blogspot.com/-8taLKDY0yaw/TxvyAFERmFI/AAAAAAAAAcI/74dbXxArq2k/s320/clover_artifacts.PNG" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;The final stage is not that difficult. In this stage we want to collect all the information collect throughout the previous stages. Again we use the artifacts shared by the previous stages, put them in the desired place and start a script to unpack them alongside the clover database. Because the metric data file names are always unique they will not clash with the files produced on the other machines. So we only need to place them in one place and start the maven clover report build again but now on all the metrics (unit-, api- and ui tests). Here's a example of what the unpack script could look like:&lt;br /&gt;&lt;pre class="brush:bash"&gt;# Remove all old Clover data&lt;br /&gt;rm -rf /opt/work/clover/example/db&lt;br /&gt;mkdir -p /opt/work/clover/example/db&lt;br /&gt;&lt;br /&gt;# Recreate symbolic links&lt;br /&gt;rm -f clover-db&lt;br /&gt;rm -f clover-instr&lt;br /&gt;ln  -s /opt/work/clover/example/db clover-db&lt;br /&gt;ln  -s /opt/work/clover/example/db clover-instr&lt;br /&gt;&lt;br /&gt;# Unpack instrumentation data&lt;br /&gt;cp ext/clover-db/* clover-db&lt;br /&gt;tar xvzf ext/instr/unit/clover-instr.tar.gz&lt;br /&gt;tar xvzf ext/instr/api/clover-instr.tar.gz&lt;br /&gt;tar xvzf ext/instr/ui/clover-instr.tar.gz&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-size: x-large;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Creating this plan took a while, but I think the information you can collect from the code coverage is worth the cost. With this information we can adapt our test plan to write extra tests for the code that's not covered and we thought we did. A special thank to Francois and Bert for dedicating some of their time to make this plan possible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6836009845114221640-964420526042974177?l=alex.vanboxel.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alex.vanboxel.be/feeds/964420526042974177/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://alex.vanboxel.be/2012/01/aggregated-test-coverage-using-maven.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default/964420526042974177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default/964420526042974177'/><link rel='alternate' type='text/html' href='http://alex.vanboxel.be/2012/01/aggregated-test-coverage-using-maven.html' title='Aggregated code coverage using Maven, Clover and Bamboo'/><author><name>Alex Van Boxel</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-jqaZc9ZsFp0/AAAAAAAAAAI/AAAAAAAAAO0/UF5osgU9z90/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-Ze674f9S5rQ/TxvvdvjCUlI/AAAAAAAAAbw/VZ0u3DSPC3I/s72-c/clover_plan.PNG' height='72' width='72'/><thr:total>0</thr:total><georss:featurename>Vilvoorde, Belgium</georss:featurename><georss:point>50.927262 4.424078</georss:point><georss:box>50.8872285 4.345114 50.9672955 4.503042</georss:box></entry><entry><id>tag:blogger.com,1999:blog-6836009845114221640.post-4205014956525346201</id><published>2010-11-09T10:17:00.000-08:00</published><updated>2012-01-17T13:45:17.285-08:00</updated><title type='text'>Evolve your REST representation with a MessageBodyWriter</title><content type='html'>THIS PAGE IS UNDER CONSTRUCTION... this is here to get the right scripts and layout in place. Thanks....&lt;p&gt;A less known component of the JAX-RS spec is the MessageBodyWriter. It’s a pity because it’s one of the most important building blocks if you plan on evolving your representation of your REST resources. A MessageBodyWriter will also keep the representation logic out of your annotated web-resource classes.&amp;nbsp;&lt;/p&gt;&lt;strong&gt;Build in support&lt;/strong&gt;&lt;br /&gt;&lt;p&gt;A JAX-RS implementation already supports a number of conversions from types to different representation. The classes responsible for the conversions are all implementent as MessageBodyWriters and Readers.&amp;nbsp;&lt;span style="background-color: transparent;"&gt;Here are a few:&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;byte[]&lt;/li&gt;&lt;li&gt;java.lang.String&lt;/li&gt;&lt;li&gt;javax.xml.transform.Source&lt;/li&gt;&lt;li&gt;javax.xml.bind.JAXBElement&lt;/li&gt;&lt;li&gt;MultivaluedMap&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;It’s already quite a rich set of types that are supported by default. On simple REST interfaces this will probably suffice. The support for JAXB is especially popular for creating XML representations. An DTO/JAXB implementation could look like this:&lt;br /&gt;&lt;/p&gt;&lt;pre class="brush:java"&gt;@GET&lt;br /&gt;@Path("companies/{companyId}")&lt;br /&gt;@Produces("application/xml")&lt;br /&gt;public XmlCompany getCompany(@PathParam("companyId") id ) {&lt;br /&gt;  return XmlAdapter.toXml(companies.getId(companyId));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@POST&lt;br /&gt;@Path("companies")&lt;br /&gt;@Consumes("application/xml")&lt;br /&gt;@Produces("application/xml")&lt;br /&gt;public Response createCompany(@Context UriInfo uriInfo,XmlCompany jaxbCompany) {&lt;br /&gt;  Company company = XMLAdapter.fromXML(jaxbCompany);&lt;br /&gt;  company = service.create(company);&lt;br /&gt;  ResponseBuilder responseBuilder = Response.created(uriInfo.getAbsolutePath().resolve(company.getID));&lt;br /&gt;  return responseBuilder.entity(xmlProfile).build();&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;But if your interface evolves, so will your representation. At some point in time you will even need to support more then one representation, because you have customers still rely on the old.&lt;br /&gt;&lt;/p&gt;&lt;strong&gt;The REST style&lt;/strong&gt;&lt;p&gt;&amp;nbsp;Before looking at the implementation we should have a mechanism to make a distinction between the different representations. This can be done by the putting our version number in the MIME type. MIME types allow for a vendor namespace where we can create all our private representations. This is how the MIME type could look like:&lt;/p&gt;&lt;code&gt;application/vnd.vanboxel.labs.app.v1.company+xml&lt;/code&gt;&lt;p&gt;You are free to define the structure between the &lt;code&gt;application/vnd.&lt;/code&gt; and the &lt;code&gt;+xml&lt;/code&gt;. Here I’ve created my private namespace &lt;code&gt;vanboxel.labs&lt;/code&gt; for my &lt;code&gt;app&lt;/code&gt; application,  version &lt;code&gt;v1&lt;/code&gt; and an entity of type &lt;code&gt;company&lt;/code&gt;. You can be even more fine grained and version your individual entities as well, but this adds to the complexity. And finally don’t forget to finish your MIME type with the real format, this could be &lt;code&gt;+xml&lt;/code&gt;, &lt;code&gt;+json&lt;/code&gt; or something else.&lt;/p&gt;&lt;p&gt;A reflex is now to add new methods to handle the new conversions, and add the MIME type to the &lt;strong&gt;@Consumers&lt;/strong&gt; and &lt;strong&gt;@Produces&lt;/strong&gt; annotation, in your resource class. But remember that by doing this you will be multiplying the methods by the number of representations. This will just add clutter to the classes. You should keep the representation logic out of the resource classes. Let them handle the path’s, caching, e-tag’s, etc…&lt;/p&gt;&lt;strong&gt;MessageBodyWriter/Reader&lt;/strong&gt;&lt;p&gt;What you could do is write a MessageBodyWriter that handles the conversion to different representations (be it format or version). Writing one is easy, you need to implement 3 methods of the interface with the same name. The &lt;code&gt;writeTo&lt;/code&gt; method is obvious, this method will be called with the object returned by the resource class, and here you do the actual conversion. The &lt;code&gt;getSize&lt;/code&gt; is called before the &lt;code&gt;writeTo&lt;/code&gt; method, to determine the size upfront. If you don't know the size before the conversion, just return -1. &lt;/p&gt;&lt;p&gt;Now the most interesting method is &lt;code&gt;isWriteable&lt;/code&gt;. Here you write the code, to detect if this is the correct &amp;lt;em&amp;gt;writer&amp;lt;/em&amp;gt; for the object. The JAX-RS implementation will iterate over each known &lt;code&gt;MessageBodyWriter&lt;/code&gt; and call the &lt;code&gt;isWritable&lt;/code&gt; method to determine if the class is capable of converting the object.&lt;/p&gt;&lt;pre class="brush:java"&gt;@Provider&lt;br /&gt;public class XMLMessageBodyWriter implements MessageBodyWriter&amp;lt;Object&amp;gt; {&lt;br /&gt;    &lt;br /&gt; @Override&lt;br /&gt; public long getSize(Object arg0, Class&amp;lt;?&amp;gt; arg1, Type arg2, Annotation[] arg3, MediaType arg4) {&lt;br /&gt;   return -1;&lt;br /&gt; }&lt;br /&gt;    &lt;br /&gt; @Override&lt;br /&gt; public boolean isWriteable(Class&amp;lt;?&amp;gt; clazz, Type type, &lt;br /&gt;       Annotation[] annotations, MediaType mediaType) {&lt;br /&gt;   if ("be.vanboxel.labs.app.dto".equals(clazz.getPackage().getName())&lt;br /&gt;       &amp;amp;&amp;amp; mediaType.getType().equals("application")&lt;br /&gt;       &amp;amp;&amp;amp; mediaType.getSubtype()&lt;br /&gt;           .matches("vnd\\.vanboxel\\.labs\\.app\\.v1\\..*\\+xml")&lt;br /&gt;   ) {&lt;br /&gt;     return true;&lt;br /&gt;   }&lt;br /&gt;   return false;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public void writeTo(Object object, Class&amp;lt;?&amp;gt; clazz, Type type, &lt;br /&gt;         Annotation[] annotation, MediaType mediaType, &lt;br /&gt;         MultivaluedMap&amp;lt;String, Object&amp;gt; map, &lt;br /&gt;         OutputStream out) throws IOException, WebApplicationException {&lt;br /&gt;   if (object instanceof Company) {&lt;br /&gt;     marshal(out, XMLAdapter.toXML((Company) object));&lt;br /&gt;   }&lt;br /&gt;   else if (object instanceof Customer) {&lt;br /&gt;     marshal(out, XMLAdapter.toXML((Customer) object));&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;In this example we only use 2 parameters of the supplied information to decide if the class is capable of the conversion. The first is the class of the object. We are only interested in converting our own DTO classes. They are all packaged together so the package name is verified. The other parameter we check is the MIME type. Here we check if it's the private namespace witch includes our version number.&lt;/p&gt;&lt;p&gt;Now the only thing left is to remove the conversion code from the methods in the resource class and add the @Produces annotation, with the MIME type.&lt;/p&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;@GET&lt;br /&gt;@Path("companies/{companyId}")&lt;br /&gt;@Produces("application/vnd.vanboxel.labs.app.v1.company+xml")&lt;br /&gt;public XmlCompany getCompany(@PathParam("companyId") id ) {&lt;br /&gt;  return companies.getId(companyId);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Say the REST interface evolved a newer and ritcher representation that is not compatible with the current and adds a JSON representation on top. We only need to write the two new MessageBodyWriters (and MessageBodyReaders) for the new XML representation and JSON. Once the classes are written, you only need to add the &lt;code&gt;@Produces&lt;/code&gt; and &lt;code&gt;@Consumer&lt;/code&gt; MIME types to the resource classes.&lt;/p&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;@GET&lt;br /&gt;@Path("companies/{companyId}")&lt;br /&gt;@Produces("application/vnd.vanboxel.labs.app.v1.company+xml")&lt;br /&gt;@Produces("application/vnd.vanboxel.labs.app.v2.company+xml")&lt;br /&gt;@Produces("application/vnd.vanboxel.labs.app.v2.company+json")&lt;br /&gt;public XmlCompany getCompany(@PathParam("companyId") id ) {&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;If you already have a written a bunch of adapters to convert your dto's to your XML representation you could consider making them MessageBodyWriter/Readers by implementing the interfaces there.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6836009845114221640-4205014956525346201?l=alex.vanboxel.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alex.vanboxel.be/feeds/4205014956525346201/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://alex.vanboxel.be/2010/11/less-known-component-of-jax-rs-spec-is.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default/4205014956525346201'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default/4205014956525346201'/><link rel='alternate' type='text/html' href='http://alex.vanboxel.be/2010/11/less-known-component-of-jax-rs-spec-is.html' title='Evolve your REST representation with a MessageBodyWriter'/><author><name>Alex Van Boxel</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-jqaZc9ZsFp0/AAAAAAAAAAI/AAAAAAAAAO0/UF5osgU9z90/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6836009845114221640.post-4661072993645266793</id><published>2010-04-02T23:42:00.000-07:00</published><updated>2012-01-17T14:43:40.560-08:00</updated><title type='text'>JAX-RS: Life-cycle, part 1</title><content type='html'>I know, we all do it. We start to play with new technology, start with some example, take shortcuts, read blogs, google when a problem pops up... But what we almost never do it read the spec. Well working in a company where we make products that comply to specs, I learned a few things about those specs. For starters, they are almost always boring, but they are full of useful information.So to prepare for a presentation about JAX-RS, I picked up the spec and read it. And as expected I found lots of interesting stuff out, and now I understood why, while preparing the examples, I had some problem. So I decided to use some of the concept found in the spec and used them in my presentation.Well now my presentation is over, I will try and share some of it here on my blog. So, to start of &lt;em&gt;JAX-RS&lt;/em&gt; is the latest addition to the J2EE6 spec to build RESTful Web Services, but if your reading this you probably already know that, and this article is not an introduction but some look at the internals of the specification.Let's have a look at the life-cycle of a Resource in JAX-RS. Understanding the life-cycle will help you troubleshoot problems in your application. So what is the life-cycle of a simple resource. An example:&lt;pre class="brush:java"&gt;&lt;br /&gt;@Path("lifecycle/constructor/{segment}")&lt;br /&gt;@Produces("text/plain")&lt;br /&gt;public class LifeCycleResource {&lt;br /&gt;  private String _segment;&lt;br /&gt;  private int _qPage;&lt;br /&gt;  private int _qEntries;&lt;br /&gt;&lt;br /&gt;  @HeaderParam("Accept") private String _accept;&lt;br /&gt;&lt;br /&gt;  public LifeCycleResource(&lt;br /&gt;      @PathParam("segment") String seqment,&lt;br /&gt;      @QueryParam("page") int page,&lt;br /&gt;      @QueryParam("entries") @DefaultValue("10") int entries) &lt;br /&gt;  {&lt;br /&gt;    _segment = seqment;&lt;br /&gt;    _qPage = page;&lt;br /&gt;    _qEntries = entries;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @GET&lt;br /&gt;  public String get() {&lt;br /&gt;    String val = " segment: " + _segment + "\n"&lt;br /&gt;      + " start: " + _qPage + "\n"&lt;br /&gt;      + " entries: " + _qEntries + "\n\n"&lt;br /&gt;      + " Accept:" + _accept +"\n";&lt;br /&gt;    return val;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;OK, we have our example. Now let's look at what the spec and it's description of the life-cycle.&lt;ol&gt; &lt;li&gt;Path's are matched with Resource classes.&lt;/li&gt; &lt;li&gt;Constructor is called.&lt;/li&gt; &lt;li&gt;Dependencies are injected.&lt;/li&gt; &lt;li&gt;Appropriate method is called.&lt;/li&gt; &lt;li&gt;Resource is garbage collected.&lt;/li&gt;&lt;/ol&gt;So when accessing the resource though the URI &lt;code&gt;web-app/lifecycle/constructor/something?start=2&lt;/code&gt;. We know the following steps are happing:&lt;ul&gt; &lt;li&gt;All classes with a &lt;em&gt;@Path&lt;/em&gt; annotation are searched for the matching path, so the example Resource will be found because &lt;code&gt;life-cycle/constructor/something&lt;/code&gt; has the closes match to the &lt;code&gt;lifecycle/constructor/{segment}&lt;/code&gt;&lt;/li&gt; &lt;li&gt;Next the constructors of the class are scanned for the closest match, we have one, so that was easy.&lt;/li&gt; &lt;li&gt;Now the class is instantiated with that constructor, looking for the annotations on the parameters and filling in the appropriate values. In this case a part of the path to &lt;em&gt;segment&lt;/em&gt;, the query parameter to &lt;em&gt;start &lt;/em&gt;, and because entries has a default value it will get the value 10.&lt;/li&gt; &lt;li&gt;Now the resource is constructed, and the injection can begin. It scans the field for known annotations and initializes those fields (&lt;em&gt;note: remember this point&lt;/em&gt;).&lt;/li&gt; &lt;li&gt;And finally the methods are scanned for annotations (another path annotation or the http verbs). Here it's found the get method, because it is annotated with &lt;em&gt;@GET&lt;/em&gt; and that one is called, it will return a value and that value will be send to the client.&lt;/li&gt; &lt;li&gt;The resource is de-referenced and is prepared to be garbage collected.&lt;/li&gt;&lt;/ul&gt;We've gone over the sequence of one call from the client. What can we tell about of what is safe? Well it's safe to use the fields, without worrying about concurrent calls, except static fields. This shows that each resource doesn't match a servlet, but we have one servlet that manages the life-cycle of these resources.But there is a gotcha as well, and that is the injection. Because the injection happens after the object is constructed means you can't injection fields in the &lt;em&gt;constructor&lt;/em&gt;. In our example we can't use the &lt;code&gt;_accept&lt;/code&gt; field because at that time it still is null. So don't wonder why you get &lt;strong&gt;NPE&lt;/strong&gt;'s when you use them there. You can use them afterwards when the framework calls the method.That's enough for now, in the next article I'll look at sub-resources.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6836009845114221640-4661072993645266793?l=alex.vanboxel.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alex.vanboxel.be/feeds/4661072993645266793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://alex.vanboxel.be/2010/04/jax-rs-life-cycle-part-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default/4661072993645266793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default/4661072993645266793'/><link rel='alternate' type='text/html' href='http://alex.vanboxel.be/2010/04/jax-rs-life-cycle-part-1.html' title='JAX-RS: Life-cycle, part 1'/><author><name>Alex Van Boxel</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-jqaZc9ZsFp0/AAAAAAAAAAI/AAAAAAAAAO0/UF5osgU9z90/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6836009845114221640.post-8846655923038333750</id><published>2009-12-02T23:24:00.000-08:00</published><updated>2012-01-17T14:38:23.955-08:00</updated><title type='text'>Writing a basic Service Provider loader</title><content type='html'>Last week I was at the Devoxx conference, and I was following the talk about the new features of J2EE6. One of the new features is that a framework can now implement a few Service Provider Interfaces and the application server will register it automatically. No hassle with adding the configuration information to the web.xml, just add the frameworks jar to your webapp.Well it inspired me to use the same kind of mechanism in my benchmark framework. Why should I have to specify a class name in my scripts when I could let the framework discover all the providers, by implementing the standard &lt;a href="http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider"&gt;Service Provider&lt;/a&gt; mechanism.Since most people don't come in direct contact with &lt;a href="http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider"&gt;Service Provider&lt;/a&gt; interfaces, it's not a well understood concept. That's a pity, because it's an easy way to implement extensible applications.My framework needs to run on J2SE1.5 onwards, so I can't use the ServiceLoader utility that is available in 1.6. So I decided to write me a basic service provider loader from scratch. And now it's time to dissect the loader.But before looking at the loader, let's take a peek what it takes to create an actual Service Provider. Not much actually, you need an interface that describes the provider, java.sql.Driver is a famous example. And an actual implementation of that interface that is the actual provider. If you installed JavaDB with your JDK you can take a look at "derbyclient.jar", what is an actual service provider, in this example the derby client jdbc driver. The class that implement the Driver interface is &lt;code&gt;org.apache.derby.jdbc.ClientDriver&lt;/code&gt;.Now the only thing left to do in making this a real service provider is adding one little file. The filename is actually the name of the interface, and the content is the name of the class implementing that interface.The location is also important, in &lt;code&gt;META-INF/services/&lt;/code&gt;. In our example we have a file named &lt;code&gt;java.sql.Driver&lt;/code&gt; with the content &lt;code&gt;org.apache.derby.jdbc.ClientDriver&lt;/code&gt;.Now having everything in place we can start with implementing our loader. It will be a basic loader that starts it search and load the &lt;code&gt;Class&lt;/code&gt; for the requested provider at construction time. Let's make it Iterable, so we can use it in the cool for loops introduced since SE5.&lt;br /&gt;&lt;pre class="brush:java"&gt;public class SPILoader implements Iterable&lt;class&gt; {&lt;br /&gt;&lt;br /&gt;  private LinkedHashMap&lt;string, class=""&gt; _spis = new LinkedHashMap&lt;string, class=""&gt;();&lt;br /&gt;&lt;/string,&gt;&lt;/string,&gt;&lt;/class&gt;&lt;/pre&gt;The following code does the actual heavy lifting. Let's go over it step by step. I prefer calling the constructor with the actual interface, in our example &lt;code&gt;java.sql.Driver.class&lt;/code&gt;.Once we have it canonical name we can start searching for every instance of the file that has the file META-INF/services/java.sql.Driver in it's classpath ( see line 19 ). It will search for it in every jar or path known to the current classloader.The only thing left to do is enumerate over every file a create a Class of that instance ( line 33 ), and store it in our HashMap.&lt;br /&gt;&lt;pre class="brush:java"&gt;  /**&lt;br /&gt;   * Try to load all known provider classes.&lt;br /&gt;   *&lt;br /&gt;   * @param spiClass&lt;br /&gt;   * @throws Exception&lt;br /&gt;   */&lt;br /&gt;  public SPILoader(Class spiClass) throws Exception {&lt;br /&gt;    this(spiClass.getCanonicalName());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * Try to load all known provider classes.&lt;br /&gt;   *&lt;br /&gt;   * @param spiClass&lt;br /&gt;   * @throws Exception&lt;br /&gt;   */&lt;br /&gt;  public SPILoader(String spiClass) throws Exception {&lt;br /&gt;    ClassLoader classLoader = getClass().getClassLoader();&lt;br /&gt;    Enumeration resourceEnumeration = classLoader.getResources("META-INF/services/" + spiClass);&lt;br /&gt;&lt;br /&gt;    while (resourceEnumeration.hasMoreElements()) {&lt;br /&gt;      URL url = resourceEnumeration.nextElement();&lt;br /&gt;      Reader reader = new InputStreamReader(url.openStream());&lt;br /&gt;      char[] line = new char[1024];&lt;br /&gt;      int length = reader.read(line);&lt;br /&gt;      if (length &amp;lt;= 0) {&lt;br /&gt;        throw new RuntimeException("Error loading SPI: Error loading service file");&lt;br /&gt;      }&lt;br /&gt;      StringBuffer buffer = new StringBuffer();&lt;br /&gt;      for (int ix = 0; (Character.isJavaIdentifierPart(line[ix]) || line[ix] == '.') &amp;amp;&amp;amp; ix &amp;lt; length; ix++) {&lt;br /&gt;        buffer.append(line[ix]);&lt;br /&gt;      }&lt;br /&gt;      Class spi = Class.forName(buffer.toString());&lt;br /&gt;      if (spi == null) {&lt;br /&gt;        throw new RuntimeException("Error loading SPI: " + buffer.toString() + " has no class defined");&lt;br /&gt;      }&lt;br /&gt;      _spis.put(buffer.toString(), spi);&lt;br /&gt;      reader.close();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;Next is write a methods to get our discovered providers. I needed one that returned only one, so I opted to write on that accepted a list of preferred providers if they are available. I have my favorite providers you know :-)&lt;br /&gt;&lt;pre class="brush:java"&gt;  /**&lt;br /&gt;   * Get a SPI provider class, optionally providing an ordered list of preferred providers.&lt;br /&gt;   * @param prefered&lt;br /&gt;   * @return&lt;br /&gt;   */&lt;br /&gt;  public Class get(String... preferred) {&lt;br /&gt;    if (preferred != null) {&lt;br /&gt;      for (String p : preferred) {&lt;br /&gt;        Class clazz = _spis.get(p);&lt;br /&gt;        if (clazz != null) {&lt;br /&gt;          return clazz;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    Iterator&lt;class&gt; iterator = _spis.values().iterator();&lt;br /&gt;    if (iterator.hasNext()) {&lt;br /&gt;      return iterator.next();&lt;br /&gt;    }&lt;br /&gt;    return null;&lt;br /&gt;  }&lt;br /&gt;&lt;/class&gt;&lt;/pre&gt;To top the loader of, lets implement the iterator method so we can loop over all discovered classes.&lt;br /&gt;&lt;pre class="brush:java"&gt;  /**&lt;br /&gt;   * Iterate over the known provider classes.&lt;br /&gt;   */&lt;br /&gt;  public Iterator&lt;class&gt; iterator() {&lt;br /&gt;    return _spis.values().iterator();&lt;br /&gt;  }&lt;br /&gt;&lt;/class&gt;&lt;/pre&gt;Now it's time to use our loader, with some examples. Let's search for all JDBC drivers that are available in the current classpath.&lt;br /&gt;&lt;pre class="brush:java"&gt;    SPILoader jdbcDrivers = new SPILoader(java.sql.Driver.class);&lt;br /&gt;    for(Class driverClass : jdbcDrivers) {&lt;br /&gt;      System.out.println(driverClass.toString());&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;If you added the derbyclient.jar mentioned in the beginning of this post to the classpath the code should output something like this&lt;br /&gt;&lt;blockquote&gt;class sun.jdbc.odbc.JdbcOdbcDriverclass org.apache.derby.jdbc.ClientDriver&lt;/blockquote&gt;And if you're only interested in one provider (example: a SAX parser, you only need one) use the get method.&lt;br /&gt;&lt;pre class="brush:java"&gt;    SPILoader loader = new SPILoader(labs.SPIClass.class);&lt;br /&gt;    Class clazz = loader.get("labs.SPIImpl1","labs.SPIImpl1");&lt;br /&gt;    if(clazz != null) {&lt;br /&gt;      // create instance of your favorite SPI implementation&lt;br /&gt;      labs.SPIClass inst = (labs.SPIClass)clazz.newInstance();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;If you are lucky enough to have J2SE 6 as your minimal platform you are lucky enough to have java.util.ServiceLoader available to you, I just don't understand that it took till SE 6, to have a utility class available in the JDK. Anyway I hope this walk trough gives you some inside how auto discovery works for service providers. Have fun writing your own.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6836009845114221640-8846655923038333750?l=alex.vanboxel.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alex.vanboxel.be/feeds/8846655923038333750/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://alex.vanboxel.be/2009/12/writing-basic-service-provider-loader.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default/8846655923038333750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default/8846655923038333750'/><link rel='alternate' type='text/html' href='http://alex.vanboxel.be/2009/12/writing-basic-service-provider-loader.html' title='Writing a basic Service Provider loader'/><author><name>Alex Van Boxel</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-jqaZc9ZsFp0/AAAAAAAAAAI/AAAAAAAAAO0/UF5osgU9z90/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6836009845114221640.post-2354069122704194273</id><published>2009-11-28T22:51:00.000-08:00</published><updated>2012-01-17T13:53:58.600-08:00</updated><title type='text'>Using XMPP for machine-to-machine communication</title><content type='html'>&lt;br /&gt;Recently I replaced the protocol of our distributed benchmark framework with XMPP. When I first started on the communication protocol I opted to use JXTA , a peer-to-peer protocol. It seemed like a good idea, a peer-to-peer protocol didn’t need a central server to handle all the communication between the slaves. It was ok at first, it got us started really quick. But the problem pilled up and the problem where always related with the communication layer. One of the problems was that too much magic was going on under the covers, what makes it hard to trouble shoot. Another thing it lacked was a nice designed api.&lt;br /&gt;&lt;br /&gt;So one day I had some problems popping up again and I got tired of it, after a day wasting time on not finding what the problem was with JXTA I gave myself a challenge. Replace the protocol on the the rebelling component before I go to sleep. Well I started at 20:00 and by 1:00 I had everything replaced with XMPP. Everything went smooth, thanks to the help of the client library Smack. The API was simple to use, create an object that represented a message and send it, that’s it.&lt;br /&gt;&lt;br /&gt;This is about two months ago, by now everything is replaced by XMPP and I couldn’t be happier. Since then no more problems anymore, and switching opens the way to a lot of possibilities. More on that later.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6836009845114221640-2354069122704194273?l=alex.vanboxel.be' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alex.vanboxel.be/feeds/2354069122704194273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://alex.vanboxel.be/2009/11/using-xmpp-for-machine-to-machine.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default/2354069122704194273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6836009845114221640/posts/default/2354069122704194273'/><link rel='alternate' type='text/html' href='http://alex.vanboxel.be/2009/11/using-xmpp-for-machine-to-machine.html' title='Using XMPP for machine-to-machine communication'/><author><name>Alex Van Boxel</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-jqaZc9ZsFp0/AAAAAAAAAAI/AAAAAAAAAO0/UF5osgU9z90/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry></feed>
