Mark Little’s team has put together a nice annotation framework on top of the JBoss TS, Business Activity service. What?!? JBoss has a Business Activity service? Yup, we do. Have had it for a long while, accept nobody but Mark (did he even?) made any noise about it within the JBoss community. What is the Business Activity service? Mark does a better job in an article he wrote a few years ago, (his book is even better), so I won’t get into a lot of detail. To summarize in my own words, the Business Activity service looks very much like XA and traditional transactions, except they are really meant for long running conversations or activity. Traditional transactions are not good in that scenario because they hold onto resources too long. Particpants in BA look a lot like resources in XA. The register themselves with an activity. An activity is very similar to a transaction in that you begin it, and either cancel (rollback) or close (commit) the activity when you are finished. Participants can interact at closing with a 2 phase protocol, much like 2PC in regular XA. The difference is that the rules are much more lax. Participants can choose to leave the activity whenever they like as well as some other neat things. See Mark’s book or article for more details. Oh, and I forgot, this is a WS-* specification.
So back to the annotation framework. Its pretty cool. It allows you to tie in a compensatory action to an executed method. So, if the business activity is cancelled, the coordinator will automatically callback to this compensation. Here’s an example:
@Stateless @SOAPBinding(style = SOAPBinding.Style.RPC) @HandlerChain(file = "jaxws-ba-handler-server.xml") public class HotelBAImpl implements HotelBA { @WebMethod @BAService(BAServiceType.MODIFY) @BAAgreement(BAAgreementType.PARTICIPANT_COMPLETION) @BACompensatedBy(value="cancel",type = BACompensationType.PARAMETERS_MATCH) public Integer book(String user, String pass, Integer room) { // ... business logic return reservationNumber; } @WebMethod public void cancel(String user, String pass, Integer room) { // ... business logic } }
So, in the example, if you invoke the book() method of the Hotel service, it will interact with the coordinator to register a compensation action. This action will be to invoke the cancel() method with the exact parameters that were passed into the book() method if the activity is aborted. Alternatively, you can map the book() method’s return value and parameters to the compensation action using the @BAResult and @BAParam.
{ @WebMethod @BAService(BAServiceType.MODIFY) @BAAgreement(BAAgreementType.PARTICIPANT_COMPLETION) @BACompensatedBy(value="cancel",type = BACompensationType.CUSTOM) @BAResult("reservationNumber") public Integer book(@BAParam("user") String user, @BAParam("password") String pass, Integer room) { // ... business logic return reservationNumber; } @WebMethod public void cancel(@BAParam("reservationNumber") Integer reserverationNumber, @BAParam("user")String user, @BAParam("password") String pass) { // ... business logic } }
There’s also a CompensationManager object that allows you to programatically put data into the activity that can be referenced by the @BAParam annotation:
{ // injected field @CompensationManagement CompensationManager cm; @WebMethod @BAService(BAServiceType.MODIFY) @BAAgreement(BAAgreementType.PARTICIPANT_COMPLETION) @BACompensatedBy(value="cancel",type = BACompensationType.CUSTOM) public Integer book(String user, String pass, Integer room) { // ... business logic cm.put("reservationNumber", reservationNumber); return reservationNumber; } @WebMethod public void cancel(@BAParam("reservationNumber") Integer reserverationNumber) { // ... business logic } }
There’s a few more ways to define a compensation. You can have the method compensated by another different EJB method, Web Service, or even a plain POJO. I won’t get into further detail. You’ll have to read the documentation and take a look at the demo app that comes with the distribution.
Client interface
The client interface is very simple and similar to UserTransaction:
public abstract class UserBusinessActivity { public static final int ATOMIC_OUTCOME = 0; public static final int MIXED_OUTCOME = 1; /** * Start a new business activity with atomic outcome. * If one is already associated with this thread * then the WrongStateException will be thrown. Upon success, this * operation associates the newly created transaction with the current * thread. */ public abstract void begin() throws WrongStateException, SystemException; /** * Start a new BA with atomic outcome and the specified timeout as * its lifetime. * If one is already associated with this thread then the * WrongStateException will be thrown. */ public abstract void begin(final int timeout) throws WrongStateException, SystemException; /** * The BA is normally terminated by the close method. This signals to * all registered participants that the BA has ended and no compensation * is required. */ public abstract void close() throws TransactionRolledBackException, UnknownTransactionException, SystemException; /** * If the BA must undo its work then the cancel method is used. Any * participants that can compensate are forced to do so. */ public abstract void cancel() throws UnknownTransactionException, SystemException; /** * If participants have registered for the BusinessAgreementWithComplete * protocol then they will be expecting the application to inform them * when all work intended for them has been sent (and responded to). The * complete method is used for this purpose. */ public abstract void complete() throws UnknownTransactionException, SystemException; public abstract String transactionIdentifier(); }
Here’s an example of using the activity service:
UserBusinessActivity businessActivity = UserBusinessActivityFactory.userBusinessActivity(); businessActivity.begin(); try { // do some work businessActivity.close(); } catch (Exception ex) { businessActivity.cancel(); }
There is of course some initialization to do. I’ll leave that to the documentation, but overall its a very simple API. What I like about this way of doing compensations is that the client is decoupled from the knowledge of whether something should be compensated for, and how it is compensated. The knowledge of this is encapsulated in the services the client interacts with. Services automatically register themselves for the appropriate callbacks. User code does not have to perform commits or rollbacks. Very similar in ease of use to what we’re used to with JCA combined with JTA transactions.
I really think this stuff would be even more useful in plain web applications, not just coordinated remote web services. In my previous blog, Andy Oliver commented how Hibernate application transactions with FlushMode NEVER isn’t always a great solution for long running web apps that interact with your browser. This new framework could provide a very simple mechanism for registering compensatory callbacks so that browser interactions with a database don’t have to be held in memory.
In my next blog on this subject, I want to dive into some ideas I have for improving this framework. Until then, have fun.
Aug 14, 2007 @ 09:06:14
Hi Bill. You’re right, I didn’t blog about this since joining JBoss, but did spend the previous 5 years blogging and writing papers/books on the subject ;-). As you know, life at JBoss has been hectic and some “old” things slip through the cracks. But this is definitely something we should push more now.
Aug 14, 2007 @ 17:22:17
I couldn’t understand some parts of this article o.us poetry, but I guess I just need to check some more resources regarding this, because it sounds interesting.
Aug 14, 2007 @ 17:36:43
Daniel, you mean this blog entry? Can you be more precise? I’ll edit and elaborate as necessary if desired.
Aug 17, 2007 @ 18:32:54
I think you meant to link to http://labs.jboss.com/jbosstm/baframework — your JBoss Business Activity URL wants me to login first.
toniBlog » links for 2009-01-20
Jan 20, 2009 @ 16:37:18