In my previous blog, Distributed Compensation with REST and jBPM, I talked about removing complexity from both the distributed protocol and the server by forcing (or enabling, depending on your perspective) the client to handle things like business activity and compensating transactions. I asked the question, where does this leave WS-BA or more importantly, JBoss’s Business Activity Framework? It could easily still be used in a one process, web application. Many web applications are long running transactions that require compensation. But, I still think JBoss’s Business Activity Framework could be useful to help orchestrate distributed services, even restful ones, if we make compensation metadata available to the client so a local-only business activity coordinator could do its work locally. This would give us the decoupling benefit of BA, but would also allow us to keep our remote services stateless (and restful). The idea would be to move the BA annotations from the bean class, to the business interface:
public interface TravelAgent { @BACompensatedBy(value="cancel") @BAResult("reservationNumber") public int book(int room); public void cancel(@BAParam("reservationNumber") int reserverationNumber); }
A client-side smart proxy could hide the interaction with the local activity coordinator. Another idea would be for the business process engine to introspect service references to see if they are annotated with compensation metadata, and interact with the coordinator themselves. A nice side effect of putting the metadata within the interface is that your service becomes self documenting. The user of this service knows exactly what’s going on by looking at the code.
You could combine this annotation-driven approach with the restful client-side framework I suggested in my comments on JSR-311.
public interface TravelAgent { @BACompensatedBy(value="cancel") @BAResult("$r.getId()") @UriTemplate("/hotel/reservations") @HttpMethod("POST") @Location("/hotel/reservations/$r.id") public Reservation book(@QueryParam("room") int room); @UriTemplate("/hotel/reservations/{reservationNumber}") @HttpMethod("DELETE") public void cancel(@BAParam("reservationNumber") @UriParam("reservationNumber") int reserverationNumber); }
This could make it fairly easy and metadata driven to have restful services interact with business activity.
In Java-to-Java calls, for example a remote or local EJB invocation, you wouldn’t necessarily need an interface-driven approach. The JBoss EJB 3.0 implementation, for instance, allows you to define both client-side and server side interceptors. At deployment time, the EJB container could scan the bean class for BA metadata, create a client interceptor initialized with this meta information, and attach the client interceptor to the proxy that is published to clients.
I think client driven conversations where the client holds all state makes for better written distributed services as they can be written in a more stateless, scalable manner. This doesn’t mean that you’d be able to apply this pattern to all scenarios. That’s why I think it might be interesting to make this approach configurable. The client must drive the compensation, the server must be allowed to participate in resource registration, or even a configuration of the client can decide whether it wants drive the coordination or not are all valid scenarios. Anyways, if you experiment with any of these patterns let me know. It would be cool to hear some real, live user experience instead of just guessing on whether or not this would be a good approach.
The thoughts of an IT professional : Annotations used for deployment
Oct 15, 2007 @ 15:37:56
Oct 15, 2007 @ 16:40:12
@Location("/hotel/reservations/${r.getId()}")
public Reservation book(@QueryParam("room") int room);
Could just be expressed as (minute detail):
@Location("/hotel/reservations/${r.id}")
public Reservation book(@QueryParam("room") int room);
Oct 18, 2007 @ 11:31:57
unrelated…. You made a post a couple of years ago. I was wondering if you could point me to the example code. We use have a commercial content management system application that will not upgrade to java5 until the end of the year and have the below I/O bottleneck.
————————-
I’ve posted this in the past, but I ran into scalability problems with Object streams when writing a custom transport for JBoss. It boils down to some static variables being synchronized within java.io.ObjectStreamClass
/** cache mapping local classes -> descriptors */
private static final SoftCache localDescs = new SoftCache(10);
/** cache mapping field group/local desc pairs -> field reflectors */
private static final SoftCache reflectors = new SoftCache(10);
I walked through some hoops to change the use of SoftCache to a ConcurrentReaderHashMap and throughput started increasing 20-40% depending on how many threads you run through it. It is not an issue if you’re just marshalling primitives, but becomes an issue of course if you’re marshalling objects. I have the code for this, but it is a derivative of JDK 1.4 source which means it is illegal to distribute. Email me if you want it. I’m interested in any legal work-around if you have one.
I’d also be interested in incorporating some of your research into a JBoss transport. Email if you’re interested and I’ll point you to the appropriate example code.
Bill