Another way to do it is actually having both methods on the same bean - and having an @EJB
reference to itself! Something like that:
// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1
@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {
@EJB
private MyStatelessLocal1 myBean2;
public void processObjects(List<Object> objs) {
// this method just processes the data; no need for a transaction
for(Object obj : objs) {
this.myBean2.process(obj);
}
}
@TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
public void process(Object obj) {
// do some work with obj that must be in the scope of a transaction
this.mgr.merge(obj);
// ...
this.mgr.merge(obj);
// ...
this.mgr.flush();
}
}
This way you actually 'force' the process()
method to be accessed via the ejb stack of proxies, therefore taking the @TransactionAttribute
in effect - and still keeping only one class. Phew!
A number of techniques are evolving, but the problem is still sufficiently cutting edge that the standardization process has not yet provided us with a totally portable solution.
Option one, you can make the web services transaction aware. This of course assumes you have control over them, although writing a transaction aware proxy for non-transactional services is also an option in some cases.
The WS-AT and WS-BA protocols are the leading standards for transactional web services. Unfortunately they specify the protocol only, not the language bindings. In other words, there is no standard API at the programming language level. For Java the nearest thing is JSR-156, but it's not ready yet.
Then the problem becomes: how to tie the EJB (i.e. JTA/XA) transaction to the WS one. Since the models used by the WS-AT and XA protocols are closely related, this can be achieved by means of a protocol bridge. Several app servers provide something alone these lines. JBoss presented theirs at JavaOne - see http://anonsvn.jboss.org/repos/labs/labs/jbosstm/workspace/jhalliday/txbridge/BOF-4182.odp
Note that the protocol bridging technique can also be used the other way around, to allow an EJB that uses e.g. an XA database backend, to be exposed as a transactional web service.
However, the locking model used by two phase commit transactions is really only suitable for short lived transactions in the same domain of control. If your services run in the same company datacenter you'll probably get away with it. For wider distribution, be it geographical or administrative, you probably want to look at WS-BA, a web service transactions protocol specifically designed for such use.
WS-BA uses a compensation based model that is harder to program. It's essentially based on the technique you mention: the effect of service methods is undone by calling a compensation method. This can be tricky to get right, but a JBoss intern did a rather nice annotation framework that allows you to define compensation methods with minimal effort and have them driven automatically. It's not standardized, but well worth checking out if you choose this approach: http://www.jboss.org/jbosstm/baframework
Best Answer
First of all, there is no rollback of an exception, it's a rollback of a transaction.
@ApplicationException(rollback=true)
, you don't have to rollback the transaction manually.Context.setRollbackOnly()
forces the container to rollback the transaction, also if there is no exception.@ApplicationException(rollback=true)
. If the exception is aRuntimeException
and the exception isn't caught, it forces the container to rollback the transaction. But watch out, the container will in this case discard the EJB instance.RuntimeException
, the transaction will be rolled back automatically. If you catch an checked exception inside the code, you have to usesetRollbackOnly
to rollback the transaction.For further information, check out the free book Mastering EJB. It describes the rollback scenarios very well and is free for download.