Friday, December 5, 2008
Here is a pattern that I have implemented a few times now.
I have had requirements to provide a web service, typically SOAP based, that persists something in a database (or whatever). Here's a general flow of events that I'm finding works particularly well:
1. Web browser to web service
My CXF based web service is developed on a contract-first basis i.e. the SOAP interface is defined using Java. The reason I like that (as opposed to a WSDL-first approach), is because WSDL appears overly complex to me. It is much easier, in my most humble opinion, to create the Java interface and have CXF generate the WSDL. I reckon that CXF does a pretty good job of this too.
My web service has the responsibility of marshaling the SOAP XML into a Java object that is representative of my information model. Here's a critical point: you must have an information model to work with aka domain model. If you don't have that nutted out very well then you tend to spin wheels.
The classes of the information model are used as a normalised object for messaging and also for persistence. The web service marshals the XML into model objects, performs various integrity checks and then forwards the model objects on to a JMS queue.
I'm using Spring's JMS template classes to forward on my JMS messages; when it comes to sending messages, lots of code is taken care of using these classes (I've also programmed JMS without them so I can make a direct comparison as to what Spring offers in this respect).
Oh, by the way, I'm using Apache Tomcat to host my CXF based web application.
2. Sending to ActiveMQ
I'm typically sending to queues dedicated to specific functions e.g. "queue to persist the biometric fingerprint of a person". These queues are persistent (a JMS default) so that messages are stored until they can be guaranteed delivery.
I like ActiveMQ. Apart from the price tag being very attractive (free), the Apache products are built to a very high standard. ActiveMQ is fast and reliable.
3. ActiveMQ replies
Once ActiveMQ replies back to my web service guaranteeing delivery, my web service replies to my browser application.
4. Browser processes the reply & Camel service consumes queue
These are concurrent activities. Of course the browser delivered the SOAP request in (1) asynchronously and registered call backs for processing any errors and successful responses. This way the user's browser remains responsive.
The design of my browser application is very much around the store-and-forward paradigm. Depending on how critical it is to feed back the results of an operation, it is generally adequate to inform the user that their requested has been posted. In the case where the request is involved in retrieving, say, customer details, receiving a response is important so I would use a request-reply enterprise integration pattern. However in many scenarios, store and forward is perfectly adequate and really suites a browser style application.
Almost as soon as ActiveMQ makes a message available to the queue (2) my Apache Camel based service is able to consume it.
I like Apache Camel. It really helps me assemble services very loosely given the strong promotion of enterprise integration patterns. You can build content based routers, aggregators, request-reply mechanisms and so forth - Camel provides the framework for you to build your services on. Camel almost forces you to think about the correct separation of concerns given its abstractions.
I typically deploy my Camel based services using jsvc - Apache's daemon toolkit (part of the Apache Commons project). jsvc is a very simple wrapper so that you can make your application run as a service on Unix and Windows based platforms. From a programming perspective, you implement a lifecycle interface (init, destroy, start, stop) and that's about it. I want to move toward using OSGi, and I have played with it. However I have not found a OSGi based container that I'm totally happy with yet (they don't appear to have totally satisfied use-cases associated with administrators - just programmers at this point).
I have many services do many things but a common task is persisting and retrieving data from a relational database. For that I program to JPA and use Hibernate as my JPA implementation. I prefer JPA over Hibernate directly so that I have the ultimate freedom of using other ORM implementations should I need to - I don't think I will be needing to at this point though as Hibernate is great.
I get great performance. The slowest parts tend to be associated with persisting data in an RDBMS (nothing to do with Hibernate etc - normal RDBMS stuff) and network latency. CXF and Camel fly along - you're mostly counting small units of milliseconds with these technologies.
I've not had to do much in the way of scaling yet, but I know I have the correct separation of concerns to cope with most of what the world can throw at these services. In the performance testing that I have done I tend to exceed my customer's expectations. As stated above, it is network latency that appears to be the bottleneck given that the internet often sits between the browser and the web service.
It may appear that I work for Apache or something, but I don't. I do value the Apache Projects though. In summary here's what I'm presently using: